activerecord 8.0.0 → 8.1.2
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 +703 -248
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +18 -2
- 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_association.rb +3 -3
- 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/join_association.rb +25 -27
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/primary_key.rb +2 -1
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +17 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
- data/lib/active_record/attribute_methods.rb +23 -18
- data/lib/active_record/attributes.rb +40 -26
- data/lib/active_record/autosave_association.rb +22 -12
- data/lib/active_record/base.rb +3 -4
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
- 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 +458 -108
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +55 -40
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
- 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 +31 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +89 -23
- data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -64
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +57 -20
- 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/quoting.rb +7 -1
- 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 +33 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -17
- 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 +22 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +67 -31
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +82 -49
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +38 -10
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +14 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +5 -12
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +65 -36
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +2 -2
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/connection_handling.rb +15 -10
- data/lib/active_record/core.rb +44 -12
- data/lib/active_record/counter_cache.rb +34 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +59 -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 +19 -19
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encryptor.rb +39 -25
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +37 -20
- data/lib/active_record/errors.rb +23 -7
- 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/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/future_result.rb +3 -3
- 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 +19 -3
- 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 +31 -21
- 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 +7 -7
- data/lib/active_record/querying.rb +4 -4
- data/lib/active_record/railtie.rb +35 -6
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +24 -20
- 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 +54 -38
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +42 -25
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -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 +4 -2
- data/lib/active_record/relation/query_methods.rb +43 -32
- data/lib/active_record/relation/spawn_methods.rb +6 -6
- data/lib/active_record/relation/where_clause.rb +10 -11
- data/lib/active_record/relation.rb +43 -19
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema_dumper.rb +42 -22
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +47 -18
- data/lib/active_record/statement_cache.rb +15 -11
- 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 +44 -45
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- 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 +39 -16
- 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 +71 -6
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +6 -22
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -15
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
|
@@ -25,7 +25,7 @@ module ActiveRecord
|
|
|
25
25
|
#
|
|
26
26
|
# The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
|
|
27
27
|
#
|
|
28
|
-
# Options
|
|
28
|
+
# ==== Options
|
|
29
29
|
#
|
|
30
30
|
# * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
|
|
31
31
|
# the default is to connect to localhost.
|
|
@@ -288,6 +288,16 @@ module ActiveRecord
|
|
|
288
288
|
database_version >= 10_00_00 # >= 10.0
|
|
289
289
|
end
|
|
290
290
|
|
|
291
|
+
if PG::Connection.method_defined?(:close_prepared) # pg 1.6.0 & libpq 17
|
|
292
|
+
def supports_close_prepared? # :nodoc:
|
|
293
|
+
database_version >= 17_00_00
|
|
294
|
+
end
|
|
295
|
+
else
|
|
296
|
+
def supports_close_prepared? # :nodoc:
|
|
297
|
+
false
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
291
301
|
def index_algorithms
|
|
292
302
|
{ concurrently: "CONCURRENTLY" }
|
|
293
303
|
end
|
|
@@ -309,8 +319,12 @@ module ActiveRecord
|
|
|
309
319
|
# accessed while holding the connection's lock. (And we
|
|
310
320
|
# don't need the complication of with_raw_connection because
|
|
311
321
|
# a reconnect would invalidate the entire statement pool.)
|
|
312
|
-
if conn = @connection.instance_variable_get(:@raw_connection)
|
|
313
|
-
|
|
322
|
+
if (conn = @connection.instance_variable_get(:@raw_connection)) && conn.status == PG::CONNECTION_OK
|
|
323
|
+
if @connection.supports_close_prepared?
|
|
324
|
+
conn.close_prepared key
|
|
325
|
+
else
|
|
326
|
+
conn.query "DEALLOCATE #{key}"
|
|
327
|
+
end
|
|
314
328
|
end
|
|
315
329
|
rescue PG::Error
|
|
316
330
|
end
|
|
@@ -349,6 +363,7 @@ module ActiveRecord
|
|
|
349
363
|
@lock.synchronize do
|
|
350
364
|
return false unless @raw_connection
|
|
351
365
|
@raw_connection.query ";"
|
|
366
|
+
verified!
|
|
352
367
|
end
|
|
353
368
|
true
|
|
354
369
|
rescue PG::Error
|
|
@@ -380,6 +395,11 @@ module ActiveRecord
|
|
|
380
395
|
end
|
|
381
396
|
end
|
|
382
397
|
|
|
398
|
+
def clear_cache!(new_connection: false)
|
|
399
|
+
super
|
|
400
|
+
@schema_search_path = nil if new_connection
|
|
401
|
+
end
|
|
402
|
+
|
|
383
403
|
# Disconnects from the database if already connected. Otherwise, this
|
|
384
404
|
# method does nothing.
|
|
385
405
|
def disconnect!
|
|
@@ -396,10 +416,6 @@ module ActiveRecord
|
|
|
396
416
|
@raw_connection = nil
|
|
397
417
|
end
|
|
398
418
|
|
|
399
|
-
def native_database_types # :nodoc:
|
|
400
|
-
self.class.native_database_types
|
|
401
|
-
end
|
|
402
|
-
|
|
403
419
|
def self.native_database_types # :nodoc:
|
|
404
420
|
@native_database_types ||= begin
|
|
405
421
|
types = NATIVE_DATABASE_TYPES.dup
|
|
@@ -520,7 +536,7 @@ module ActiveRecord
|
|
|
520
536
|
type.typname AS name,
|
|
521
537
|
type.OID AS oid,
|
|
522
538
|
n.nspname AS schema,
|
|
523
|
-
|
|
539
|
+
array_agg(enum.enumlabel ORDER BY enum.enumsortorder) AS value
|
|
524
540
|
FROM pg_enum AS enum
|
|
525
541
|
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
|
526
542
|
JOIN pg_namespace n ON type.typnamespace = n.oid
|
|
@@ -615,7 +631,7 @@ module ActiveRecord
|
|
|
615
631
|
}
|
|
616
632
|
end
|
|
617
633
|
|
|
618
|
-
# Returns the configured supported identifier length supported by PostgreSQL
|
|
634
|
+
# Returns the configured maximum supported identifier length supported by PostgreSQL
|
|
619
635
|
def max_identifier_length
|
|
620
636
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
|
621
637
|
end
|
|
@@ -633,7 +649,11 @@ module ActiveRecord
|
|
|
633
649
|
# Returns the version of the connected PostgreSQL server.
|
|
634
650
|
def get_database_version # :nodoc:
|
|
635
651
|
with_raw_connection do |conn|
|
|
636
|
-
conn.server_version
|
|
652
|
+
version = conn.server_version
|
|
653
|
+
if version == 0
|
|
654
|
+
raise ActiveRecord::ConnectionNotEstablished, "Could not determine PostgreSQL version"
|
|
655
|
+
end
|
|
656
|
+
version
|
|
637
657
|
end
|
|
638
658
|
end
|
|
639
659
|
alias :postgresql_version :database_version
|
|
@@ -787,6 +807,8 @@ module ActiveRecord
|
|
|
787
807
|
NOT_NULL_VIOLATION = "23502"
|
|
788
808
|
FOREIGN_KEY_VIOLATION = "23503"
|
|
789
809
|
UNIQUE_VIOLATION = "23505"
|
|
810
|
+
CHECK_VIOLATION = "23514"
|
|
811
|
+
EXCLUSION_VIOLATION = "23P01"
|
|
790
812
|
SERIALIZATION_FAILURE = "40001"
|
|
791
813
|
DEADLOCK_DETECTED = "40P01"
|
|
792
814
|
DUPLICATE_DATABASE = "42P04"
|
|
@@ -818,6 +840,10 @@ module ActiveRecord
|
|
|
818
840
|
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
819
841
|
when FOREIGN_KEY_VIOLATION
|
|
820
842
|
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
843
|
+
when CHECK_VIOLATION
|
|
844
|
+
CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
845
|
+
when EXCLUSION_VIOLATION
|
|
846
|
+
ExclusionViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
821
847
|
when VALUE_LIMIT_VIOLATION
|
|
822
848
|
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
823
849
|
when NUMERIC_VALUE_OUT_OF_RANGE
|
|
@@ -988,6 +1014,8 @@ module ActiveRecord
|
|
|
988
1014
|
add_pg_encoders
|
|
989
1015
|
add_pg_decoders
|
|
990
1016
|
|
|
1017
|
+
schema_search_path # populate cache
|
|
1018
|
+
|
|
991
1019
|
reload_type_map
|
|
992
1020
|
end
|
|
993
1021
|
|
|
@@ -271,10 +271,10 @@ module ActiveRecord
|
|
|
271
271
|
end
|
|
272
272
|
|
|
273
273
|
def encode_with(coder) # :nodoc:
|
|
274
|
-
coder["columns"] = @columns.sort.to_h
|
|
274
|
+
coder["columns"] = @columns.sort.to_h.transform_values { _1.sort_by(&:name) }
|
|
275
275
|
coder["primary_keys"] = @primary_keys.sort.to_h
|
|
276
276
|
coder["data_sources"] = @data_sources.sort.to_h
|
|
277
|
-
coder["indexes"] = @indexes.sort.to_h
|
|
277
|
+
coder["indexes"] = @indexes.sort.to_h.transform_values { _1.sort_by(&:name) }
|
|
278
278
|
coder["version"] = @version
|
|
279
279
|
end
|
|
280
280
|
|
|
@@ -61,6 +61,14 @@ module ActiveRecord
|
|
|
61
61
|
@previous_read_uncommitted = nil
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
def default_insert_value(column) # :nodoc:
|
|
65
|
+
if column.default_function
|
|
66
|
+
Arel.sql(column.default_function)
|
|
67
|
+
else
|
|
68
|
+
column.default
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
64
72
|
private
|
|
65
73
|
def internal_begin_transaction(mode, isolation)
|
|
66
74
|
if isolation
|
|
@@ -76,51 +84,63 @@ module ActiveRecord
|
|
|
76
84
|
end
|
|
77
85
|
|
|
78
86
|
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
|
87
|
+
total_changes_before_query = raw_connection.total_changes
|
|
88
|
+
affected_rows = nil
|
|
89
|
+
|
|
79
90
|
if batch
|
|
80
91
|
raw_connection.execute_batch2(sql)
|
|
81
|
-
|
|
82
|
-
stmt =
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
result = if stmt.column_count.zero? # No return
|
|
87
|
-
stmt.step
|
|
88
|
-
ActiveRecord::Result.empty
|
|
92
|
+
else
|
|
93
|
+
stmt = if prepare
|
|
94
|
+
@statements[sql] ||= raw_connection.prepare(sql)
|
|
95
|
+
@statements[sql].reset!
|
|
89
96
|
else
|
|
90
|
-
|
|
97
|
+
# Don't cache statements if they are not prepared.
|
|
98
|
+
raw_connection.prepare(sql)
|
|
91
99
|
end
|
|
92
|
-
else
|
|
93
|
-
# Don't cache statements if they are not prepared.
|
|
94
|
-
stmt = raw_connection.prepare(sql)
|
|
95
100
|
begin
|
|
96
101
|
unless binds.nil? || binds.empty?
|
|
97
102
|
stmt.bind_params(type_casted_binds)
|
|
98
103
|
end
|
|
99
104
|
result = if stmt.column_count.zero? # No return
|
|
100
105
|
stmt.step
|
|
101
|
-
|
|
106
|
+
|
|
107
|
+
affected_rows = if raw_connection.total_changes > total_changes_before_query
|
|
108
|
+
raw_connection.changes
|
|
109
|
+
else
|
|
110
|
+
0
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
ActiveRecord::Result.empty(affected_rows: affected_rows)
|
|
102
114
|
else
|
|
103
|
-
|
|
115
|
+
rows = stmt.to_a
|
|
116
|
+
|
|
117
|
+
affected_rows = if raw_connection.total_changes > total_changes_before_query
|
|
118
|
+
raw_connection.changes
|
|
119
|
+
else
|
|
120
|
+
0
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
ActiveRecord::Result.new(stmt.columns, rows, stmt.types.map { |t| type_map.lookup(t) }, affected_rows: affected_rows)
|
|
104
124
|
end
|
|
105
125
|
ensure
|
|
106
|
-
stmt.close
|
|
126
|
+
stmt.close unless prepare
|
|
107
127
|
end
|
|
108
128
|
end
|
|
109
|
-
@last_affected_rows = raw_connection.changes
|
|
110
129
|
verified!
|
|
111
130
|
|
|
131
|
+
notification_payload[:affected_rows] = affected_rows
|
|
112
132
|
notification_payload[:row_count] = result&.length || 0
|
|
113
133
|
result
|
|
114
134
|
end
|
|
115
135
|
|
|
116
136
|
def cast_result(result)
|
|
117
|
-
# Given that SQLite3 doesn't
|
|
118
|
-
#
|
|
137
|
+
# Given that SQLite3 doesn't have a Result type, raw_execute already returns an ActiveRecord::Result
|
|
138
|
+
# so we have nothing to cast here.
|
|
119
139
|
result
|
|
120
140
|
end
|
|
121
141
|
|
|
122
142
|
def affected_rows(result)
|
|
123
|
-
|
|
143
|
+
result.affected_rows
|
|
124
144
|
end
|
|
125
145
|
|
|
126
146
|
def execute_batch(statements, name = nil, **kwargs)
|
|
@@ -135,10 +155,6 @@ module ActiveRecord
|
|
|
135
155
|
def returning_column_values(result)
|
|
136
156
|
result.rows.first
|
|
137
157
|
end
|
|
138
|
-
|
|
139
|
-
def default_insert_value(column)
|
|
140
|
-
column.default
|
|
141
|
-
end
|
|
142
158
|
end
|
|
143
159
|
end
|
|
144
160
|
end
|
|
@@ -50,6 +50,19 @@ module ActiveRecord
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def quote(value) # :nodoc:
|
|
54
|
+
case value
|
|
55
|
+
when Numeric
|
|
56
|
+
if value.finite?
|
|
57
|
+
super
|
|
58
|
+
else
|
|
59
|
+
"'#{value}'"
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
53
66
|
def quote_string(s)
|
|
54
67
|
::SQLite3::Database.quote(s)
|
|
55
68
|
end
|
|
@@ -67,18 +80,10 @@ module ActiveRecord
|
|
|
67
80
|
"x'#{value.hex}'"
|
|
68
81
|
end
|
|
69
82
|
|
|
70
|
-
def quoted_true
|
|
71
|
-
"1"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
83
|
def unquoted_true
|
|
75
84
|
1
|
|
76
85
|
end
|
|
77
86
|
|
|
78
|
-
def quoted_false
|
|
79
|
-
"0"
|
|
80
|
-
end
|
|
81
|
-
|
|
82
87
|
def unquoted_false
|
|
83
88
|
0
|
|
84
89
|
end
|
|
@@ -19,11 +19,23 @@ module ActiveRecord
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def default_primary_key?(column)
|
|
22
|
-
schema_type(column) == :integer
|
|
22
|
+
schema_type(column) == :integer && primary_key_has_autoincrement?
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def explicit_primary_key_default?(column)
|
|
26
|
-
column.bigint?
|
|
26
|
+
column.bigint? || (column.type == :integer && !primary_key_has_autoincrement?)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def primary_key_has_autoincrement?
|
|
30
|
+
return false unless table_name
|
|
31
|
+
|
|
32
|
+
table_sql = @connection.query_value(<<~SQL, "SCHEMA")
|
|
33
|
+
SELECT sql FROM sqlite_master WHERE name = #{@connection.quote(table_name)} AND type = 'table'
|
|
34
|
+
UNION ALL
|
|
35
|
+
SELECT sql FROM sqlite_temp_master WHERE name = #{@connection.quote(table_name)} AND type = 'table'
|
|
36
|
+
SQL
|
|
37
|
+
|
|
38
|
+
table_sql.to_s.match?(/\bAUTOINCREMENT\b/i)
|
|
27
39
|
end
|
|
28
40
|
|
|
29
41
|
def prepare_column_options(column)
|
|
@@ -27,6 +27,7 @@ module ActiveRecord
|
|
|
27
27
|
col["name"]
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
where = where.sub(/\s*\/\*.*\*\/\z/, "") if where
|
|
30
31
|
orders = {}
|
|
31
32
|
|
|
32
33
|
if columns.any?(&:nil?) # index created with an expression
|
|
@@ -62,22 +63,13 @@ module ActiveRecord
|
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
|
65
|
-
return if options.delete(:if_exists)
|
|
66
|
+
return if options.delete(:if_exists) && !foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
|
66
67
|
|
|
67
68
|
to_table ||= options[:to_table]
|
|
68
69
|
options = options.except(:name, :to_table, :validate)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
fkey = foreign_keys.detect do |fk|
|
|
72
|
-
table = to_table || begin
|
|
73
|
-
table = options[:column].to_s.delete_suffix("_id")
|
|
74
|
-
Base.pluralize_table_names ? table.pluralize : table
|
|
75
|
-
end
|
|
76
|
-
table = strip_table_name_prefix_and_suffix(table)
|
|
77
|
-
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
|
78
|
-
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
|
79
|
-
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
|
70
|
+
fkey = foreign_key_for!(from_table, to_table: to_table, **options)
|
|
80
71
|
|
|
72
|
+
foreign_keys = foreign_keys(from_table)
|
|
81
73
|
foreign_keys.delete(fkey)
|
|
82
74
|
alter_table(from_table, foreign_keys)
|
|
83
75
|
end
|
|
@@ -155,6 +147,7 @@ module ActiveRecord
|
|
|
155
147
|
|
|
156
148
|
Column.new(
|
|
157
149
|
field["name"],
|
|
150
|
+
lookup_cast_type(field["type"]),
|
|
158
151
|
default_value,
|
|
159
152
|
type_metadata,
|
|
160
153
|
field["notnull"].to_i == 0,
|
|
@@ -19,14 +19,30 @@ SQLite3::ForkSafety.suppress_warnings!
|
|
|
19
19
|
|
|
20
20
|
module ActiveRecord
|
|
21
21
|
module ConnectionAdapters # :nodoc:
|
|
22
|
-
# = Active Record SQLite3 Adapter
|
|
22
|
+
# = Active Record \SQLite3 Adapter
|
|
23
23
|
#
|
|
24
|
-
# The SQLite3 adapter works with the sqlite3-ruby
|
|
25
|
-
#
|
|
24
|
+
# The \SQLite3 adapter works with the sqlite3[https://sparklemotion.github.io/sqlite3-ruby/]
|
|
25
|
+
# driver.
|
|
26
26
|
#
|
|
27
|
-
# Options
|
|
27
|
+
# ==== Options
|
|
28
|
+
#
|
|
29
|
+
# * +:database+ (String): Filesystem path to the database file.
|
|
30
|
+
# * +:statement_limit+ (Integer): Maximum number of prepared statements to cache per database connection. (default: 1000)
|
|
31
|
+
# * +:timeout+ (Integer): Timeout in milliseconds to use when waiting for a lock. (default: no wait)
|
|
32
|
+
# * +:strict+ (Boolean): Enable or disable strict mode. When enabled, this will
|
|
33
|
+
# {disallow double-quoted string literals in SQL
|
|
34
|
+
# statements}[https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted].
|
|
35
|
+
# (default: see strict_strings_by_default)
|
|
36
|
+
# * +:extensions+ (Array): (<b>requires sqlite3 v2.4.0</b>) Each entry specifies a sqlite extension
|
|
37
|
+
# to load for this database. The entry may be a filesystem path, or the name of a class that
|
|
38
|
+
# responds to +.to_path+ to provide the filesystem path for the extension. See {sqlite3-ruby
|
|
39
|
+
# documentation}[https://sparklemotion.github.io/sqlite3-ruby/SQLite3/Database.html#class-SQLite3::Database-label-SQLite+Extensions]
|
|
40
|
+
# for more information.
|
|
41
|
+
#
|
|
42
|
+
# There may be other options available specific to the SQLite3 driver. Please read the
|
|
43
|
+
# documentation for
|
|
44
|
+
# {SQLite3::Database.new}[https://sparklemotion.github.io/sqlite3-ruby/SQLite3/Database.html#method-c-new]
|
|
28
45
|
#
|
|
29
|
-
# * <tt>:database</tt> - Path to the database file.
|
|
30
46
|
class SQLite3Adapter < AbstractAdapter
|
|
31
47
|
ADAPTER_NAME = "SQLite"
|
|
32
48
|
|
|
@@ -46,10 +62,14 @@ module ActiveRecord
|
|
|
46
62
|
|
|
47
63
|
args << "-#{options[:mode]}" if options[:mode]
|
|
48
64
|
args << "-header" if options[:header]
|
|
49
|
-
args << File.expand_path(config.database, Rails.
|
|
65
|
+
args << File.expand_path(config.database, defined?(Rails.root) ? Rails.root : nil)
|
|
50
66
|
|
|
51
67
|
find_cmd_and_exec(ActiveRecord.database_cli[:sqlite], *args)
|
|
52
68
|
end
|
|
69
|
+
|
|
70
|
+
def native_database_types # :nodoc:
|
|
71
|
+
NATIVE_DATABASE_TYPES
|
|
72
|
+
end
|
|
53
73
|
end
|
|
54
74
|
|
|
55
75
|
include SQLite3::Quoting
|
|
@@ -58,12 +78,19 @@ module ActiveRecord
|
|
|
58
78
|
|
|
59
79
|
##
|
|
60
80
|
# :singleton-method:
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
81
|
+
#
|
|
82
|
+
# Configure the SQLite3Adapter to be used in a "strict strings" mode. When enabled, this will
|
|
83
|
+
# {disallow double-quoted string literals in SQL
|
|
84
|
+
# statements}[https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted],
|
|
85
|
+
# which may prevent some typographical errors like creating an index for a non-existent
|
|
86
|
+
# column. The default is +false+.
|
|
87
|
+
#
|
|
64
88
|
# If you wish to enable this mode you can add the following line to your application.rb file:
|
|
65
89
|
#
|
|
66
90
|
# config.active_record.sqlite3_adapter_strict_strings_by_default = true
|
|
91
|
+
#
|
|
92
|
+
# This can also be configured on individual databases by setting the +strict:+ option.
|
|
93
|
+
#
|
|
67
94
|
class_attribute :strict_strings_by_default, default: false
|
|
68
95
|
|
|
69
96
|
NATIVE_DATABASE_TYPES = {
|
|
@@ -122,13 +149,18 @@ module ActiveRecord
|
|
|
122
149
|
end
|
|
123
150
|
end
|
|
124
151
|
|
|
125
|
-
@last_affected_rows = nil
|
|
126
152
|
@previous_read_uncommitted = nil
|
|
127
153
|
@config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)
|
|
154
|
+
|
|
155
|
+
extensions = @config.fetch(:extensions, []).map do |extension|
|
|
156
|
+
extension.safe_constantize || extension
|
|
157
|
+
end
|
|
158
|
+
|
|
128
159
|
@connection_parameters = @config.merge(
|
|
129
160
|
database: @config[:database].to_s,
|
|
130
161
|
results_as_hash: true,
|
|
131
162
|
default_transaction_mode: :immediate,
|
|
163
|
+
extensions: extensions
|
|
132
164
|
)
|
|
133
165
|
end
|
|
134
166
|
|
|
@@ -153,7 +185,7 @@ module ActiveRecord
|
|
|
153
185
|
end
|
|
154
186
|
|
|
155
187
|
def supports_expression_index?
|
|
156
|
-
|
|
188
|
+
true
|
|
157
189
|
end
|
|
158
190
|
|
|
159
191
|
def requires_reloading?
|
|
@@ -181,7 +213,7 @@ module ActiveRecord
|
|
|
181
213
|
end
|
|
182
214
|
|
|
183
215
|
def supports_common_table_expressions?
|
|
184
|
-
|
|
216
|
+
true
|
|
185
217
|
end
|
|
186
218
|
|
|
187
219
|
def supports_insert_returning?
|
|
@@ -207,7 +239,12 @@ module ActiveRecord
|
|
|
207
239
|
!(@raw_connection.nil? || @raw_connection.closed?)
|
|
208
240
|
end
|
|
209
241
|
|
|
210
|
-
|
|
242
|
+
def active?
|
|
243
|
+
if connected?
|
|
244
|
+
verified!
|
|
245
|
+
true
|
|
246
|
+
end
|
|
247
|
+
end
|
|
211
248
|
|
|
212
249
|
alias :reset! :reconnect!
|
|
213
250
|
|
|
@@ -224,10 +261,6 @@ module ActiveRecord
|
|
|
224
261
|
true
|
|
225
262
|
end
|
|
226
263
|
|
|
227
|
-
def native_database_types # :nodoc:
|
|
228
|
-
NATIVE_DATABASE_TYPES
|
|
229
|
-
end
|
|
230
|
-
|
|
231
264
|
# Returns the current database encoding format as a string, e.g. 'UTF-8'
|
|
232
265
|
def encoding
|
|
233
266
|
any_raw_connection.encoding.to_s
|
|
@@ -304,7 +337,7 @@ module ActiveRecord
|
|
|
304
337
|
# Creates a virtual table
|
|
305
338
|
#
|
|
306
339
|
# Example:
|
|
307
|
-
# create_virtual_table :emails, :fts5, ['sender', 'title','
|
|
340
|
+
# create_virtual_table :emails, :fts5, ['sender', 'title', 'body']
|
|
308
341
|
def create_virtual_table(table_name, module_name, values)
|
|
309
342
|
exec_query "CREATE VIRTUAL TABLE IF NOT EXISTS #{table_name} USING #{module_name} (#{values.join(", ")})"
|
|
310
343
|
end
|
|
@@ -407,7 +440,7 @@ module ActiveRecord
|
|
|
407
440
|
end
|
|
408
441
|
alias :add_belongs_to :add_reference
|
|
409
442
|
|
|
410
|
-
FK_REGEX = /.*FOREIGN KEY\s+\("(
|
|
443
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
|
411
444
|
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
|
412
445
|
def foreign_keys(table_name)
|
|
413
446
|
# SQLite returns 1 row for each column of composite foreign keys.
|
|
@@ -473,8 +506,8 @@ module ActiveRecord
|
|
|
473
506
|
end
|
|
474
507
|
|
|
475
508
|
def check_version # :nodoc:
|
|
476
|
-
if database_version < "3.
|
|
477
|
-
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.
|
|
509
|
+
if database_version < "3.23.0"
|
|
510
|
+
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.23.0."
|
|
478
511
|
end
|
|
479
512
|
end
|
|
480
513
|
|
|
@@ -530,6 +563,8 @@ module ActiveRecord
|
|
|
530
563
|
# Binary columns
|
|
531
564
|
when /x'(.*)'/
|
|
532
565
|
[ $1 ].pack("H*")
|
|
566
|
+
when "TRUE", "FALSE"
|
|
567
|
+
default
|
|
533
568
|
else
|
|
534
569
|
# Anything else is blank or some function
|
|
535
570
|
# and we can't know the value of that, so return nil.
|
|
@@ -578,8 +613,8 @@ module ActiveRecord
|
|
|
578
613
|
yield definition if block_given?
|
|
579
614
|
end
|
|
580
615
|
|
|
581
|
-
|
|
582
|
-
|
|
616
|
+
disable_referential_integrity do
|
|
617
|
+
transaction do
|
|
583
618
|
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
|
584
619
|
move_table(altered_table_name, table_name, &caller)
|
|
585
620
|
end
|
|
@@ -620,8 +655,8 @@ module ActiveRecord
|
|
|
620
655
|
column_options[:stored] = column.virtual_stored?
|
|
621
656
|
column_options[:type] = column.type
|
|
622
657
|
elsif column.has_default?
|
|
623
|
-
|
|
624
|
-
default =
|
|
658
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
659
|
+
default = column.fetch_cast_type(self).deserialize(column.default)
|
|
625
660
|
default = -> { column.default_function } if default.nil?
|
|
626
661
|
|
|
627
662
|
unless column.auto_increment?
|
|
@@ -695,6 +730,8 @@ module ActiveRecord
|
|
|
695
730
|
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
696
731
|
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
|
697
732
|
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
733
|
+
elsif exception.message.match?(/CHECK constraint failed: .*/i)
|
|
734
|
+
CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
698
735
|
elsif exception.message.match?(/called on a closed database/i)
|
|
699
736
|
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
700
737
|
elsif exception.is_a?(::SQLite3::BusyException)
|
|
@@ -784,9 +821,9 @@ module ActiveRecord
|
|
|
784
821
|
|
|
785
822
|
def table_info(table_name)
|
|
786
823
|
if supports_virtual_columns?
|
|
787
|
-
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
|
|
824
|
+
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA", allow_retry: true)
|
|
788
825
|
else
|
|
789
|
-
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
|
826
|
+
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA", allow_retry: true)
|
|
790
827
|
end
|
|
791
828
|
end
|
|
792
829
|
|
|
@@ -813,18 +850,10 @@ module ActiveRecord
|
|
|
813
850
|
end
|
|
814
851
|
|
|
815
852
|
def configure_connection
|
|
816
|
-
if @config[:timeout]
|
|
817
|
-
raise ArgumentError, "Cannot specify both timeout and retries arguments"
|
|
818
|
-
elsif @config[:timeout]
|
|
853
|
+
if @config[:timeout]
|
|
819
854
|
timeout = self.class.type_cast_config_to_integer(@config[:timeout])
|
|
820
855
|
raise TypeError, "timeout must be integer, not #{timeout}" unless timeout.is_a?(Integer)
|
|
821
856
|
@raw_connection.busy_handler_timeout = timeout
|
|
822
|
-
elsif @config[:retries]
|
|
823
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
|
824
|
-
The retries option is deprecated and will be removed in Rails 8.1. Use timeout instead.
|
|
825
|
-
MSG
|
|
826
|
-
retries = self.class.type_cast_config_to_integer(@config[:retries])
|
|
827
|
-
raw_connection.busy_handler { |count| count <= retries }
|
|
828
857
|
end
|
|
829
858
|
|
|
830
859
|
super
|
|
@@ -29,7 +29,8 @@ module ActiveRecord
|
|
|
29
29
|
raw_connection.next_result
|
|
30
30
|
end
|
|
31
31
|
verified!
|
|
32
|
-
|
|
32
|
+
|
|
33
|
+
notification_payload[:affected_rows] = result.affected_rows
|
|
33
34
|
notification_payload[:row_count] = result.count
|
|
34
35
|
result
|
|
35
36
|
ensure
|
|
@@ -40,9 +41,9 @@ module ActiveRecord
|
|
|
40
41
|
|
|
41
42
|
def cast_result(result)
|
|
42
43
|
if result.fields.empty?
|
|
43
|
-
ActiveRecord::Result.empty
|
|
44
|
+
ActiveRecord::Result.empty(affected_rows: result.affected_rows)
|
|
44
45
|
else
|
|
45
|
-
ActiveRecord::Result.new(result.fields, result.rows)
|
|
46
|
+
ActiveRecord::Result.new(result.fields, result.rows, affected_rows: result.affected_rows)
|
|
46
47
|
end
|
|
47
48
|
end
|
|
48
49
|
|
|
@@ -121,7 +121,7 @@ module ActiveRecord
|
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def active?
|
|
124
|
-
connected? && @lock.synchronize { @raw_connection&.ping } || false
|
|
124
|
+
connected? && @lock.synchronize { @raw_connection&.ping; verified! } || false
|
|
125
125
|
rescue ::Trilogy::Error
|
|
126
126
|
false
|
|
127
127
|
end
|
|
@@ -181,7 +181,7 @@ module ActiveRecord
|
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
case exception
|
|
184
|
-
when ::Trilogy::ConnectionClosed, ::Trilogy::EOFError
|
|
184
|
+
when ::Trilogy::ConnectionClosed, ::Trilogy::EOFError, ::Trilogy::SSLError
|
|
185
185
|
return ConnectionFailed.new(message, connection_pool: @pool)
|
|
186
186
|
when ::Trilogy::Error
|
|
187
187
|
if exception.is_a?(SystemCallError) || exception.message.include?("TRILOGY_INVALID_SEQUENCE_ID")
|
|
@@ -84,6 +84,7 @@ module ActiveRecord
|
|
|
84
84
|
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
|
|
85
85
|
autoload :IndexDefinition
|
|
86
86
|
autoload :ColumnDefinition
|
|
87
|
+
autoload :ColumnMethods
|
|
87
88
|
autoload :ChangeColumnDefinition
|
|
88
89
|
autoload :ChangeColumnDefaultDefinition
|
|
89
90
|
autoload :ForeignKeyDefinition
|