activerecord 8.0.0 → 8.1.4
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 +1381 -241
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +35 -17
- data/lib/active_record/associations/association_scope.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +20 -4
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +36 -8
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +9 -5
- data/lib/active_record/associations/collection_proxy.rb +26 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/associations/join_dependency.rb +5 -4
- data/lib/active_record/associations/preloader/association.rb +1 -1
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/primary_key.rb +2 -1
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +19 -6
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
- data/lib/active_record/attribute_methods.rb +24 -17
- data/lib/active_record/attributes.rb +40 -26
- data/lib/active_record/autosave_association.rb +22 -12
- data/lib/active_record/base.rb +3 -4
- data/lib/active_record/callbacks.rb +1 -2
- data/lib/active_record/coders/column_serializer.rb +1 -1
- data/lib/active_record/coders/json.rb +16 -6
- data/lib/active_record/coders/yaml_column.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +474 -112
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +68 -41
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +31 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +94 -28
- data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -64
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +133 -40
- data/lib/active_record/connection_adapters/column.rb +14 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +40 -25
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +29 -33
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +70 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +99 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +40 -12
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +15 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +7 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +68 -37
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +8 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -2
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/connection_handling.rb +15 -10
- data/lib/active_record/core.rb +44 -12
- data/lib/active_record/counter_cache.rb +36 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +66 -9
- data/lib/active_record/database_configurations/url_config.rb +15 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +19 -19
- data/lib/active_record/disable_joins_association_relation.rb +5 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
- data/lib/active_record/encryption/encryptor.rb +39 -25
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +37 -20
- data/lib/active_record/errors.rb +23 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +76 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/future_result.rb +5 -3
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +9 -4
- data/lib/active_record/insert_all.rb +14 -9
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +30 -22
- data/lib/active_record/message_pack.rb +13 -0
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +32 -6
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +31 -21
- data/lib/active_record/model_schema.rb +38 -12
- data/lib/active_record/nested_attributes.rb +10 -6
- data/lib/active_record/persistence.rb +35 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +7 -7
- data/lib/active_record/querying.rb +17 -5
- data/lib/active_record/railtie.rb +35 -6
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +24 -20
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +36 -1
- data/lib/active_record/relation/batches.rb +25 -11
- data/lib/active_record/relation/calculations.rb +62 -41
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +76 -45
- data/lib/active_record/relation/merger.rb +4 -2
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
- data/lib/active_record/relation/predicate_builder.rb +10 -8
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +106 -45
- data/lib/active_record/relation/spawn_methods.rb +6 -6
- data/lib/active_record/relation/where_clause.rb +10 -11
- data/lib/active_record/relation.rb +63 -31
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +41 -21
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +48 -19
- data/lib/active_record/statement_cache.rb +16 -12
- data/lib/active_record/store.rb +46 -19
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +5 -20
- data/lib/active_record/tasks/abstract_tasks.rb +83 -0
- data/lib/active_record/tasks/database_tasks.rb +44 -45
- data/lib/active_record/tasks/mysql_database_tasks.rb +4 -56
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +18 -4
- data/lib/active_record/test_fixtures.rb +40 -6
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +58 -32
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +13 -2
- data/lib/active_record/type/serialized.rb +16 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +72 -7
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +7 -23
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -15
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
|
@@ -61,6 +61,14 @@ module ActiveRecord
|
|
|
61
61
|
@previous_read_uncommitted = nil
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
def default_insert_value(column) # :nodoc:
|
|
65
|
+
if column.default_function
|
|
66
|
+
Arel.sql(column.default_function)
|
|
67
|
+
else
|
|
68
|
+
column.default
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
64
72
|
private
|
|
65
73
|
def internal_begin_transaction(mode, isolation)
|
|
66
74
|
if isolation
|
|
@@ -76,51 +84,63 @@ module ActiveRecord
|
|
|
76
84
|
end
|
|
77
85
|
|
|
78
86
|
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
|
87
|
+
total_changes_before_query = raw_connection.total_changes
|
|
88
|
+
affected_rows = nil
|
|
89
|
+
|
|
79
90
|
if batch
|
|
80
91
|
raw_connection.execute_batch2(sql)
|
|
81
|
-
|
|
82
|
-
stmt =
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
result = if stmt.column_count.zero? # No return
|
|
87
|
-
stmt.step
|
|
88
|
-
ActiveRecord::Result.empty
|
|
92
|
+
else
|
|
93
|
+
stmt = if prepare
|
|
94
|
+
@statements[sql] ||= raw_connection.prepare(sql)
|
|
95
|
+
@statements[sql].reset!
|
|
89
96
|
else
|
|
90
|
-
|
|
97
|
+
# Don't cache statements if they are not prepared.
|
|
98
|
+
raw_connection.prepare(sql)
|
|
91
99
|
end
|
|
92
|
-
else
|
|
93
|
-
# Don't cache statements if they are not prepared.
|
|
94
|
-
stmt = raw_connection.prepare(sql)
|
|
95
100
|
begin
|
|
96
101
|
unless binds.nil? || binds.empty?
|
|
97
102
|
stmt.bind_params(type_casted_binds)
|
|
98
103
|
end
|
|
99
104
|
result = if stmt.column_count.zero? # No return
|
|
100
105
|
stmt.step
|
|
101
|
-
|
|
106
|
+
|
|
107
|
+
affected_rows = if raw_connection.total_changes > total_changes_before_query
|
|
108
|
+
raw_connection.changes
|
|
109
|
+
else
|
|
110
|
+
0
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
ActiveRecord::Result.empty(affected_rows: affected_rows)
|
|
102
114
|
else
|
|
103
|
-
|
|
115
|
+
rows = stmt.to_a
|
|
116
|
+
|
|
117
|
+
affected_rows = if raw_connection.total_changes > total_changes_before_query
|
|
118
|
+
raw_connection.changes
|
|
119
|
+
else
|
|
120
|
+
0
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
ActiveRecord::Result.new(stmt.columns, rows, stmt.types.map { |t| type_map.lookup(t) }, affected_rows: affected_rows)
|
|
104
124
|
end
|
|
105
125
|
ensure
|
|
106
|
-
stmt.close
|
|
126
|
+
stmt.close unless prepare
|
|
107
127
|
end
|
|
108
128
|
end
|
|
109
|
-
@last_affected_rows = raw_connection.changes
|
|
110
129
|
verified!
|
|
111
130
|
|
|
131
|
+
notification_payload[:affected_rows] = affected_rows
|
|
112
132
|
notification_payload[:row_count] = result&.length || 0
|
|
113
133
|
result
|
|
114
134
|
end
|
|
115
135
|
|
|
116
136
|
def cast_result(result)
|
|
117
|
-
# Given that SQLite3 doesn't
|
|
118
|
-
#
|
|
137
|
+
# Given that SQLite3 doesn't have a Result type, raw_execute already returns an ActiveRecord::Result
|
|
138
|
+
# so we have nothing to cast here.
|
|
119
139
|
result
|
|
120
140
|
end
|
|
121
141
|
|
|
122
142
|
def affected_rows(result)
|
|
123
|
-
|
|
143
|
+
result.affected_rows
|
|
124
144
|
end
|
|
125
145
|
|
|
126
146
|
def execute_batch(statements, name = nil, **kwargs)
|
|
@@ -135,10 +155,6 @@ module ActiveRecord
|
|
|
135
155
|
def returning_column_values(result)
|
|
136
156
|
result.rows.first
|
|
137
157
|
end
|
|
138
|
-
|
|
139
|
-
def default_insert_value(column)
|
|
140
|
-
column.default
|
|
141
|
-
end
|
|
142
158
|
end
|
|
143
159
|
end
|
|
144
160
|
end
|
|
@@ -50,6 +50,19 @@ module ActiveRecord
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def quote(value) # :nodoc:
|
|
54
|
+
case value
|
|
55
|
+
when Numeric
|
|
56
|
+
if value.finite?
|
|
57
|
+
super
|
|
58
|
+
else
|
|
59
|
+
"'#{value}'"
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
53
66
|
def quote_string(s)
|
|
54
67
|
::SQLite3::Database.quote(s)
|
|
55
68
|
end
|
|
@@ -67,18 +80,10 @@ module ActiveRecord
|
|
|
67
80
|
"x'#{value.hex}'"
|
|
68
81
|
end
|
|
69
82
|
|
|
70
|
-
def quoted_true
|
|
71
|
-
"1"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
83
|
def unquoted_true
|
|
75
84
|
1
|
|
76
85
|
end
|
|
77
86
|
|
|
78
|
-
def quoted_false
|
|
79
|
-
"0"
|
|
80
|
-
end
|
|
81
|
-
|
|
82
87
|
def unquoted_false
|
|
83
88
|
0
|
|
84
89
|
end
|
|
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
|
6
6
|
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
|
|
7
7
|
private
|
|
8
8
|
def virtual_tables(stream)
|
|
9
|
-
virtual_tables = @connection.virtual_tables
|
|
9
|
+
virtual_tables = @connection.virtual_tables.reject { |name, _| ignored?(name) }
|
|
10
10
|
if virtual_tables.any?
|
|
11
11
|
stream.puts
|
|
12
12
|
stream.puts " # Virtual tables defined in this database."
|
|
@@ -19,11 +19,23 @@ module ActiveRecord
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def default_primary_key?(column)
|
|
22
|
-
schema_type(column) == :integer
|
|
22
|
+
schema_type(column) == :integer && primary_key_has_autoincrement?
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def explicit_primary_key_default?(column)
|
|
26
|
-
column.bigint?
|
|
26
|
+
column.bigint? || (column.type == :integer && !primary_key_has_autoincrement?)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def primary_key_has_autoincrement?
|
|
30
|
+
return false unless table_name
|
|
31
|
+
|
|
32
|
+
table_sql = @connection.query_value(<<~SQL, "SCHEMA")
|
|
33
|
+
SELECT sql FROM sqlite_master WHERE name = #{@connection.quote(table_name)} AND type = 'table'
|
|
34
|
+
UNION ALL
|
|
35
|
+
SELECT sql FROM sqlite_temp_master WHERE name = #{@connection.quote(table_name)} AND type = 'table'
|
|
36
|
+
SQL
|
|
37
|
+
|
|
38
|
+
table_sql.to_s.match?(/\bAUTOINCREMENT\b/i)
|
|
27
39
|
end
|
|
28
40
|
|
|
29
41
|
def prepare_column_options(column)
|
|
@@ -21,12 +21,13 @@ module ActiveRecord
|
|
|
21
21
|
WHERE name = #{quote(row['name'])} AND type = 'index'
|
|
22
22
|
SQL
|
|
23
23
|
|
|
24
|
-
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where
|
|
24
|
+
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+?))?(?:\s*\/\*.*\*\/)?\s*\z/im =~ index_sql
|
|
25
25
|
|
|
26
26
|
columns = internal_exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
|
|
27
27
|
col["name"]
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
where = where.sub(/\s*\/\*.*\*\/\z/, "") if where
|
|
30
31
|
orders = {}
|
|
31
32
|
|
|
32
33
|
if columns.any?(&:nil?) # index created with an expression
|
|
@@ -62,22 +63,13 @@ module ActiveRecord
|
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
|
65
|
-
return if options.delete(:if_exists)
|
|
66
|
+
return if options.delete(:if_exists) && !foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
|
66
67
|
|
|
67
68
|
to_table ||= options[:to_table]
|
|
68
69
|
options = options.except(:name, :to_table, :validate)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
fkey = foreign_keys.detect do |fk|
|
|
72
|
-
table = to_table || begin
|
|
73
|
-
table = options[:column].to_s.delete_suffix("_id")
|
|
74
|
-
Base.pluralize_table_names ? table.pluralize : table
|
|
75
|
-
end
|
|
76
|
-
table = strip_table_name_prefix_and_suffix(table)
|
|
77
|
-
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
|
78
|
-
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
|
79
|
-
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
|
70
|
+
fkey = foreign_key_for!(from_table, to_table: to_table, **options)
|
|
80
71
|
|
|
72
|
+
foreign_keys = foreign_keys(from_table)
|
|
81
73
|
foreign_keys.delete(fkey)
|
|
82
74
|
alter_table(from_table, foreign_keys)
|
|
83
75
|
end
|
|
@@ -109,7 +101,7 @@ module ActiveRecord
|
|
|
109
101
|
end
|
|
110
102
|
|
|
111
103
|
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
|
112
|
-
return if if_exists && !check_constraint_exists?(table_name, **options)
|
|
104
|
+
return if if_exists && !check_constraint_exists?(table_name, expression: expression, **options)
|
|
113
105
|
|
|
114
106
|
check_constraints = check_constraints(table_name)
|
|
115
107
|
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
|
@@ -155,6 +147,7 @@ module ActiveRecord
|
|
|
155
147
|
|
|
156
148
|
Column.new(
|
|
157
149
|
field["name"],
|
|
150
|
+
lookup_cast_type(field["type"]),
|
|
158
151
|
default_value,
|
|
159
152
|
type_metadata,
|
|
160
153
|
field["notnull"].to_i == 0,
|
|
@@ -19,14 +19,30 @@ SQLite3::ForkSafety.suppress_warnings!
|
|
|
19
19
|
|
|
20
20
|
module ActiveRecord
|
|
21
21
|
module ConnectionAdapters # :nodoc:
|
|
22
|
-
# = Active Record SQLite3 Adapter
|
|
22
|
+
# = Active Record \SQLite3 Adapter
|
|
23
23
|
#
|
|
24
|
-
# The SQLite3 adapter works with the sqlite3-ruby
|
|
25
|
-
#
|
|
24
|
+
# The \SQLite3 adapter works with the sqlite3[https://sparklemotion.github.io/sqlite3-ruby/]
|
|
25
|
+
# driver.
|
|
26
26
|
#
|
|
27
|
-
# Options
|
|
27
|
+
# ==== Options
|
|
28
|
+
#
|
|
29
|
+
# * +:database+ (String): Filesystem path to the database file.
|
|
30
|
+
# * +:statement_limit+ (Integer): Maximum number of prepared statements to cache per database connection. (default: 1000)
|
|
31
|
+
# * +:timeout+ (Integer): Timeout in milliseconds to use when waiting for a lock. (default: no wait)
|
|
32
|
+
# * +:strict+ (Boolean): Enable or disable strict mode. When enabled, this will
|
|
33
|
+
# {disallow double-quoted string literals in SQL
|
|
34
|
+
# statements}[https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted].
|
|
35
|
+
# (default: see strict_strings_by_default)
|
|
36
|
+
# * +:extensions+ (Array): (<b>requires sqlite3 v2.4.0</b>) Each entry specifies a sqlite extension
|
|
37
|
+
# to load for this database. The entry may be a filesystem path, or the name of a class that
|
|
38
|
+
# responds to +.to_path+ to provide the filesystem path for the extension. See {sqlite3-ruby
|
|
39
|
+
# documentation}[https://sparklemotion.github.io/sqlite3-ruby/SQLite3/Database.html#class-SQLite3::Database-label-SQLite+Extensions]
|
|
40
|
+
# for more information.
|
|
41
|
+
#
|
|
42
|
+
# There may be other options available specific to the SQLite3 driver. Please read the
|
|
43
|
+
# documentation for
|
|
44
|
+
# {SQLite3::Database.new}[https://sparklemotion.github.io/sqlite3-ruby/SQLite3/Database.html#method-c-new]
|
|
28
45
|
#
|
|
29
|
-
# * <tt>:database</tt> - Path to the database file.
|
|
30
46
|
class SQLite3Adapter < AbstractAdapter
|
|
31
47
|
ADAPTER_NAME = "SQLite"
|
|
32
48
|
|
|
@@ -46,10 +62,14 @@ module ActiveRecord
|
|
|
46
62
|
|
|
47
63
|
args << "-#{options[:mode]}" if options[:mode]
|
|
48
64
|
args << "-header" if options[:header]
|
|
49
|
-
args << File.expand_path(config.database, Rails.
|
|
65
|
+
args << File.expand_path(config.database, defined?(Rails.root) ? Rails.root : nil)
|
|
50
66
|
|
|
51
67
|
find_cmd_and_exec(ActiveRecord.database_cli[:sqlite], *args)
|
|
52
68
|
end
|
|
69
|
+
|
|
70
|
+
def native_database_types # :nodoc:
|
|
71
|
+
NATIVE_DATABASE_TYPES
|
|
72
|
+
end
|
|
53
73
|
end
|
|
54
74
|
|
|
55
75
|
include SQLite3::Quoting
|
|
@@ -58,12 +78,19 @@ module ActiveRecord
|
|
|
58
78
|
|
|
59
79
|
##
|
|
60
80
|
# :singleton-method:
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
81
|
+
#
|
|
82
|
+
# Configure the SQLite3Adapter to be used in a "strict strings" mode. When enabled, this will
|
|
83
|
+
# {disallow double-quoted string literals in SQL
|
|
84
|
+
# statements}[https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted],
|
|
85
|
+
# which may prevent some typographical errors like creating an index for a non-existent
|
|
86
|
+
# column. The default is +false+.
|
|
87
|
+
#
|
|
64
88
|
# If you wish to enable this mode you can add the following line to your application.rb file:
|
|
65
89
|
#
|
|
66
90
|
# config.active_record.sqlite3_adapter_strict_strings_by_default = true
|
|
91
|
+
#
|
|
92
|
+
# This can also be configured on individual databases by setting the +strict:+ option.
|
|
93
|
+
#
|
|
67
94
|
class_attribute :strict_strings_by_default, default: false
|
|
68
95
|
|
|
69
96
|
NATIVE_DATABASE_TYPES = {
|
|
@@ -122,13 +149,18 @@ module ActiveRecord
|
|
|
122
149
|
end
|
|
123
150
|
end
|
|
124
151
|
|
|
125
|
-
@last_affected_rows = nil
|
|
126
152
|
@previous_read_uncommitted = nil
|
|
127
153
|
@config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)
|
|
154
|
+
|
|
155
|
+
extensions = @config.fetch(:extensions, []).map do |extension|
|
|
156
|
+
extension.safe_constantize || extension
|
|
157
|
+
end
|
|
158
|
+
|
|
128
159
|
@connection_parameters = @config.merge(
|
|
129
160
|
database: @config[:database].to_s,
|
|
130
161
|
results_as_hash: true,
|
|
131
162
|
default_transaction_mode: :immediate,
|
|
163
|
+
extensions: extensions
|
|
132
164
|
)
|
|
133
165
|
end
|
|
134
166
|
|
|
@@ -153,7 +185,7 @@ module ActiveRecord
|
|
|
153
185
|
end
|
|
154
186
|
|
|
155
187
|
def supports_expression_index?
|
|
156
|
-
|
|
188
|
+
true
|
|
157
189
|
end
|
|
158
190
|
|
|
159
191
|
def requires_reloading?
|
|
@@ -181,7 +213,7 @@ module ActiveRecord
|
|
|
181
213
|
end
|
|
182
214
|
|
|
183
215
|
def supports_common_table_expressions?
|
|
184
|
-
|
|
216
|
+
true
|
|
185
217
|
end
|
|
186
218
|
|
|
187
219
|
def supports_insert_returning?
|
|
@@ -207,7 +239,12 @@ module ActiveRecord
|
|
|
207
239
|
!(@raw_connection.nil? || @raw_connection.closed?)
|
|
208
240
|
end
|
|
209
241
|
|
|
210
|
-
|
|
242
|
+
def active?
|
|
243
|
+
if connected?
|
|
244
|
+
verified!
|
|
245
|
+
true
|
|
246
|
+
end
|
|
247
|
+
end
|
|
211
248
|
|
|
212
249
|
alias :reset! :reconnect!
|
|
213
250
|
|
|
@@ -224,10 +261,6 @@ module ActiveRecord
|
|
|
224
261
|
true
|
|
225
262
|
end
|
|
226
263
|
|
|
227
|
-
def native_database_types # :nodoc:
|
|
228
|
-
NATIVE_DATABASE_TYPES
|
|
229
|
-
end
|
|
230
|
-
|
|
231
264
|
# Returns the current database encoding format as a string, e.g. 'UTF-8'
|
|
232
265
|
def encoding
|
|
233
266
|
any_raw_connection.encoding.to_s
|
|
@@ -286,7 +319,7 @@ module ActiveRecord
|
|
|
286
319
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
|
287
320
|
end
|
|
288
321
|
|
|
289
|
-
VIRTUAL_TABLE_REGEX = /USING\s+(\w+)\s*\((
|
|
322
|
+
VIRTUAL_TABLE_REGEX = /USING\s+(\w+)\s*\((.*)\)/i
|
|
290
323
|
|
|
291
324
|
# Returns a list of defined virtual tables
|
|
292
325
|
def virtual_tables
|
|
@@ -304,7 +337,7 @@ module ActiveRecord
|
|
|
304
337
|
# Creates a virtual table
|
|
305
338
|
#
|
|
306
339
|
# Example:
|
|
307
|
-
# create_virtual_table :emails, :fts5, ['sender', 'title','
|
|
340
|
+
# create_virtual_table :emails, :fts5, ['sender', 'title', 'body']
|
|
308
341
|
def create_virtual_table(table_name, module_name, values)
|
|
309
342
|
exec_query "CREATE VIRTUAL TABLE IF NOT EXISTS #{table_name} USING #{module_name} (#{values.join(", ")})"
|
|
310
343
|
end
|
|
@@ -407,7 +440,7 @@ module ActiveRecord
|
|
|
407
440
|
end
|
|
408
441
|
alias :add_belongs_to :add_reference
|
|
409
442
|
|
|
410
|
-
FK_REGEX = /.*FOREIGN KEY\s+\("(
|
|
443
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
|
411
444
|
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
|
412
445
|
def foreign_keys(table_name)
|
|
413
446
|
# SQLite returns 1 row for each column of composite foreign keys.
|
|
@@ -473,8 +506,8 @@ module ActiveRecord
|
|
|
473
506
|
end
|
|
474
507
|
|
|
475
508
|
def check_version # :nodoc:
|
|
476
|
-
if database_version < "3.
|
|
477
|
-
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.
|
|
509
|
+
if database_version < "3.23.0"
|
|
510
|
+
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.23.0."
|
|
478
511
|
end
|
|
479
512
|
end
|
|
480
513
|
|
|
@@ -518,6 +551,10 @@ module ActiveRecord
|
|
|
518
551
|
case default
|
|
519
552
|
when /^null$/i
|
|
520
553
|
nil
|
|
554
|
+
when /^false$/i
|
|
555
|
+
false
|
|
556
|
+
when /^true$/i
|
|
557
|
+
true
|
|
521
558
|
# Quoted types
|
|
522
559
|
when /^'([^|]*)'$/m
|
|
523
560
|
$1.gsub("''", "'")
|
|
@@ -578,8 +615,8 @@ module ActiveRecord
|
|
|
578
615
|
yield definition if block_given?
|
|
579
616
|
end
|
|
580
617
|
|
|
581
|
-
|
|
582
|
-
|
|
618
|
+
disable_referential_integrity do
|
|
619
|
+
transaction do
|
|
583
620
|
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
|
584
621
|
move_table(altered_table_name, table_name, &caller)
|
|
585
622
|
end
|
|
@@ -620,8 +657,8 @@ module ActiveRecord
|
|
|
620
657
|
column_options[:stored] = column.virtual_stored?
|
|
621
658
|
column_options[:type] = column.type
|
|
622
659
|
elsif column.has_default?
|
|
623
|
-
|
|
624
|
-
default =
|
|
660
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
661
|
+
default = column.fetch_cast_type(self).deserialize(column.default)
|
|
625
662
|
default = -> { column.default_function } if default.nil?
|
|
626
663
|
|
|
627
664
|
unless column.auto_increment?
|
|
@@ -695,6 +732,8 @@ module ActiveRecord
|
|
|
695
732
|
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
696
733
|
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
|
697
734
|
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
735
|
+
elsif exception.message.match?(/CHECK constraint failed: .*/i)
|
|
736
|
+
CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
698
737
|
elsif exception.message.match?(/called on a closed database/i)
|
|
699
738
|
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
700
739
|
elsif exception.is_a?(::SQLite3::BusyException)
|
|
@@ -784,9 +823,9 @@ module ActiveRecord
|
|
|
784
823
|
|
|
785
824
|
def table_info(table_name)
|
|
786
825
|
if supports_virtual_columns?
|
|
787
|
-
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
|
|
826
|
+
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA", allow_retry: true)
|
|
788
827
|
else
|
|
789
|
-
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
|
828
|
+
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA", allow_retry: true)
|
|
790
829
|
end
|
|
791
830
|
end
|
|
792
831
|
|
|
@@ -813,18 +852,10 @@ module ActiveRecord
|
|
|
813
852
|
end
|
|
814
853
|
|
|
815
854
|
def configure_connection
|
|
816
|
-
if @config[:timeout]
|
|
817
|
-
raise ArgumentError, "Cannot specify both timeout and retries arguments"
|
|
818
|
-
elsif @config[:timeout]
|
|
855
|
+
if @config[:timeout]
|
|
819
856
|
timeout = self.class.type_cast_config_to_integer(@config[:timeout])
|
|
820
857
|
raise TypeError, "timeout must be integer, not #{timeout}" unless timeout.is_a?(Integer)
|
|
821
858
|
@raw_connection.busy_handler_timeout = timeout
|
|
822
|
-
elsif @config[:retries]
|
|
823
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
|
824
|
-
The retries option is deprecated and will be removed in Rails 8.1. Use timeout instead.
|
|
825
|
-
MSG
|
|
826
|
-
retries = self.class.type_cast_config_to_integer(@config[:retries])
|
|
827
|
-
raw_connection.busy_handler { |count| count <= retries }
|
|
828
859
|
end
|
|
829
860
|
|
|
830
861
|
super
|
|
@@ -10,6 +10,10 @@ module ActiveRecord
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
private
|
|
13
|
+
def multi_statements_enabled?
|
|
14
|
+
@config[:multi_statement]
|
|
15
|
+
end
|
|
16
|
+
|
|
13
17
|
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
|
14
18
|
reset_multi_statement = if batch && !@config[:multi_statement]
|
|
15
19
|
raw_connection.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
|
|
@@ -29,7 +33,8 @@ module ActiveRecord
|
|
|
29
33
|
raw_connection.next_result
|
|
30
34
|
end
|
|
31
35
|
verified!
|
|
32
|
-
|
|
36
|
+
|
|
37
|
+
notification_payload[:affected_rows] = result.affected_rows
|
|
33
38
|
notification_payload[:row_count] = result.count
|
|
34
39
|
result
|
|
35
40
|
ensure
|
|
@@ -40,9 +45,9 @@ module ActiveRecord
|
|
|
40
45
|
|
|
41
46
|
def cast_result(result)
|
|
42
47
|
if result.fields.empty?
|
|
43
|
-
ActiveRecord::Result.empty
|
|
48
|
+
ActiveRecord::Result.empty(affected_rows: result.affected_rows)
|
|
44
49
|
else
|
|
45
|
-
ActiveRecord::Result.new(result.fields, result.rows)
|
|
50
|
+
ActiveRecord::Result.new(result.fields, result.rows, affected_rows: result.affected_rows)
|
|
46
51
|
end
|
|
47
52
|
end
|
|
48
53
|
|
|
@@ -18,6 +18,20 @@ module ActiveRecord
|
|
|
18
18
|
|
|
19
19
|
include Trilogy::DatabaseStatements
|
|
20
20
|
|
|
21
|
+
# Trilogy spells its SSL options with underscores, and supports certificate
|
|
22
|
+
# revocation lists, which mysql2 does not.
|
|
23
|
+
self.cli_arg_map = cli_arg_map
|
|
24
|
+
.except(:sslca, :sslcert, :sslcapath, :sslcipher, :sslkey)
|
|
25
|
+
.merge(
|
|
26
|
+
ssl_ca: "--ssl-ca",
|
|
27
|
+
ssl_cert: "--ssl-cert",
|
|
28
|
+
ssl_capath: "--ssl-capath",
|
|
29
|
+
ssl_cipher: "--ssl-cipher",
|
|
30
|
+
ssl_key: "--ssl-key",
|
|
31
|
+
ssl_crl: "--ssl-crl",
|
|
32
|
+
ssl_crlpath: "--ssl-crlpath"
|
|
33
|
+
).freeze
|
|
34
|
+
|
|
21
35
|
SSL_MODES = {
|
|
22
36
|
SSL_MODE_DISABLED: ::Trilogy::SSL_DISABLED,
|
|
23
37
|
SSL_MODE_PREFERRED: ::Trilogy::SSL_PREFERRED_NOVERIFY,
|
|
@@ -121,7 +135,7 @@ module ActiveRecord
|
|
|
121
135
|
end
|
|
122
136
|
|
|
123
137
|
def active?
|
|
124
|
-
connected? && @lock.synchronize { @raw_connection&.ping } || false
|
|
138
|
+
connected? && @lock.synchronize { @raw_connection&.ping; verified! } || false
|
|
125
139
|
rescue ::Trilogy::Error
|
|
126
140
|
false
|
|
127
141
|
end
|
|
@@ -181,7 +195,7 @@ module ActiveRecord
|
|
|
181
195
|
end
|
|
182
196
|
|
|
183
197
|
case exception
|
|
184
|
-
when ::Trilogy::ConnectionClosed, ::Trilogy::EOFError
|
|
198
|
+
when ::Trilogy::ConnectionClosed, ::Trilogy::EOFError, ::Trilogy::SSLError
|
|
185
199
|
return ConnectionFailed.new(message, connection_pool: @pool)
|
|
186
200
|
when ::Trilogy::Error
|
|
187
201
|
if exception.is_a?(SystemCallError) || exception.message.include?("TRILOGY_INVALID_SEQUENCE_ID")
|
|
@@ -84,6 +84,7 @@ module ActiveRecord
|
|
|
84
84
|
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
|
|
85
85
|
autoload :IndexDefinition
|
|
86
86
|
autoload :ColumnDefinition
|
|
87
|
+
autoload :ColumnMethods
|
|
87
88
|
autoload :ChangeColumnDefinition
|
|
88
89
|
autoload :ChangeColumnDefaultDefinition
|
|
89
90
|
autoload :ForeignKeyDefinition
|
|
@@ -100,7 +100,8 @@ module ActiveRecord
|
|
|
100
100
|
db_config = resolve_config_for_connection(database_key)
|
|
101
101
|
|
|
102
102
|
self.connection_class = true
|
|
103
|
-
|
|
103
|
+
shard = shard.to_sym unless shard.is_a? Integer
|
|
104
|
+
connections << connection_handler.establish_connection(db_config, owner_name: self, role: role, shard: shard)
|
|
104
105
|
end
|
|
105
106
|
end
|
|
106
107
|
|
|
@@ -172,9 +173,11 @@ module ActiveRecord
|
|
|
172
173
|
prevent_writes = true if role == ActiveRecord.reading_role
|
|
173
174
|
|
|
174
175
|
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
begin
|
|
177
|
+
yield
|
|
178
|
+
ensure
|
|
179
|
+
connected_to_stack.pop
|
|
180
|
+
end
|
|
178
181
|
end
|
|
179
182
|
|
|
180
183
|
# Passes the block to +connected_to+ for every +shard+ the
|
|
@@ -301,7 +304,7 @@ module ActiveRecord
|
|
|
301
304
|
|
|
302
305
|
# Checkouts a connection from the pool, yield it and then check it back in.
|
|
303
306
|
# If a connection was already leased via #lease_connection or a parent call to
|
|
304
|
-
# #with_connection, that same connection is
|
|
307
|
+
# #with_connection, that same connection is yielded.
|
|
305
308
|
# If #lease_connection is called inside the block, the connection won't be checked
|
|
306
309
|
# back in.
|
|
307
310
|
# If #connection is called inside the block, the connection won't be checked back in
|
|
@@ -395,11 +398,13 @@ module ActiveRecord
|
|
|
395
398
|
prevent_writes = true if role == ActiveRecord.reading_role
|
|
396
399
|
|
|
397
400
|
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
401
|
+
begin
|
|
402
|
+
return_value = yield
|
|
403
|
+
return_value.load if return_value.is_a? ActiveRecord::Relation
|
|
404
|
+
return_value
|
|
405
|
+
ensure
|
|
406
|
+
self.connected_to_stack.pop
|
|
407
|
+
end
|
|
403
408
|
end
|
|
404
409
|
|
|
405
410
|
def append_to_connected_to_stack(entry)
|