activerecord 7.0.8.7 → 7.2.3
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 +781 -1777
- data/MIT-LICENSE +1 -1
- data/README.rdoc +30 -30
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +40 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +35 -21
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -3
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -33
- data/lib/active_record/attributes.rb +96 -71
- data/lib/active_record/autosave_association.rb +81 -39
- data/lib/active_record/base.rb +11 -7
- data/lib/active_record/callbacks.rb +11 -25
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +343 -91
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +229 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +142 -12
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +60 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +108 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -45
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +51 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +101 -105
- data/lib/active_record/core.rb +273 -178
- data/lib/active_record/counter_cache.rb +69 -35
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -3
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +56 -27
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +46 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
- data/lib/active_record/encryption/encryptor.rb +35 -19
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +130 -28
- data/lib/active_record/errors.rb +154 -34
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +48 -10
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +236 -118
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +96 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +35 -10
- data/lib/active_record/railtie.rb +131 -87
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +147 -155
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +267 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +270 -108
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +97 -21
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +28 -16
- data/lib/active_record/relation/query_attribute.rb +3 -2
- data/lib/active_record/relation/query_methods.rb +585 -109
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +15 -21
- data/lib/active_record/relation.rb +592 -92
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +90 -23
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +33 -11
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +23 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +108 -24
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +3 -1
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +7 -3
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +114 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +56 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -15,39 +15,25 @@ module ActiveRecord
|
|
|
15
15
|
!READ_QUERY.match?(sql.b)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def explain(arel, binds = [])
|
|
19
|
-
sql
|
|
20
|
-
|
|
18
|
+
def explain(arel, binds = [], _options = [])
|
|
19
|
+
sql = "EXPLAIN QUERY PLAN " + to_sql(arel, binds)
|
|
20
|
+
result = internal_exec_query(sql, "EXPLAIN", [])
|
|
21
|
+
SQLite3::ExplainPrettyPrinter.new.pp(result)
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
def
|
|
24
|
+
def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
|
|
24
25
|
sql = transform_query(sql)
|
|
25
26
|
check_if_write_query(sql)
|
|
26
27
|
|
|
27
|
-
materialize_transactions
|
|
28
|
-
mark_transaction_written_if_write(sql)
|
|
29
|
-
|
|
30
|
-
log(sql, name) do
|
|
31
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
|
32
|
-
@connection.execute(sql)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nodoc:
|
|
38
|
-
sql = transform_query(sql)
|
|
39
|
-
check_if_write_query(sql)
|
|
40
|
-
|
|
41
|
-
materialize_transactions
|
|
42
28
|
mark_transaction_written_if_write(sql)
|
|
43
29
|
|
|
44
30
|
type_casted_binds = type_casted_binds(binds)
|
|
45
31
|
|
|
46
|
-
log(sql, name, binds, type_casted_binds, async: async) do
|
|
47
|
-
|
|
32
|
+
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
|
33
|
+
with_raw_connection do |conn|
|
|
48
34
|
# Don't cache statements if they are not prepared
|
|
49
35
|
unless prepare
|
|
50
|
-
stmt =
|
|
36
|
+
stmt = conn.prepare(sql)
|
|
51
37
|
begin
|
|
52
38
|
cols = stmt.columns
|
|
53
39
|
unless without_prepared_statement?(binds)
|
|
@@ -58,21 +44,24 @@ module ActiveRecord
|
|
|
58
44
|
stmt.close
|
|
59
45
|
end
|
|
60
46
|
else
|
|
61
|
-
stmt = @statements[sql] ||=
|
|
47
|
+
stmt = @statements[sql] ||= conn.prepare(sql)
|
|
62
48
|
cols = stmt.columns
|
|
63
49
|
stmt.reset!
|
|
64
50
|
stmt.bind_params(type_casted_binds)
|
|
65
51
|
records = stmt.to_a
|
|
66
52
|
end
|
|
53
|
+
verified!
|
|
67
54
|
|
|
68
|
-
build_result(columns: cols, rows: records)
|
|
55
|
+
result = build_result(columns: cols, rows: records)
|
|
56
|
+
notification_payload[:row_count] = result.length
|
|
57
|
+
result
|
|
69
58
|
end
|
|
70
59
|
end
|
|
71
60
|
end
|
|
72
61
|
|
|
73
62
|
def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
|
|
74
|
-
|
|
75
|
-
@
|
|
63
|
+
internal_exec_query(sql, name, binds)
|
|
64
|
+
@raw_connection.changes
|
|
76
65
|
end
|
|
77
66
|
alias :exec_update :exec_delete
|
|
78
67
|
|
|
@@ -80,28 +69,44 @@ module ActiveRecord
|
|
|
80
69
|
raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
|
81
70
|
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
|
82
71
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
72
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
|
73
|
+
ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = conn.get_first_value("PRAGMA read_uncommitted")
|
|
74
|
+
conn.read_uncommitted = true
|
|
75
|
+
begin_db_transaction
|
|
76
|
+
end
|
|
86
77
|
end
|
|
87
78
|
|
|
88
79
|
def begin_db_transaction # :nodoc:
|
|
89
|
-
log("begin transaction", "TRANSACTION")
|
|
80
|
+
log("begin transaction", "TRANSACTION") do
|
|
81
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
|
82
|
+
result = conn.transaction
|
|
83
|
+
verified!
|
|
84
|
+
result
|
|
85
|
+
end
|
|
86
|
+
end
|
|
90
87
|
end
|
|
91
88
|
|
|
92
89
|
def commit_db_transaction # :nodoc:
|
|
93
|
-
log("commit transaction", "TRANSACTION")
|
|
90
|
+
log("commit transaction", "TRANSACTION") do
|
|
91
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
|
92
|
+
conn.commit
|
|
93
|
+
end
|
|
94
|
+
end
|
|
94
95
|
reset_read_uncommitted
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
def exec_rollback_db_transaction # :nodoc:
|
|
98
|
-
log("rollback transaction", "TRANSACTION")
|
|
99
|
+
log("rollback transaction", "TRANSACTION") do
|
|
100
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
|
101
|
+
conn.rollback
|
|
102
|
+
end
|
|
103
|
+
end
|
|
99
104
|
reset_read_uncommitted
|
|
100
105
|
end
|
|
101
106
|
|
|
102
107
|
# https://stackoverflow.com/questions/17574784
|
|
103
108
|
# https://www.sqlite.org/lang_datefunc.html
|
|
104
|
-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')").freeze # :nodoc:
|
|
109
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')", retryable: true).freeze # :nodoc:
|
|
105
110
|
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
|
106
111
|
|
|
107
112
|
def high_precision_current_timestamp
|
|
@@ -109,11 +114,22 @@ module ActiveRecord
|
|
|
109
114
|
end
|
|
110
115
|
|
|
111
116
|
private
|
|
117
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
|
|
118
|
+
log(sql, name, async: async) do |notification_payload|
|
|
119
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
120
|
+
result = conn.execute(sql)
|
|
121
|
+
verified!
|
|
122
|
+
notification_payload[:row_count] = result.length
|
|
123
|
+
result
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
112
128
|
def reset_read_uncommitted
|
|
113
129
|
read_uncommitted = ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted]
|
|
114
130
|
return unless read_uncommitted
|
|
115
131
|
|
|
116
|
-
@
|
|
132
|
+
@raw_connection&.read_uncommitted = read_uncommitted
|
|
117
133
|
end
|
|
118
134
|
|
|
119
135
|
def execute_batch(statements, name = nil)
|
|
@@ -121,21 +137,18 @@ module ActiveRecord
|
|
|
121
137
|
sql = combine_multi_statements(statements)
|
|
122
138
|
|
|
123
139
|
check_if_write_query(sql)
|
|
124
|
-
|
|
125
|
-
materialize_transactions
|
|
126
140
|
mark_transaction_written_if_write(sql)
|
|
127
141
|
|
|
128
|
-
log(sql, name) do
|
|
129
|
-
|
|
130
|
-
|
|
142
|
+
log(sql, name) do |notification_payload|
|
|
143
|
+
with_raw_connection do |conn|
|
|
144
|
+
result = conn.execute_batch2(sql)
|
|
145
|
+
verified!
|
|
146
|
+
notification_payload[:row_count] = result.length
|
|
147
|
+
result
|
|
131
148
|
end
|
|
132
149
|
end
|
|
133
150
|
end
|
|
134
151
|
|
|
135
|
-
def last_inserted_id(result)
|
|
136
|
-
@connection.last_insert_row_id
|
|
137
|
-
end
|
|
138
|
-
|
|
139
152
|
def build_fixture_statements(fixture_set)
|
|
140
153
|
fixture_set.flat_map do |table_name, fixtures|
|
|
141
154
|
next if fixtures.empty?
|
|
@@ -146,6 +159,10 @@ module ActiveRecord
|
|
|
146
159
|
def build_truncate_statement(table_name)
|
|
147
160
|
"DELETE FROM #{quote_table_name(table_name)}"
|
|
148
161
|
end
|
|
162
|
+
|
|
163
|
+
def returning_column_values(result)
|
|
164
|
+
result.rows.first
|
|
165
|
+
end
|
|
149
166
|
end
|
|
150
167
|
end
|
|
151
168
|
end
|
|
@@ -4,23 +4,71 @@ module ActiveRecord
|
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
module SQLite3
|
|
6
6
|
module Quoting # :nodoc:
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
7
9
|
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
|
8
10
|
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
module ClassMethods # :nodoc:
|
|
13
|
+
def column_name_matcher
|
|
14
|
+
/
|
|
15
|
+
\A
|
|
16
|
+
(
|
|
17
|
+
(?:
|
|
18
|
+
# "table_name"."column_name" | function(one or no argument)
|
|
19
|
+
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+") | \w+\((?:|\g<2>)\))
|
|
20
|
+
)
|
|
21
|
+
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
|
22
|
+
)
|
|
23
|
+
(?:\s*,\s*\g<1>)*
|
|
24
|
+
\z
|
|
25
|
+
/ix
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def column_name_with_order_matcher
|
|
29
|
+
/
|
|
30
|
+
\A
|
|
31
|
+
(
|
|
32
|
+
(?:
|
|
33
|
+
# "table_name"."column_name" | function(one or no argument)
|
|
34
|
+
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+") | \w+\((?:|\g<2>)\))
|
|
35
|
+
)
|
|
36
|
+
(?:\s+COLLATE\s+(?:\w+|"\w+"))?
|
|
37
|
+
(?:\s+ASC|\s+DESC)?
|
|
38
|
+
)
|
|
39
|
+
(?:\s*,\s*\g<1>)*
|
|
40
|
+
\z
|
|
41
|
+
/ix
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def quote_column_name(name)
|
|
45
|
+
QUOTED_COLUMN_NAMES[name] ||= %Q("#{name.to_s.gsub('"', '""')}").freeze
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def quote_table_name(name)
|
|
49
|
+
QUOTED_TABLE_NAMES[name] ||= %Q("#{name.to_s.gsub('"', '""').gsub(".", "\".\"")}").freeze
|
|
50
|
+
end
|
|
12
51
|
end
|
|
13
52
|
|
|
14
|
-
def
|
|
15
|
-
|
|
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
|
|
16
64
|
end
|
|
17
65
|
|
|
18
|
-
def
|
|
19
|
-
|
|
66
|
+
def quote_string(s)
|
|
67
|
+
::SQLite3::Database.quote(s)
|
|
20
68
|
end
|
|
21
69
|
|
|
22
|
-
def
|
|
23
|
-
|
|
70
|
+
def quote_table_name_for_assignment(table, attr)
|
|
71
|
+
quote_column_name(attr)
|
|
24
72
|
end
|
|
25
73
|
|
|
26
74
|
def quoted_time(value)
|
|
@@ -63,7 +111,7 @@ module ActiveRecord
|
|
|
63
111
|
|
|
64
112
|
def type_cast(value) # :nodoc:
|
|
65
113
|
case value
|
|
66
|
-
when BigDecimal
|
|
114
|
+
when BigDecimal, Rational
|
|
67
115
|
value.to_f
|
|
68
116
|
when String
|
|
69
117
|
if value.encoding == Encoding::ASCII_8BIT
|
|
@@ -75,42 +123,6 @@ module ActiveRecord
|
|
|
75
123
|
super
|
|
76
124
|
end
|
|
77
125
|
end
|
|
78
|
-
|
|
79
|
-
def column_name_matcher
|
|
80
|
-
COLUMN_NAME
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def column_name_with_order_matcher
|
|
84
|
-
COLUMN_NAME_WITH_ORDER
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
COLUMN_NAME = /
|
|
88
|
-
\A
|
|
89
|
-
(
|
|
90
|
-
(?:
|
|
91
|
-
# "table_name"."column_name" | function(one or no argument)
|
|
92
|
-
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
|
|
93
|
-
)
|
|
94
|
-
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
|
95
|
-
)
|
|
96
|
-
(?:\s*,\s*\g<1>)*
|
|
97
|
-
\z
|
|
98
|
-
/ix
|
|
99
|
-
|
|
100
|
-
COLUMN_NAME_WITH_ORDER = /
|
|
101
|
-
\A
|
|
102
|
-
(
|
|
103
|
-
(?:
|
|
104
|
-
# "table_name"."column_name" | function(one or no argument)
|
|
105
|
-
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
|
|
106
|
-
)
|
|
107
|
-
(?:\s+ASC|\s+DESC)?
|
|
108
|
-
)
|
|
109
|
-
(?:\s*,\s*\g<1>)*
|
|
110
|
-
\z
|
|
111
|
-
/ix
|
|
112
|
-
|
|
113
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
|
114
126
|
end
|
|
115
127
|
end
|
|
116
128
|
end
|
|
@@ -5,6 +5,18 @@ module ActiveRecord
|
|
|
5
5
|
module SQLite3
|
|
6
6
|
class SchemaCreation < SchemaCreation # :nodoc:
|
|
7
7
|
private
|
|
8
|
+
def visit_AddForeignKey(o)
|
|
9
|
+
super.dup.tap do |sql|
|
|
10
|
+
sql << " DEFERRABLE INITIALLY #{o.options[:deferrable].to_s.upcase}" if o.deferrable
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def visit_ForeignKeyDefinition(o)
|
|
15
|
+
super.dup.tap do |sql|
|
|
16
|
+
sql << " DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}" if o.deferrable
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
8
20
|
def supports_index_using?
|
|
9
21
|
false
|
|
10
22
|
end
|
|
@@ -13,6 +25,16 @@ module ActiveRecord
|
|
|
13
25
|
if options[:collation]
|
|
14
26
|
sql << " COLLATE \"#{options[:collation]}\""
|
|
15
27
|
end
|
|
28
|
+
|
|
29
|
+
if as = options[:as]
|
|
30
|
+
sql << " GENERATED ALWAYS AS (#{as})"
|
|
31
|
+
|
|
32
|
+
if options[:stored]
|
|
33
|
+
sql << " STORED"
|
|
34
|
+
else
|
|
35
|
+
sql << " VIRTUAL"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
16
38
|
super
|
|
17
39
|
end
|
|
18
40
|
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
module SQLite3
|
|
6
|
+
# = Active Record SQLite3 Adapter \Table Definition
|
|
6
7
|
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
|
7
8
|
def change_column(column_name, type, **options)
|
|
8
9
|
name = column_name.to_s
|
|
@@ -15,10 +16,23 @@ module ActiveRecord
|
|
|
15
16
|
end
|
|
16
17
|
alias :belongs_to :references
|
|
17
18
|
|
|
19
|
+
def new_column_definition(name, type, **options) # :nodoc:
|
|
20
|
+
case type
|
|
21
|
+
when :virtual
|
|
22
|
+
type = options[:type]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
|
|
18
28
|
private
|
|
19
29
|
def integer_like_primary_key_type(type, options)
|
|
20
30
|
:primary_key
|
|
21
31
|
end
|
|
32
|
+
|
|
33
|
+
def valid_column_definition_options
|
|
34
|
+
super + [:as, :type, :stored]
|
|
35
|
+
end
|
|
22
36
|
end
|
|
23
37
|
end
|
|
24
38
|
end
|
|
@@ -12,6 +12,22 @@ module ActiveRecord
|
|
|
12
12
|
def explicit_primary_key_default?(column)
|
|
13
13
|
column.bigint?
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
def prepare_column_options(column)
|
|
17
|
+
spec = super
|
|
18
|
+
|
|
19
|
+
if @connection.supports_virtual_columns? && column.virtual?
|
|
20
|
+
spec[:as] = extract_expression_for_virtual_column(column)
|
|
21
|
+
spec[:stored] = column.virtual_stored?
|
|
22
|
+
spec = { type: schema_type(column).inspect }.merge!(spec)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
spec
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def extract_expression_for_virtual_column(column)
|
|
29
|
+
column.default_function.inspect
|
|
30
|
+
end
|
|
15
31
|
end
|
|
16
32
|
end
|
|
17
33
|
end
|
|
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
|
6
6
|
module SchemaStatements # :nodoc:
|
|
7
7
|
# Returns an array of indexes for the given table.
|
|
8
8
|
def indexes(table_name)
|
|
9
|
-
|
|
9
|
+
internal_exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").filter_map do |row|
|
|
10
10
|
# Indexes SQLite creates implicitly for internal use start with "sqlite_".
|
|
11
11
|
# See https://www.sqlite.org/fileformat2.html#intschema
|
|
12
12
|
next if row["name"].start_with?("sqlite_")
|
|
@@ -23,7 +23,7 @@ module ActiveRecord
|
|
|
23
23
|
|
|
24
24
|
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?(?:\s*\/\*.*\*\/)?\z/i =~ index_sql
|
|
25
25
|
|
|
26
|
-
columns =
|
|
26
|
+
columns = internal_exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
|
|
27
27
|
col["name"]
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -53,6 +53,8 @@ module ActiveRecord
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def add_foreign_key(from_table, to_table, **options)
|
|
56
|
+
assert_valid_deferrable(options[:deferrable])
|
|
57
|
+
|
|
56
58
|
alter_table(from_table) do |definition|
|
|
57
59
|
to_table = strip_table_name_prefix_and_suffix(to_table)
|
|
58
60
|
definition.foreign_key(to_table, **options)
|
|
@@ -72,6 +74,7 @@ module ActiveRecord
|
|
|
72
74
|
Base.pluralize_table_names ? table.pluralize : table
|
|
73
75
|
end
|
|
74
76
|
table = strip_table_name_prefix_and_suffix(table)
|
|
77
|
+
options = options.slice(*fk.options.keys)
|
|
75
78
|
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
|
76
79
|
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
|
77
80
|
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
|
@@ -102,7 +105,9 @@ module ActiveRecord
|
|
|
102
105
|
end
|
|
103
106
|
end
|
|
104
107
|
|
|
105
|
-
def remove_check_constraint(table_name, expression = nil, **options)
|
|
108
|
+
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
|
109
|
+
return if if_exists && !check_constraint_exists?(table_name, **options)
|
|
110
|
+
|
|
106
111
|
check_constraints = check_constraints(table_name)
|
|
107
112
|
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
|
108
113
|
check_constraints.delete_if { |chk| chk.name == chk_name_to_delete }
|
|
@@ -113,9 +118,13 @@ module ActiveRecord
|
|
|
113
118
|
SQLite3::SchemaDumper.create(self, options)
|
|
114
119
|
end
|
|
115
120
|
|
|
121
|
+
def schema_creation # :nodoc
|
|
122
|
+
SQLite3::SchemaCreation.new(self)
|
|
123
|
+
end
|
|
124
|
+
|
|
116
125
|
private
|
|
117
|
-
def
|
|
118
|
-
|
|
126
|
+
def valid_table_definition_options
|
|
127
|
+
super + [:rename]
|
|
119
128
|
end
|
|
120
129
|
|
|
121
130
|
def create_table_definition(name, **options)
|
|
@@ -126,12 +135,20 @@ module ActiveRecord
|
|
|
126
135
|
super unless internal
|
|
127
136
|
end
|
|
128
137
|
|
|
129
|
-
def new_column_from_field(table_name, field)
|
|
138
|
+
def new_column_from_field(table_name, field, definitions)
|
|
130
139
|
default = field["dflt_value"]
|
|
131
140
|
|
|
132
141
|
type_metadata = fetch_type_metadata(field["type"])
|
|
133
142
|
default_value = extract_value_from_default(default)
|
|
134
|
-
|
|
143
|
+
generated_type = extract_generated_type(field)
|
|
144
|
+
|
|
145
|
+
if generated_type.present?
|
|
146
|
+
default_function = default
|
|
147
|
+
else
|
|
148
|
+
default_function = extract_default_function(default_value, default)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
rowid = is_column_the_rowid?(field, definitions)
|
|
135
152
|
|
|
136
153
|
Column.new(
|
|
137
154
|
field["name"],
|
|
@@ -139,10 +156,23 @@ module ActiveRecord
|
|
|
139
156
|
type_metadata,
|
|
140
157
|
field["notnull"].to_i == 0,
|
|
141
158
|
default_function,
|
|
142
|
-
collation: field["collation"]
|
|
159
|
+
collation: field["collation"],
|
|
160
|
+
auto_increment: field["auto_increment"],
|
|
161
|
+
rowid: rowid,
|
|
162
|
+
generated_type: generated_type
|
|
143
163
|
)
|
|
144
164
|
end
|
|
145
165
|
|
|
166
|
+
INTEGER_REGEX = /integer/i
|
|
167
|
+
# if a rowid table has a primary key that consists of a single column
|
|
168
|
+
# and the declared type of that column is "INTEGER" in any mixture of upper and lower case,
|
|
169
|
+
# then the column becomes an alias for the rowid.
|
|
170
|
+
def is_column_the_rowid?(field, column_definitions)
|
|
171
|
+
return false unless INTEGER_REGEX.match?(field["type"]) && field["pk"] == 1
|
|
172
|
+
# is the primary key a single column?
|
|
173
|
+
column_definitions.one? { |c| c["pk"] > 0 }
|
|
174
|
+
end
|
|
175
|
+
|
|
146
176
|
def data_source_sql(name = nil, type: nil)
|
|
147
177
|
scope = quoted_scope(name, type: type)
|
|
148
178
|
scope[:type] ||= "'table','view'"
|
|
@@ -166,6 +196,19 @@ module ActiveRecord
|
|
|
166
196
|
scope[:type] = type if type
|
|
167
197
|
scope
|
|
168
198
|
end
|
|
199
|
+
|
|
200
|
+
def assert_valid_deferrable(deferrable)
|
|
201
|
+
return if !deferrable || %i(immediate deferred).include?(deferrable)
|
|
202
|
+
|
|
203
|
+
raise ArgumentError, "deferrable must be `:immediate` or `:deferred`, got: `#{deferrable.inspect}`"
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def extract_generated_type(field)
|
|
207
|
+
case field["hidden"]
|
|
208
|
+
when 2 then :virtual
|
|
209
|
+
when 3 then :stored
|
|
210
|
+
end
|
|
211
|
+
end
|
|
169
212
|
end
|
|
170
213
|
end
|
|
171
214
|
end
|