activerecord 7.0.8.1 → 7.2.2.1
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 +642 -1925
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- 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 +25 -19
- 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 +23 -8
- 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 +26 -14
- 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 +30 -27
- 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 +148 -33
- data/lib/active_record/attributes.rb +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- 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 +323 -88
- 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 +217 -63
- 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 +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
- 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 +53 -54
- 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 +101 -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 +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- 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 +45 -46
- 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 +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
- 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 +96 -104
- data/lib/active_record/core.rb +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -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 +39 -10
- 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 +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- 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 +129 -28
- data/lib/active_record/errors.rb +151 -31
- 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 +29 -8
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +3 -3
- 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 +234 -117
- 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 +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +129 -85
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- 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 +250 -93
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +93 -18
- 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 +18 -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 +2 -1
- data/lib/active_record/relation/query_methods.rb +576 -107
- 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 +7 -19
- data/lib/active_record/relation.rb +580 -90
- 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 +63 -14
- 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 +27 -6
- 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 +16 -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 +106 -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 +2 -0
- 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/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/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.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -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 +112 -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 +59 -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,58 @@ 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
|
-
|
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
|
13
27
|
|
14
|
-
|
15
|
-
|
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
|
16
51
|
end
|
17
52
|
|
18
|
-
def
|
19
|
-
|
53
|
+
def quote_string(s)
|
54
|
+
::SQLite3::Database.quote(s)
|
20
55
|
end
|
21
56
|
|
22
|
-
def
|
23
|
-
|
57
|
+
def quote_table_name_for_assignment(table, attr)
|
58
|
+
quote_column_name(attr)
|
24
59
|
end
|
25
60
|
|
26
61
|
def quoted_time(value)
|
@@ -63,7 +98,7 @@ module ActiveRecord
|
|
63
98
|
|
64
99
|
def type_cast(value) # :nodoc:
|
65
100
|
case value
|
66
|
-
when BigDecimal
|
101
|
+
when BigDecimal, Rational
|
67
102
|
value.to_f
|
68
103
|
when String
|
69
104
|
if value.encoding == Encoding::ASCII_8BIT
|
@@ -75,42 +110,6 @@ module ActiveRecord
|
|
75
110
|
super
|
76
111
|
end
|
77
112
|
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
113
|
end
|
115
114
|
end
|
116
115
|
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)
|
@@ -102,7 +104,9 @@ module ActiveRecord
|
|
102
104
|
end
|
103
105
|
end
|
104
106
|
|
105
|
-
def remove_check_constraint(table_name, expression = nil, **options)
|
107
|
+
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
108
|
+
return if if_exists && !check_constraint_exists?(table_name, **options)
|
109
|
+
|
106
110
|
check_constraints = check_constraints(table_name)
|
107
111
|
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
108
112
|
check_constraints.delete_if { |chk| chk.name == chk_name_to_delete }
|
@@ -113,9 +117,13 @@ module ActiveRecord
|
|
113
117
|
SQLite3::SchemaDumper.create(self, options)
|
114
118
|
end
|
115
119
|
|
120
|
+
def schema_creation # :nodoc
|
121
|
+
SQLite3::SchemaCreation.new(self)
|
122
|
+
end
|
123
|
+
|
116
124
|
private
|
117
|
-
def
|
118
|
-
|
125
|
+
def valid_table_definition_options
|
126
|
+
super + [:rename]
|
119
127
|
end
|
120
128
|
|
121
129
|
def create_table_definition(name, **options)
|
@@ -126,12 +134,20 @@ module ActiveRecord
|
|
126
134
|
super unless internal
|
127
135
|
end
|
128
136
|
|
129
|
-
def new_column_from_field(table_name, field)
|
137
|
+
def new_column_from_field(table_name, field, definitions)
|
130
138
|
default = field["dflt_value"]
|
131
139
|
|
132
140
|
type_metadata = fetch_type_metadata(field["type"])
|
133
141
|
default_value = extract_value_from_default(default)
|
134
|
-
|
142
|
+
generated_type = extract_generated_type(field)
|
143
|
+
|
144
|
+
if generated_type.present?
|
145
|
+
default_function = default
|
146
|
+
else
|
147
|
+
default_function = extract_default_function(default_value, default)
|
148
|
+
end
|
149
|
+
|
150
|
+
rowid = is_column_the_rowid?(field, definitions)
|
135
151
|
|
136
152
|
Column.new(
|
137
153
|
field["name"],
|
@@ -139,10 +155,23 @@ module ActiveRecord
|
|
139
155
|
type_metadata,
|
140
156
|
field["notnull"].to_i == 0,
|
141
157
|
default_function,
|
142
|
-
collation: field["collation"]
|
158
|
+
collation: field["collation"],
|
159
|
+
auto_increment: field["auto_increment"],
|
160
|
+
rowid: rowid,
|
161
|
+
generated_type: generated_type
|
143
162
|
)
|
144
163
|
end
|
145
164
|
|
165
|
+
INTEGER_REGEX = /integer/i
|
166
|
+
# if a rowid table has a primary key that consists of a single column
|
167
|
+
# and the declared type of that column is "INTEGER" in any mixture of upper and lower case,
|
168
|
+
# then the column becomes an alias for the rowid.
|
169
|
+
def is_column_the_rowid?(field, column_definitions)
|
170
|
+
return false unless INTEGER_REGEX.match?(field["type"]) && field["pk"] == 1
|
171
|
+
# is the primary key a single column?
|
172
|
+
column_definitions.one? { |c| c["pk"] > 0 }
|
173
|
+
end
|
174
|
+
|
146
175
|
def data_source_sql(name = nil, type: nil)
|
147
176
|
scope = quoted_scope(name, type: type)
|
148
177
|
scope[:type] ||= "'table','view'"
|
@@ -166,6 +195,19 @@ module ActiveRecord
|
|
166
195
|
scope[:type] = type if type
|
167
196
|
scope
|
168
197
|
end
|
198
|
+
|
199
|
+
def assert_valid_deferrable(deferrable)
|
200
|
+
return if !deferrable || %i(immediate deferred).include?(deferrable)
|
201
|
+
|
202
|
+
raise ArgumentError, "deferrable must be `:immediate` or `:deferred`, got: `#{deferrable.inspect}`"
|
203
|
+
end
|
204
|
+
|
205
|
+
def extract_generated_type(field)
|
206
|
+
case field["hidden"]
|
207
|
+
when 2 then :virtual
|
208
|
+
when 3 then :stored
|
209
|
+
end
|
210
|
+
end
|
169
211
|
end
|
170
212
|
end
|
171
213
|
end
|