activerecord 7.1.5.1 → 7.2.0.beta1
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 +515 -2445
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +14 -7
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- 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 +6 -4
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
- data/lib/active_record/associations/join_dependency.rb +5 -5
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- 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 +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +33 -16
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +4 -16
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +60 -71
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +13 -32
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -65
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- 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/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -13
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +53 -37
- data/lib/active_record/counter_cache.rb +18 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +38 -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 +24 -0
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
- data/lib/active_record/encryption/encryptor.rb +17 -2
- 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.rb +0 -2
- data/lib/active_record/enum.rb +10 -1
- data/lib/active_record/errors.rb +16 -11
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +8 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +7 -6
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -4
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +5 -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 +85 -76
- data/lib/active_record/model_schema.rb +28 -68
- data/lib/active_record/nested_attributes.rb +13 -16
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +50 -62
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +90 -35
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -57
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +496 -72
- data/lib/active_record/result.rb +31 -44
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/signed_id.rb +11 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +76 -70
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +81 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +1 -1
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +68 -0
- data/lib/active_record/transactions.rb +43 -14
- 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 +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- 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/nodes/binary.rb +0 -6
- 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 +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- 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/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/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- metadata +20 -15
@@ -13,15 +13,16 @@ module ActiveRecord
|
|
13
13
|
:truncate_tables, :rollback_to_savepoint, :rollback_db_transaction, :restart_db_transaction,
|
14
14
|
:exec_insert_all
|
15
15
|
|
16
|
-
base.set_callback :
|
17
|
-
base.set_callback :checkin, :after, :disable_query_cache!
|
16
|
+
base.set_callback :checkin, :after, :unset_query_cache!
|
18
17
|
end
|
19
18
|
|
20
19
|
def dirties_query_cache(base, *method_names)
|
21
20
|
method_names.each do |method_name|
|
22
21
|
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
|
23
22
|
def #{method_name}(...)
|
24
|
-
|
23
|
+
if pool.dirties_query_cache
|
24
|
+
ActiveRecord::Base.clear_query_caches_for_current_thread
|
25
|
+
end
|
25
26
|
super
|
26
27
|
end
|
27
28
|
end_code
|
@@ -29,60 +30,168 @@ module ActiveRecord
|
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
|
-
|
33
|
-
|
33
|
+
class Store # :nodoc:
|
34
|
+
attr_accessor :enabled, :dirties
|
35
|
+
alias_method :enabled?, :enabled
|
36
|
+
alias_method :dirties?, :dirties
|
37
|
+
|
38
|
+
def initialize(max_size)
|
39
|
+
@map = {}
|
40
|
+
@max_size = max_size
|
41
|
+
@enabled = false
|
42
|
+
@dirties = true
|
43
|
+
end
|
44
|
+
|
45
|
+
def size
|
46
|
+
@map.size
|
47
|
+
end
|
48
|
+
|
49
|
+
def empty?
|
50
|
+
@map.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
def [](key)
|
54
|
+
return unless @enabled
|
55
|
+
|
56
|
+
if entry = @map.delete(key)
|
57
|
+
@map[key] = entry
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def compute_if_absent(key)
|
62
|
+
return yield unless @enabled
|
63
|
+
|
64
|
+
if entry = @map.delete(key)
|
65
|
+
return @map[key] = entry
|
66
|
+
end
|
67
|
+
|
68
|
+
if @max_size && @map.size >= @max_size
|
69
|
+
@map.shift # evict the oldest entry
|
70
|
+
end
|
71
|
+
|
72
|
+
@map[key] ||= yield
|
73
|
+
end
|
74
|
+
|
75
|
+
def clear
|
76
|
+
@map.clear
|
77
|
+
self
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module ConnectionPoolConfiguration # :nodoc:
|
82
|
+
def initialize(...)
|
83
|
+
super
|
84
|
+
@thread_query_caches = Concurrent::Map.new(initial_capacity: @size)
|
85
|
+
@query_cache_max_size = \
|
86
|
+
case query_cache = db_config&.query_cache
|
87
|
+
when 0, false
|
88
|
+
nil
|
89
|
+
when Integer
|
90
|
+
query_cache
|
91
|
+
when nil
|
92
|
+
DEFAULT_SIZE
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def checkout_and_verify(connection)
|
34
97
|
super
|
35
|
-
|
98
|
+
connection.query_cache ||= query_cache
|
99
|
+
connection
|
100
|
+
end
|
101
|
+
|
102
|
+
# Disable the query cache within the block.
|
103
|
+
def disable_query_cache(dirties: true)
|
104
|
+
cache = query_cache
|
105
|
+
old_enabled, cache.enabled, old_dirties, cache.dirties = cache.enabled, false, cache.dirties, dirties
|
106
|
+
begin
|
107
|
+
yield
|
108
|
+
ensure
|
109
|
+
cache.enabled, cache.dirties = old_enabled, old_dirties
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def enable_query_cache
|
114
|
+
cache = query_cache
|
115
|
+
old_enabled, cache.enabled, old_dirties, cache.dirties = cache.enabled, true, cache.dirties, true
|
116
|
+
begin
|
117
|
+
yield
|
118
|
+
ensure
|
119
|
+
cache.enabled, cache.dirties = old_enabled, old_dirties
|
120
|
+
end
|
36
121
|
end
|
37
122
|
|
38
123
|
def enable_query_cache!
|
39
|
-
|
40
|
-
connection.enable_query_cache! if active_connection?
|
124
|
+
query_cache.enabled, query_cache.dirties = true, true
|
41
125
|
end
|
42
126
|
|
43
127
|
def disable_query_cache!
|
44
|
-
|
45
|
-
connection.disable_query_cache! if active_connection?
|
128
|
+
query_cache.enabled, query_cache.dirties = false, true
|
46
129
|
end
|
47
130
|
|
48
131
|
def query_cache_enabled
|
49
|
-
|
132
|
+
query_cache.enabled
|
133
|
+
end
|
134
|
+
|
135
|
+
def dirties_query_cache
|
136
|
+
query_cache.dirties
|
50
137
|
end
|
138
|
+
|
139
|
+
def clear_query_cache
|
140
|
+
if @pinned_connection
|
141
|
+
# With transactional fixtures, and especially systems test
|
142
|
+
# another thread may use the same connection, but with a different
|
143
|
+
# query cache. So we must clear them all.
|
144
|
+
@thread_query_caches.each_value(&:clear)
|
145
|
+
else
|
146
|
+
query_cache.clear
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def query_cache
|
151
|
+
@thread_query_caches.compute_if_absent(ActiveSupport::IsolatedExecutionState.context) do
|
152
|
+
Store.new(@query_cache_max_size)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
def prune_thread_cache
|
158
|
+
dead_threads = @thread_query_caches.keys.reject(&:alive?)
|
159
|
+
dead_threads.each do |dead_thread|
|
160
|
+
@thread_query_caches.delete(dead_thread)
|
161
|
+
end
|
162
|
+
end
|
51
163
|
end
|
52
164
|
|
53
|
-
|
165
|
+
attr_accessor :query_cache
|
54
166
|
|
55
167
|
def initialize(*)
|
56
168
|
super
|
57
|
-
@query_cache
|
58
|
-
|
59
|
-
|
169
|
+
@query_cache = nil
|
170
|
+
end
|
171
|
+
|
172
|
+
def query_cache_enabled
|
173
|
+
@query_cache&.enabled?
|
60
174
|
end
|
61
175
|
|
62
176
|
# Enable the query cache within the block.
|
63
|
-
def cache
|
64
|
-
|
65
|
-
yield
|
66
|
-
ensure
|
67
|
-
@query_cache_enabled = old
|
68
|
-
clear_query_cache unless @query_cache_enabled
|
177
|
+
def cache(&block)
|
178
|
+
pool.enable_query_cache(&block)
|
69
179
|
end
|
70
180
|
|
71
181
|
def enable_query_cache!
|
72
|
-
|
182
|
+
pool.enable_query_cache!
|
73
183
|
end
|
74
184
|
|
75
|
-
|
76
|
-
|
77
|
-
|
185
|
+
# Disable the query cache within the block.
|
186
|
+
#
|
187
|
+
# Set <tt>dirties: false</tt> to prevent query caches on all connections from being cleared by write operations.
|
188
|
+
# (By default, write operations dirty all connections' query caches in case they are replicas whose cache would now be outdated.)
|
189
|
+
def uncached(dirties: true, &block)
|
190
|
+
pool.disable_query_cache(dirties: dirties, &block)
|
78
191
|
end
|
79
192
|
|
80
|
-
|
81
|
-
|
82
|
-
old, @query_cache_enabled = @query_cache_enabled, false
|
83
|
-
yield
|
84
|
-
ensure
|
85
|
-
@query_cache_enabled = old
|
193
|
+
def disable_query_cache!
|
194
|
+
pool.disable_query_cache!
|
86
195
|
end
|
87
196
|
|
88
197
|
# Clears the query cache.
|
@@ -92,24 +201,22 @@ module ActiveRecord
|
|
92
201
|
# the same SQL query and repeatedly return the same result each time, silently
|
93
202
|
# undermining the randomness you were expecting.
|
94
203
|
def clear_query_cache
|
95
|
-
|
96
|
-
@query_cache.clear
|
97
|
-
end
|
204
|
+
pool.clear_query_cache
|
98
205
|
end
|
99
206
|
|
100
|
-
def select_all(arel, name = nil, binds = [], preparable: nil, async: false) # :nodoc:
|
207
|
+
def select_all(arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc:
|
101
208
|
arel = arel_from_relation(arel)
|
102
209
|
|
103
210
|
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch.
|
104
211
|
# Such queries should not be cached.
|
105
|
-
if @
|
106
|
-
sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
|
212
|
+
if @query_cache&.enabled? && !(arel.respond_to?(:locked) && arel.locked)
|
213
|
+
sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable)
|
107
214
|
|
108
215
|
if async
|
109
|
-
result = lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async)
|
216
|
+
result = lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async, allow_retry: allow_retry)
|
110
217
|
FutureResult.wrap(result)
|
111
218
|
else
|
112
|
-
cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable, async: async) }
|
219
|
+
cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable, async: async, allow_retry: allow_retry) }
|
113
220
|
end
|
114
221
|
else
|
115
222
|
super
|
@@ -117,42 +224,37 @@ module ActiveRecord
|
|
117
224
|
end
|
118
225
|
|
119
226
|
private
|
227
|
+
def unset_query_cache!
|
228
|
+
@query_cache = nil
|
229
|
+
end
|
230
|
+
|
120
231
|
def lookup_sql_cache(sql, name, binds)
|
121
232
|
key = binds.empty? ? sql : [sql, binds]
|
122
|
-
hit = false
|
123
|
-
result = nil
|
124
233
|
|
234
|
+
result = nil
|
125
235
|
@lock.synchronize do
|
126
|
-
|
127
|
-
hit = true
|
128
|
-
@query_cache[key] = result
|
129
|
-
end
|
236
|
+
result = @query_cache[key]
|
130
237
|
end
|
131
238
|
|
132
|
-
if
|
239
|
+
if result
|
133
240
|
ActiveSupport::Notifications.instrument(
|
134
241
|
"sql.active_record",
|
135
242
|
cache_notification_info(sql, name, binds)
|
136
243
|
)
|
137
|
-
|
138
|
-
result
|
139
244
|
end
|
245
|
+
|
246
|
+
result
|
140
247
|
end
|
141
248
|
|
142
249
|
def cache_sql(sql, name, binds)
|
143
250
|
key = binds.empty? ? sql : [sql, binds]
|
144
251
|
result = nil
|
145
|
-
hit =
|
252
|
+
hit = true
|
146
253
|
|
147
254
|
@lock.synchronize do
|
148
|
-
|
149
|
-
hit =
|
150
|
-
|
151
|
-
else
|
152
|
-
result = @query_cache[key] = yield
|
153
|
-
if @query_cache_max_size && @query_cache.size > @query_cache_max_size
|
154
|
-
@query_cache.shift
|
155
|
-
end
|
255
|
+
result = @query_cache.compute_if_absent(key) do
|
256
|
+
hit = false
|
257
|
+
yield
|
156
258
|
end
|
157
259
|
end
|
158
260
|
|
@@ -178,23 +280,6 @@ module ActiveRecord
|
|
178
280
|
cached: true
|
179
281
|
}
|
180
282
|
end
|
181
|
-
|
182
|
-
def configure_query_cache!
|
183
|
-
case query_cache = pool.db_config.query_cache
|
184
|
-
when 0, false
|
185
|
-
return
|
186
|
-
when Integer
|
187
|
-
@query_cache_max_size = query_cache
|
188
|
-
when nil
|
189
|
-
@query_cache_max_size = DEFAULT_SIZE
|
190
|
-
else
|
191
|
-
@query_cache_max_size = nil # no limit
|
192
|
-
end
|
193
|
-
|
194
|
-
if pool.query_cache_enabled
|
195
|
-
enable_query_cache!
|
196
|
-
end
|
197
|
-
end
|
198
283
|
end
|
199
284
|
end
|
200
285
|
end
|
@@ -7,6 +7,67 @@ module ActiveRecord
|
|
7
7
|
module ConnectionAdapters # :nodoc:
|
8
8
|
# = Active Record Connection Adapters \Quoting
|
9
9
|
module Quoting
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
module ClassMethods # :nodoc:
|
13
|
+
# Regexp for column names (with or without a table name prefix).
|
14
|
+
# Matches the following:
|
15
|
+
#
|
16
|
+
# "#{table_name}.#{column_name}"
|
17
|
+
# "#{column_name}"
|
18
|
+
def column_name_matcher
|
19
|
+
/
|
20
|
+
\A
|
21
|
+
(
|
22
|
+
(?:
|
23
|
+
# table_name.column_name | function(one or no argument)
|
24
|
+
((?:\w+\.)?\w+ | \w+\((?:|\g<2>)\))
|
25
|
+
)
|
26
|
+
(?:(?:\s+AS)?\s+\w+)?
|
27
|
+
)
|
28
|
+
(?:\s*,\s*\g<1>)*
|
29
|
+
\z
|
30
|
+
/ix
|
31
|
+
end
|
32
|
+
|
33
|
+
# Regexp for column names with order (with or without a table name prefix,
|
34
|
+
# with or without various order modifiers). Matches the following:
|
35
|
+
#
|
36
|
+
# "#{table_name}.#{column_name}"
|
37
|
+
# "#{table_name}.#{column_name} #{direction}"
|
38
|
+
# "#{table_name}.#{column_name} #{direction} NULLS FIRST"
|
39
|
+
# "#{table_name}.#{column_name} NULLS LAST"
|
40
|
+
# "#{column_name}"
|
41
|
+
# "#{column_name} #{direction}"
|
42
|
+
# "#{column_name} #{direction} NULLS FIRST"
|
43
|
+
# "#{column_name} NULLS LAST"
|
44
|
+
def column_name_with_order_matcher
|
45
|
+
/
|
46
|
+
\A
|
47
|
+
(
|
48
|
+
(?:
|
49
|
+
# table_name.column_name | function(one or no argument)
|
50
|
+
((?:\w+\.)?\w+ | \w+\((?:|\g<2>)\))
|
51
|
+
)
|
52
|
+
(?:\s+ASC|\s+DESC)?
|
53
|
+
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
54
|
+
)
|
55
|
+
(?:\s*,\s*\g<1>)*
|
56
|
+
\z
|
57
|
+
/ix
|
58
|
+
end
|
59
|
+
|
60
|
+
# Quotes the column name. Must be implemented by subclasses
|
61
|
+
def quote_column_name(column_name)
|
62
|
+
raise NotImplementedError
|
63
|
+
end
|
64
|
+
|
65
|
+
# Quotes the table name. Defaults to column name quoting.
|
66
|
+
def quote_table_name(table_name)
|
67
|
+
quote_column_name(table_name)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
10
71
|
# Quotes the column value to help prevent
|
11
72
|
# {SQL injection attacks}[https://en.wikipedia.org/wiki/SQL_injection].
|
12
73
|
def quote(value)
|
@@ -23,9 +84,6 @@ module ActiveRecord
|
|
23
84
|
when Type::Time::Value then "'#{quoted_time(value)}'"
|
24
85
|
when Date, Time then "'#{quoted_date(value)}'"
|
25
86
|
when Class then "'#{value}'"
|
26
|
-
when ActiveSupport::Duration
|
27
|
-
warn_quote_duration_deprecated
|
28
|
-
value.to_s
|
29
87
|
else raise TypeError, "can't quote #{value.class.name}"
|
30
88
|
end
|
31
89
|
end
|
@@ -48,21 +106,6 @@ module ActiveRecord
|
|
48
106
|
end
|
49
107
|
end
|
50
108
|
|
51
|
-
# Quote a value to be used as a bound parameter of unknown type. For example,
|
52
|
-
# MySQL might perform dangerous castings when comparing a string to a number,
|
53
|
-
# so this method will cast numbers to string.
|
54
|
-
#
|
55
|
-
# Deprecated: Consider `Arel.sql("... ? ...", value)` or
|
56
|
-
# +sanitize_sql+ instead.
|
57
|
-
def quote_bound_value(value)
|
58
|
-
ActiveRecord.deprecator.warn(<<~MSG.squish)
|
59
|
-
#quote_bound_value is deprecated and will be removed in Rails 7.2.
|
60
|
-
Consider Arel.sql(".. ? ..", value) or #sanitize_sql instead.
|
61
|
-
MSG
|
62
|
-
|
63
|
-
quote(cast_bound_value(value))
|
64
|
-
end
|
65
|
-
|
66
109
|
# Cast a value to be used as a bound parameter of unknown type. For example,
|
67
110
|
# MySQL might perform dangerous castings when comparing a string to a number,
|
68
111
|
# so this method will cast numbers to string.
|
@@ -89,14 +132,14 @@ module ActiveRecord
|
|
89
132
|
s.gsub("\\", '\&\&').gsub("'", "''") # ' (for ruby-mode)
|
90
133
|
end
|
91
134
|
|
92
|
-
# Quotes the column name.
|
135
|
+
# Quotes the column name.
|
93
136
|
def quote_column_name(column_name)
|
94
|
-
column_name
|
137
|
+
self.class.quote_column_name(column_name)
|
95
138
|
end
|
96
139
|
|
97
|
-
# Quotes the table name.
|
140
|
+
# Quotes the table name.
|
98
141
|
def quote_table_name(table_name)
|
99
|
-
|
142
|
+
self.class.quote_table_name(table_name)
|
100
143
|
end
|
101
144
|
|
102
145
|
# Override to return the quoted table name for assignment. Defaults to
|
@@ -177,59 +220,6 @@ module ActiveRecord
|
|
177
220
|
comment
|
178
221
|
end
|
179
222
|
|
180
|
-
def column_name_matcher # :nodoc:
|
181
|
-
COLUMN_NAME
|
182
|
-
end
|
183
|
-
|
184
|
-
def column_name_with_order_matcher # :nodoc:
|
185
|
-
COLUMN_NAME_WITH_ORDER
|
186
|
-
end
|
187
|
-
|
188
|
-
# Regexp for column names (with or without a table name prefix).
|
189
|
-
# Matches the following:
|
190
|
-
#
|
191
|
-
# "#{table_name}.#{column_name}"
|
192
|
-
# "#{column_name}"
|
193
|
-
COLUMN_NAME = /
|
194
|
-
\A
|
195
|
-
(
|
196
|
-
(?:
|
197
|
-
# table_name.column_name | function(one or no argument)
|
198
|
-
((?:\w+\.)?\w+ | \w+\((?:|\g<2>)\))
|
199
|
-
)
|
200
|
-
(?:(?:\s+AS)?\s+\w+)?
|
201
|
-
)
|
202
|
-
(?:\s*,\s*\g<1>)*
|
203
|
-
\z
|
204
|
-
/ix
|
205
|
-
|
206
|
-
# Regexp for column names with order (with or without a table name prefix,
|
207
|
-
# with or without various order modifiers). Matches the following:
|
208
|
-
#
|
209
|
-
# "#{table_name}.#{column_name}"
|
210
|
-
# "#{table_name}.#{column_name} #{direction}"
|
211
|
-
# "#{table_name}.#{column_name} #{direction} NULLS FIRST"
|
212
|
-
# "#{table_name}.#{column_name} NULLS LAST"
|
213
|
-
# "#{column_name}"
|
214
|
-
# "#{column_name} #{direction}"
|
215
|
-
# "#{column_name} #{direction} NULLS FIRST"
|
216
|
-
# "#{column_name} NULLS LAST"
|
217
|
-
COLUMN_NAME_WITH_ORDER = /
|
218
|
-
\A
|
219
|
-
(
|
220
|
-
(?:
|
221
|
-
# table_name.column_name | function(one or no argument)
|
222
|
-
((?:\w+\.)?\w+ | \w+\((?:|\g<2>)\))
|
223
|
-
)
|
224
|
-
(?:\s+ASC|\s+DESC)?
|
225
|
-
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
226
|
-
)
|
227
|
-
(?:\s*,\s*\g<1>)*
|
228
|
-
\z
|
229
|
-
/ix
|
230
|
-
|
231
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
232
|
-
|
233
223
|
private
|
234
224
|
def type_casted_binds(binds)
|
235
225
|
binds.map do |value|
|
@@ -244,22 +234,6 @@ module ActiveRecord
|
|
244
234
|
def lookup_cast_type(sql_type)
|
245
235
|
type_map.lookup(sql_type)
|
246
236
|
end
|
247
|
-
|
248
|
-
def warn_quote_duration_deprecated
|
249
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
250
|
-
Using ActiveSupport::Duration as an interpolated bind parameter in a SQL
|
251
|
-
string template is deprecated. To avoid this warning, you should explicitly
|
252
|
-
convert the duration to a more specific database type. For example, if you
|
253
|
-
want to use a duration as an integer number of seconds:
|
254
|
-
```
|
255
|
-
Record.where("duration = ?", 1.hour.to_i)
|
256
|
-
```
|
257
|
-
If you want to use a duration as an ISO 8601 string:
|
258
|
-
```
|
259
|
-
Record.where("duration = ?", 1.hour.iso8601)
|
260
|
-
```
|
261
|
-
MSG
|
262
|
-
end
|
263
237
|
end
|
264
238
|
end
|
265
239
|
end
|
@@ -348,7 +348,7 @@ module ActiveRecord
|
|
348
348
|
# Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
|
349
349
|
# is actually of this type:
|
350
350
|
#
|
351
|
-
# class SomeMigration < ActiveRecord::Migration[7.
|
351
|
+
# class SomeMigration < ActiveRecord::Migration[7.2]
|
352
352
|
# def up
|
353
353
|
# create_table :foo do |t|
|
354
354
|
# puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
|
@@ -293,6 +293,11 @@ module ActiveRecord
|
|
293
293
|
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
|
294
294
|
validate_create_table_options!(options)
|
295
295
|
validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
|
296
|
+
|
297
|
+
if force && options.key?(:if_not_exists)
|
298
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
299
|
+
end
|
300
|
+
|
296
301
|
td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
|
297
302
|
|
298
303
|
if force
|
@@ -876,9 +881,12 @@ module ActiveRecord
|
|
876
881
|
# ====== Creating an index with a specific algorithm
|
877
882
|
#
|
878
883
|
# add_index(:developers, :name, algorithm: :concurrently)
|
879
|
-
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
|
884
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL
|
880
885
|
#
|
881
|
-
#
|
886
|
+
# add_index(:developers, :name, algorithm: :inplace)
|
887
|
+
# # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
|
888
|
+
#
|
889
|
+
# Note: only supported by PostgreSQL and MySQL.
|
882
890
|
#
|
883
891
|
# Concurrently adding an index is not supported in a transaction.
|
884
892
|
#
|
@@ -1318,7 +1326,7 @@ module ActiveRecord
|
|
1318
1326
|
end
|
1319
1327
|
|
1320
1328
|
def dump_schema_information # :nodoc:
|
1321
|
-
versions = schema_migration.versions
|
1329
|
+
versions = pool.schema_migration.versions
|
1322
1330
|
insert_versions_sql(versions) if versions.any?
|
1323
1331
|
end
|
1324
1332
|
|
@@ -1328,8 +1336,9 @@ module ActiveRecord
|
|
1328
1336
|
|
1329
1337
|
def assume_migrated_upto_version(version)
|
1330
1338
|
version = version.to_i
|
1331
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
1339
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
1332
1340
|
|
1341
|
+
migration_context = pool.migration_context
|
1333
1342
|
migrated = migration_context.get_all_versions
|
1334
1343
|
versions = migration_context.migrations.map(&:version)
|
1335
1344
|
|
@@ -1839,7 +1848,7 @@ module ActiveRecord
|
|
1839
1848
|
end
|
1840
1849
|
|
1841
1850
|
def insert_versions_sql(versions)
|
1842
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
1851
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
1843
1852
|
|
1844
1853
|
if versions.is_a?(Array)
|
1845
1854
|
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|