activerecord 7.1.5.1 → 8.0.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 +369 -2484
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +21 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- 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/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- 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/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +92 -295
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +63 -49
- data/lib/active_record/autosave_association.rb +92 -57
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
- data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
- data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
- data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
- data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +65 -0
- data/lib/active_record/connection_handling.rb +74 -37
- data/lib/active_record/core.rb +132 -51
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +23 -4
- data/lib/active_record/database_configurations/hash_config.rb +46 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +41 -17
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -7
- data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
- data/lib/active_record/encryption/encryptor.rb +28 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- 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 +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +20 -16
- data/lib/active_record/errors.rb +54 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -33
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +19 -16
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +5 -32
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +33 -14
- data/lib/active_record/migration/compatibility.rb +8 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +104 -98
- data/lib/active_record/model_schema.rb +32 -70
- data/lib/active_record/nested_attributes.rb +15 -9
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +127 -451
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +104 -37
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +24 -12
- data/lib/active_record/railtie.rb +26 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +43 -61
- data/lib/active_record/reflection.rb +112 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +138 -72
- data/lib/active_record/relation/calculations.rb +122 -82
- data/lib/active_record/relation/delegation.rb +30 -22
- data/lib/active_record/relation/finder_methods.rb +32 -18
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +16 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +317 -101
- data/lib/active_record/relation/spawn_methods.rb +3 -19
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +561 -119
- data/lib/active_record/result.rb +95 -46
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +31 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +53 -20
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/signed_id.rb +24 -4
- data/lib/active_record/statement_cache.rb +19 -19
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +2 -13
- data/lib/active_record/tasks/database_tasks.rb +87 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
- data/lib/active_record/test_fixtures.rb +98 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +72 -17
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +138 -57
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +5 -4
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -16
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -21,135 +21,111 @@ module ActiveRecord
|
|
21
21
|
SQLite3::ExplainPrettyPrinter.new.pp(result)
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
|
26
|
-
check_if_write_query(sql)
|
27
|
-
|
28
|
-
mark_transaction_written_if_write(sql)
|
29
|
-
|
30
|
-
type_casted_binds = type_casted_binds(binds)
|
31
|
-
|
32
|
-
log(sql, name, binds, type_casted_binds, async: async) do
|
33
|
-
with_raw_connection do |conn|
|
34
|
-
# Don't cache statements if they are not prepared
|
35
|
-
unless prepare
|
36
|
-
stmt = conn.prepare(sql)
|
37
|
-
begin
|
38
|
-
cols = stmt.columns
|
39
|
-
unless without_prepared_statement?(binds)
|
40
|
-
stmt.bind_params(type_casted_binds)
|
41
|
-
end
|
42
|
-
records = stmt.to_a
|
43
|
-
ensure
|
44
|
-
stmt.close
|
45
|
-
end
|
46
|
-
else
|
47
|
-
stmt = @statements[sql] ||= conn.prepare(sql)
|
48
|
-
cols = stmt.columns
|
49
|
-
stmt.reset!
|
50
|
-
stmt.bind_params(type_casted_binds)
|
51
|
-
records = stmt.to_a
|
52
|
-
end
|
53
|
-
verified!
|
54
|
-
|
55
|
-
build_result(columns: cols, rows: records)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
|
61
|
-
internal_exec_query(sql, name, binds)
|
62
|
-
@raw_connection.changes
|
24
|
+
def begin_deferred_transaction(isolation = nil) # :nodoc:
|
25
|
+
internal_begin_transaction(:deferred, isolation)
|
63
26
|
end
|
64
|
-
alias :exec_update :exec_delete
|
65
27
|
|
66
28
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
67
|
-
|
68
|
-
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
69
|
-
|
70
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
71
|
-
ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = conn.get_first_value("PRAGMA read_uncommitted")
|
72
|
-
conn.read_uncommitted = true
|
73
|
-
begin_db_transaction
|
74
|
-
end
|
29
|
+
internal_begin_transaction(:deferred, isolation)
|
75
30
|
end
|
76
31
|
|
77
32
|
def begin_db_transaction # :nodoc:
|
78
|
-
|
79
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
80
|
-
result = conn.transaction
|
81
|
-
verified!
|
82
|
-
result
|
83
|
-
end
|
84
|
-
end
|
33
|
+
internal_begin_transaction(:immediate, nil)
|
85
34
|
end
|
86
35
|
|
87
36
|
def commit_db_transaction # :nodoc:
|
88
|
-
|
89
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
90
|
-
conn.commit
|
91
|
-
end
|
92
|
-
end
|
93
|
-
reset_read_uncommitted
|
37
|
+
internal_execute("COMMIT TRANSACTION", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
94
38
|
end
|
95
39
|
|
96
40
|
def exec_rollback_db_transaction # :nodoc:
|
97
|
-
|
98
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
99
|
-
conn.rollback
|
100
|
-
end
|
101
|
-
end
|
102
|
-
reset_read_uncommitted
|
41
|
+
internal_execute("ROLLBACK TRANSACTION", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
103
42
|
end
|
104
43
|
|
105
44
|
# https://stackoverflow.com/questions/17574784
|
106
45
|
# https://www.sqlite.org/lang_datefunc.html
|
107
|
-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')").freeze # :nodoc:
|
46
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')", retryable: true).freeze # :nodoc:
|
108
47
|
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
109
48
|
|
110
49
|
def high_precision_current_timestamp
|
111
50
|
HIGH_PRECISION_CURRENT_TIMESTAMP
|
112
51
|
end
|
113
52
|
|
53
|
+
def execute(...) # :nodoc:
|
54
|
+
# SQLite3Adapter was refactored to use ActiveRecord::Result internally
|
55
|
+
# but for backward compatibility we have to keep returning arrays of hashes here
|
56
|
+
super&.to_a
|
57
|
+
end
|
58
|
+
|
59
|
+
def reset_isolation_level # :nodoc:
|
60
|
+
internal_execute("PRAGMA read_uncommitted=#{@previous_read_uncommitted}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
61
|
+
@previous_read_uncommitted = nil
|
62
|
+
end
|
63
|
+
|
114
64
|
private
|
115
|
-
def
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
65
|
+
def internal_begin_transaction(mode, isolation)
|
66
|
+
if isolation
|
67
|
+
raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
68
|
+
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
69
|
+
end
|
70
|
+
|
71
|
+
internal_execute("BEGIN #{mode} TRANSACTION", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
72
|
+
if isolation
|
73
|
+
@previous_read_uncommitted = query_value("PRAGMA read_uncommitted")
|
74
|
+
internal_execute("PRAGMA read_uncommitted=ON", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
122
75
|
end
|
123
76
|
end
|
124
77
|
|
125
|
-
def
|
126
|
-
|
127
|
-
|
78
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
79
|
+
if batch
|
80
|
+
raw_connection.execute_batch2(sql)
|
81
|
+
elsif prepare
|
82
|
+
stmt = @statements[sql] ||= raw_connection.prepare(sql)
|
83
|
+
stmt.reset!
|
84
|
+
stmt.bind_params(type_casted_binds)
|
85
|
+
|
86
|
+
result = if stmt.column_count.zero? # No return
|
87
|
+
stmt.step
|
88
|
+
ActiveRecord::Result.empty
|
89
|
+
else
|
90
|
+
ActiveRecord::Result.new(stmt.columns, stmt.to_a)
|
91
|
+
end
|
92
|
+
else
|
93
|
+
# Don't cache statements if they are not prepared.
|
94
|
+
stmt = raw_connection.prepare(sql)
|
95
|
+
begin
|
96
|
+
unless binds.nil? || binds.empty?
|
97
|
+
stmt.bind_params(type_casted_binds)
|
98
|
+
end
|
99
|
+
result = if stmt.column_count.zero? # No return
|
100
|
+
stmt.step
|
101
|
+
ActiveRecord::Result.empty
|
102
|
+
else
|
103
|
+
ActiveRecord::Result.new(stmt.columns, stmt.to_a)
|
104
|
+
end
|
105
|
+
ensure
|
106
|
+
stmt.close
|
107
|
+
end
|
108
|
+
end
|
109
|
+
@last_affected_rows = raw_connection.changes
|
110
|
+
verified!
|
128
111
|
|
129
|
-
|
112
|
+
notification_payload[:row_count] = result&.length || 0
|
113
|
+
result
|
130
114
|
end
|
131
115
|
|
132
|
-
def
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
mark_transaction_written_if_write(sql)
|
116
|
+
def cast_result(result)
|
117
|
+
# Given that SQLite3 doesn't really a Result type, raw_execute already return an ActiveRecord::Result
|
118
|
+
# and we have nothing to cast here.
|
119
|
+
result
|
120
|
+
end
|
138
121
|
|
139
|
-
|
140
|
-
|
141
|
-
result = conn.execute_batch2(sql)
|
142
|
-
verified!
|
143
|
-
result
|
144
|
-
end
|
145
|
-
end
|
122
|
+
def affected_rows(result)
|
123
|
+
@last_affected_rows
|
146
124
|
end
|
147
125
|
|
148
|
-
def
|
149
|
-
|
150
|
-
|
151
|
-
fixtures.map { |fixture| build_fixture_sql([fixture], table_name) }
|
152
|
-
end.compact
|
126
|
+
def execute_batch(statements, name = nil, **kwargs)
|
127
|
+
sql = combine_multi_statements(statements)
|
128
|
+
raw_execute(sql, name, batch: true, **kwargs)
|
153
129
|
end
|
154
130
|
|
155
131
|
def build_truncate_statement(table_name)
|
@@ -159,6 +135,14 @@ module ActiveRecord
|
|
159
135
|
def returning_column_values(result)
|
160
136
|
result.rows.first
|
161
137
|
end
|
138
|
+
|
139
|
+
def default_insert_value(column)
|
140
|
+
if column.default_function
|
141
|
+
Arel.sql(column.default_function)
|
142
|
+
else
|
143
|
+
column.default
|
144
|
+
end
|
145
|
+
end
|
162
146
|
end
|
163
147
|
end
|
164
148
|
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,43 +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+COLLATE\s+(?:\w+|"\w+"))?
|
108
|
-
(?:\s+ASC|\s+DESC)?
|
109
|
-
)
|
110
|
-
(?:\s*,\s*\g<1>)*
|
111
|
-
\z
|
112
|
-
/ix
|
113
|
-
|
114
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
115
126
|
end
|
116
127
|
end
|
117
128
|
end
|
@@ -5,6 +5,12 @@ module ActiveRecord
|
|
5
5
|
module SQLite3
|
6
6
|
class SchemaCreation < SchemaCreation # :nodoc:
|
7
7
|
private
|
8
|
+
def visit_ForeignKeyDefinition(o)
|
9
|
+
super.dup.tap do |sql|
|
10
|
+
sql << " DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}" if o.deferrable
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
8
14
|
def supports_index_using?
|
9
15
|
false
|
10
16
|
end
|
@@ -13,6 +19,16 @@ module ActiveRecord
|
|
13
19
|
if options[:collation]
|
14
20
|
sql << " COLLATE \"#{options[:collation]}\""
|
15
21
|
end
|
22
|
+
|
23
|
+
if as = options[:as]
|
24
|
+
sql << " GENERATED ALWAYS AS (#{as})"
|
25
|
+
|
26
|
+
if options[:stored]
|
27
|
+
sql << " STORED"
|
28
|
+
else
|
29
|
+
sql << " VIRTUAL"
|
30
|
+
end
|
31
|
+
end
|
16
32
|
super
|
17
33
|
end
|
18
34
|
end
|
@@ -16,10 +16,23 @@ module ActiveRecord
|
|
16
16
|
end
|
17
17
|
alias :belongs_to :references
|
18
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
|
+
|
19
28
|
private
|
20
29
|
def integer_like_primary_key_type(type, options)
|
21
30
|
:primary_key
|
22
31
|
end
|
32
|
+
|
33
|
+
def valid_column_definition_options
|
34
|
+
super + [:as, :type, :stored]
|
35
|
+
end
|
23
36
|
end
|
24
37
|
end
|
25
38
|
end
|
@@ -5,6 +5,19 @@ module ActiveRecord
|
|
5
5
|
module SQLite3
|
6
6
|
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
|
7
7
|
private
|
8
|
+
def virtual_tables(stream)
|
9
|
+
virtual_tables = @connection.virtual_tables
|
10
|
+
if virtual_tables.any?
|
11
|
+
stream.puts
|
12
|
+
stream.puts " # Virtual tables defined in this database."
|
13
|
+
stream.puts " # Note that virtual tables may not work with other database engines. Be careful if changing database."
|
14
|
+
virtual_tables.sort.each do |table_name, options|
|
15
|
+
module_name, arguments = options
|
16
|
+
stream.puts " create_virtual_table #{table_name.inspect}, #{module_name.inspect}, #{arguments.split(", ").inspect}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
8
21
|
def default_primary_key?(column)
|
9
22
|
schema_type(column) == :integer
|
10
23
|
end
|
@@ -12,6 +25,22 @@ module ActiveRecord
|
|
12
25
|
def explicit_primary_key_default?(column)
|
13
26
|
column.bigint?
|
14
27
|
end
|
28
|
+
|
29
|
+
def prepare_column_options(column)
|
30
|
+
spec = super
|
31
|
+
|
32
|
+
if @connection.supports_virtual_columns? && column.virtual?
|
33
|
+
spec[:as] = extract_expression_for_virtual_column(column)
|
34
|
+
spec[:stored] = column.virtual_stored?
|
35
|
+
spec = { type: schema_type(column).inspect }.merge!(spec)
|
36
|
+
end
|
37
|
+
|
38
|
+
spec
|
39
|
+
end
|
40
|
+
|
41
|
+
def extract_expression_for_virtual_column(column)
|
42
|
+
column.default_function.inspect
|
43
|
+
end
|
15
44
|
end
|
16
45
|
end
|
17
46
|
end
|
@@ -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
|
@@ -53,6 +54,8 @@ module ActiveRecord
|
|
53
54
|
end
|
54
55
|
|
55
56
|
def add_foreign_key(from_table, to_table, **options)
|
57
|
+
assert_valid_deferrable(options[:deferrable])
|
58
|
+
|
56
59
|
alter_table(from_table) do |definition|
|
57
60
|
to_table = strip_table_name_prefix_and_suffix(to_table)
|
58
61
|
definition.foreign_key(to_table, **options)
|
@@ -72,6 +75,7 @@ module ActiveRecord
|
|
72
75
|
Base.pluralize_table_names ? table.pluralize : table
|
73
76
|
end
|
74
77
|
table = strip_table_name_prefix_and_suffix(table)
|
78
|
+
options = options.slice(*fk.options.keys)
|
75
79
|
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
76
80
|
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
77
81
|
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
@@ -80,6 +84,10 @@ module ActiveRecord
|
|
80
84
|
alter_table(from_table, foreign_keys)
|
81
85
|
end
|
82
86
|
|
87
|
+
def virtual_table_exists?(table_name)
|
88
|
+
query_values(data_source_sql(table_name, type: "VIRTUAL TABLE"), "SCHEMA").any?
|
89
|
+
end
|
90
|
+
|
83
91
|
def check_constraints(table_name)
|
84
92
|
table_sql = query_value(<<-SQL, "SCHEMA")
|
85
93
|
SELECT sql
|
@@ -137,7 +145,14 @@ module ActiveRecord
|
|
137
145
|
|
138
146
|
type_metadata = fetch_type_metadata(field["type"])
|
139
147
|
default_value = extract_value_from_default(default)
|
140
|
-
|
148
|
+
generated_type = extract_generated_type(field)
|
149
|
+
|
150
|
+
if generated_type.present?
|
151
|
+
default_function = default
|
152
|
+
else
|
153
|
+
default_function = extract_default_function(default_value, default)
|
154
|
+
end
|
155
|
+
|
141
156
|
rowid = is_column_the_rowid?(field, definitions)
|
142
157
|
|
143
158
|
Column.new(
|
@@ -148,7 +163,8 @@ module ActiveRecord
|
|
148
163
|
default_function,
|
149
164
|
collation: field["collation"],
|
150
165
|
auto_increment: field["auto_increment"],
|
151
|
-
rowid: rowid
|
166
|
+
rowid: rowid,
|
167
|
+
generated_type: generated_type
|
152
168
|
)
|
153
169
|
end
|
154
170
|
|
@@ -166,7 +182,8 @@ module ActiveRecord
|
|
166
182
|
scope = quoted_scope(name, type: type)
|
167
183
|
scope[:type] ||= "'table','view'"
|
168
184
|
|
169
|
-
sql = +"SELECT name FROM
|
185
|
+
sql = +"SELECT name FROM pragma_table_list WHERE schema <> 'temp'"
|
186
|
+
sql << " AND name NOT IN ('sqlite_sequence', 'sqlite_schema')"
|
170
187
|
sql << " AND name = #{scope[:name]}" if scope[:name]
|
171
188
|
sql << " AND type IN (#{scope[:type]})"
|
172
189
|
sql
|
@@ -179,12 +196,27 @@ module ActiveRecord
|
|
179
196
|
"'table'"
|
180
197
|
when "VIEW"
|
181
198
|
"'view'"
|
199
|
+
when "VIRTUAL TABLE"
|
200
|
+
"'virtual'"
|
182
201
|
end
|
183
202
|
scope = {}
|
184
203
|
scope[:name] = quote(name) if name
|
185
204
|
scope[:type] = type if type
|
186
205
|
scope
|
187
206
|
end
|
207
|
+
|
208
|
+
def assert_valid_deferrable(deferrable)
|
209
|
+
return if !deferrable || %i(immediate deferred).include?(deferrable)
|
210
|
+
|
211
|
+
raise ArgumentError, "deferrable must be `:immediate` or `:deferred`, got: `#{deferrable.inspect}`"
|
212
|
+
end
|
213
|
+
|
214
|
+
def extract_generated_type(field)
|
215
|
+
case field["hidden"]
|
216
|
+
when 2 then :virtual
|
217
|
+
when 3 then :stored
|
218
|
+
end
|
219
|
+
end
|
188
220
|
end
|
189
221
|
end
|
190
222
|
end
|