activerecord 7.0.8.7 → 7.2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +631 -1944
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +26 -14
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -33
- data/lib/active_record/attributes.rb +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +11 -25
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +96 -104
- data/lib/active_record/core.rb +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +39 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +129 -28
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +234 -117
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +129 -85
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +267 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +250 -93
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +28 -16
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +576 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +580 -90
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +63 -14
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +27 -6
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +106 -24
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +54 -12
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -1,24 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "concurrent/map"
|
4
|
+
require "concurrent/atomic/atomic_fixnum"
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
module ConnectionAdapters # :nodoc:
|
7
8
|
module QueryCache
|
9
|
+
DEFAULT_SIZE = 100 # :nodoc:
|
10
|
+
|
8
11
|
class << self
|
9
12
|
def included(base) # :nodoc:
|
10
|
-
dirties_query_cache base, :create, :insert, :update, :delete, :truncate,
|
11
|
-
:rollback_to_savepoint, :rollback_db_transaction, :
|
13
|
+
dirties_query_cache base, :exec_query, :execute, :create, :insert, :update, :delete, :truncate,
|
14
|
+
:truncate_tables, :rollback_to_savepoint, :rollback_db_transaction, :restart_db_transaction,
|
15
|
+
:exec_insert_all
|
12
16
|
|
13
|
-
base.set_callback :
|
14
|
-
base.set_callback :checkin, :after, :disable_query_cache!
|
17
|
+
base.set_callback :checkin, :after, :unset_query_cache!
|
15
18
|
end
|
16
19
|
|
17
20
|
def dirties_query_cache(base, *method_names)
|
18
21
|
method_names.each do |method_name|
|
19
22
|
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
|
20
|
-
def #{method_name}(
|
21
|
-
|
23
|
+
def #{method_name}(...)
|
24
|
+
if pool.dirties_query_cache
|
25
|
+
ActiveRecord::Base.clear_query_caches_for_current_thread
|
26
|
+
end
|
22
27
|
super
|
23
28
|
end
|
24
29
|
end_code
|
@@ -26,59 +31,196 @@ module ActiveRecord
|
|
26
31
|
end
|
27
32
|
end
|
28
33
|
|
29
|
-
|
30
|
-
|
34
|
+
class Store # :nodoc:
|
35
|
+
attr_accessor :enabled, :dirties
|
36
|
+
alias_method :enabled?, :enabled
|
37
|
+
alias_method :dirties?, :dirties
|
38
|
+
|
39
|
+
def initialize(version, max_size)
|
40
|
+
@version = version
|
41
|
+
@current_version = version.value
|
42
|
+
@map = {}
|
43
|
+
@max_size = max_size
|
44
|
+
@enabled = false
|
45
|
+
@dirties = true
|
46
|
+
end
|
47
|
+
|
48
|
+
def size
|
49
|
+
check_version
|
50
|
+
@map.size
|
51
|
+
end
|
52
|
+
|
53
|
+
def empty?
|
54
|
+
check_version
|
55
|
+
@map.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
def [](key)
|
59
|
+
check_version
|
60
|
+
return unless @enabled
|
61
|
+
|
62
|
+
if entry = @map.delete(key)
|
63
|
+
@map[key] = entry
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def compute_if_absent(key)
|
68
|
+
check_version
|
69
|
+
|
70
|
+
return yield unless @enabled
|
71
|
+
|
72
|
+
if entry = @map.delete(key)
|
73
|
+
return @map[key] = entry
|
74
|
+
end
|
75
|
+
|
76
|
+
if @max_size && @map.size >= @max_size
|
77
|
+
@map.shift # evict the oldest entry
|
78
|
+
end
|
79
|
+
|
80
|
+
@map[key] ||= yield
|
81
|
+
end
|
82
|
+
|
83
|
+
def clear
|
84
|
+
@map.clear
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def check_version
|
90
|
+
if @current_version != @version.value
|
91
|
+
@map.clear
|
92
|
+
@current_version = @version.value
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class QueryCacheRegistry # :nodoc:
|
98
|
+
def initialize
|
99
|
+
@mutex = Mutex.new
|
100
|
+
@map = ConnectionPool::WeakThreadKeyMap.new
|
101
|
+
end
|
102
|
+
|
103
|
+
def compute_if_absent(context)
|
104
|
+
@map[context] || @mutex.synchronize do
|
105
|
+
@map[context] ||= yield
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def clear
|
110
|
+
@map.synchronize do
|
111
|
+
@map.clear
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
module ConnectionPoolConfiguration # :nodoc:
|
117
|
+
def initialize(...)
|
118
|
+
super
|
119
|
+
@query_cache_version = Concurrent::AtomicFixnum.new
|
120
|
+
@thread_query_caches = QueryCacheRegistry.new
|
121
|
+
@query_cache_max_size = \
|
122
|
+
case query_cache = db_config&.query_cache
|
123
|
+
when 0, false
|
124
|
+
nil
|
125
|
+
when Integer
|
126
|
+
query_cache
|
127
|
+
when nil
|
128
|
+
DEFAULT_SIZE
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def checkout_and_verify(connection)
|
31
133
|
super
|
32
|
-
|
134
|
+
connection.query_cache ||= query_cache
|
135
|
+
connection
|
136
|
+
end
|
137
|
+
|
138
|
+
# Disable the query cache within the block.
|
139
|
+
def disable_query_cache(dirties: true)
|
140
|
+
cache = query_cache
|
141
|
+
old_enabled, cache.enabled, old_dirties, cache.dirties = cache.enabled, false, cache.dirties, dirties
|
142
|
+
begin
|
143
|
+
yield
|
144
|
+
ensure
|
145
|
+
cache.enabled, cache.dirties = old_enabled, old_dirties
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def enable_query_cache
|
150
|
+
cache = query_cache
|
151
|
+
old_enabled, cache.enabled, old_dirties, cache.dirties = cache.enabled, true, cache.dirties, true
|
152
|
+
begin
|
153
|
+
yield
|
154
|
+
ensure
|
155
|
+
cache.enabled, cache.dirties = old_enabled, old_dirties
|
156
|
+
end
|
33
157
|
end
|
34
158
|
|
35
159
|
def enable_query_cache!
|
36
|
-
|
37
|
-
|
160
|
+
query_cache.enabled = true
|
161
|
+
query_cache.dirties = true
|
38
162
|
end
|
39
163
|
|
40
164
|
def disable_query_cache!
|
41
|
-
|
42
|
-
|
165
|
+
query_cache.enabled = false
|
166
|
+
query_cache.dirties = true
|
43
167
|
end
|
44
168
|
|
45
169
|
def query_cache_enabled
|
46
|
-
|
170
|
+
query_cache.enabled
|
171
|
+
end
|
172
|
+
|
173
|
+
def dirties_query_cache
|
174
|
+
query_cache.dirties
|
175
|
+
end
|
176
|
+
|
177
|
+
def clear_query_cache
|
178
|
+
if @pinned_connection
|
179
|
+
# With transactional fixtures, and especially systems test
|
180
|
+
# another thread may use the same connection, but with a different
|
181
|
+
# query cache. So we must clear them all.
|
182
|
+
@query_cache_version.increment
|
183
|
+
end
|
184
|
+
query_cache.clear
|
185
|
+
end
|
186
|
+
|
187
|
+
def query_cache
|
188
|
+
@thread_query_caches.compute_if_absent(ActiveSupport::IsolatedExecutionState.context) do
|
189
|
+
Store.new(@query_cache_version, @query_cache_max_size)
|
190
|
+
end
|
47
191
|
end
|
48
192
|
end
|
49
193
|
|
50
|
-
|
194
|
+
attr_accessor :query_cache
|
51
195
|
|
52
196
|
def initialize(*)
|
53
197
|
super
|
54
|
-
@query_cache
|
55
|
-
|
198
|
+
@query_cache = nil
|
199
|
+
end
|
200
|
+
|
201
|
+
def query_cache_enabled
|
202
|
+
@query_cache&.enabled?
|
56
203
|
end
|
57
204
|
|
58
205
|
# Enable the query cache within the block.
|
59
|
-
def cache
|
60
|
-
|
61
|
-
yield
|
62
|
-
ensure
|
63
|
-
@query_cache_enabled = old
|
64
|
-
clear_query_cache unless @query_cache_enabled
|
206
|
+
def cache(&block)
|
207
|
+
pool.enable_query_cache(&block)
|
65
208
|
end
|
66
209
|
|
67
210
|
def enable_query_cache!
|
68
|
-
|
211
|
+
pool.enable_query_cache!
|
69
212
|
end
|
70
213
|
|
71
|
-
|
72
|
-
|
73
|
-
|
214
|
+
# Disable the query cache within the block.
|
215
|
+
#
|
216
|
+
# Set <tt>dirties: false</tt> to prevent query caches on all connections from being cleared by write operations.
|
217
|
+
# (By default, write operations dirty all connections' query caches in case they are replicas whose cache would now be outdated.)
|
218
|
+
def uncached(dirties: true, &block)
|
219
|
+
pool.disable_query_cache(dirties: dirties, &block)
|
74
220
|
end
|
75
221
|
|
76
|
-
|
77
|
-
|
78
|
-
old, @query_cache_enabled = @query_cache_enabled, false
|
79
|
-
yield
|
80
|
-
ensure
|
81
|
-
@query_cache_enabled = old
|
222
|
+
def disable_query_cache!
|
223
|
+
pool.disable_query_cache!
|
82
224
|
end
|
83
225
|
|
84
226
|
# Clears the query cache.
|
@@ -88,23 +230,22 @@ module ActiveRecord
|
|
88
230
|
# the same SQL query and repeatedly return the same result each time, silently
|
89
231
|
# undermining the randomness you were expecting.
|
90
232
|
def clear_query_cache
|
91
|
-
|
92
|
-
@query_cache.clear
|
93
|
-
end
|
233
|
+
pool.clear_query_cache
|
94
234
|
end
|
95
235
|
|
96
|
-
def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
|
236
|
+
def select_all(arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc:
|
97
237
|
arel = arel_from_relation(arel)
|
98
238
|
|
99
239
|
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch.
|
100
240
|
# Such queries should not be cached.
|
101
|
-
if @
|
102
|
-
sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
|
241
|
+
if @query_cache&.enabled? && !(arel.respond_to?(:locked) && arel.locked)
|
242
|
+
sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable)
|
103
243
|
|
104
244
|
if async
|
105
|
-
lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async)
|
245
|
+
result = lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async, allow_retry: allow_retry)
|
246
|
+
FutureResult.wrap(result)
|
106
247
|
else
|
107
|
-
cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable, async: async) }
|
248
|
+
cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable, async: async, allow_retry: allow_retry) }
|
108
249
|
end
|
109
250
|
else
|
110
251
|
super
|
@@ -112,32 +253,48 @@ module ActiveRecord
|
|
112
253
|
end
|
113
254
|
|
114
255
|
private
|
256
|
+
def unset_query_cache!
|
257
|
+
@query_cache = nil
|
258
|
+
end
|
259
|
+
|
115
260
|
def lookup_sql_cache(sql, name, binds)
|
261
|
+
key = binds.empty? ? sql : [sql, binds]
|
262
|
+
|
263
|
+
result = nil
|
116
264
|
@lock.synchronize do
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
265
|
+
result = @query_cache[key]
|
266
|
+
end
|
267
|
+
|
268
|
+
if result
|
269
|
+
ActiveSupport::Notifications.instrument(
|
270
|
+
"sql.active_record",
|
271
|
+
cache_notification_info(sql, name, binds)
|
272
|
+
)
|
124
273
|
end
|
274
|
+
|
275
|
+
result
|
125
276
|
end
|
126
277
|
|
127
278
|
def cache_sql(sql, name, binds)
|
279
|
+
key = binds.empty? ? sql : [sql, binds]
|
280
|
+
result = nil
|
281
|
+
hit = true
|
282
|
+
|
128
283
|
@lock.synchronize do
|
129
|
-
result =
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
cache_notification_info(sql, name, binds)
|
134
|
-
)
|
135
|
-
@query_cache[sql][binds]
|
136
|
-
else
|
137
|
-
@query_cache[sql][binds] = yield
|
138
|
-
end
|
139
|
-
result.dup
|
284
|
+
result = @query_cache.compute_if_absent(key) do
|
285
|
+
hit = false
|
286
|
+
yield
|
287
|
+
end
|
140
288
|
end
|
289
|
+
|
290
|
+
if hit
|
291
|
+
ActiveSupport::Notifications.instrument(
|
292
|
+
"sql.active_record",
|
293
|
+
cache_notification_info(sql, name, binds)
|
294
|
+
)
|
295
|
+
end
|
296
|
+
|
297
|
+
result.dup
|
141
298
|
end
|
142
299
|
|
143
300
|
# Database adapters can override this method to
|
@@ -149,13 +306,10 @@ module ActiveRecord
|
|
149
306
|
type_casted_binds: -> { type_casted_binds(binds) },
|
150
307
|
name: name,
|
151
308
|
connection: self,
|
309
|
+
transaction: current_transaction.user_transaction.presence,
|
152
310
|
cached: true
|
153
311
|
}
|
154
312
|
end
|
155
|
-
|
156
|
-
def configure_query_cache!
|
157
|
-
enable_query_cache! if pool.query_cache_enabled
|
158
|
-
end
|
159
313
|
end
|
160
314
|
end
|
161
315
|
end
|
@@ -5,7 +5,69 @@ require "active_support/multibyte/chars"
|
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
module ConnectionAdapters # :nodoc:
|
8
|
+
# = Active Record Connection Adapters \Quoting
|
8
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
|
+
|
9
71
|
# Quotes the column value to help prevent
|
10
72
|
# {SQL injection attacks}[https://en.wikipedia.org/wiki/SQL_injection].
|
11
73
|
def quote(value)
|
@@ -17,7 +79,7 @@ module ActiveRecord
|
|
17
79
|
when nil then "NULL"
|
18
80
|
# BigDecimals need to be put in a non-normalized form and quoted.
|
19
81
|
when BigDecimal then value.to_s("F")
|
20
|
-
when Numeric
|
82
|
+
when Numeric then value.to_s
|
21
83
|
when Type::Binary::Data then quoted_binary(value)
|
22
84
|
when Type::Time::Value then "'#{quoted_time(value)}'"
|
23
85
|
when Date, Time then "'#{quoted_date(value)}'"
|
@@ -44,11 +106,11 @@ module ActiveRecord
|
|
44
106
|
end
|
45
107
|
end
|
46
108
|
|
47
|
-
#
|
109
|
+
# Cast a value to be used as a bound parameter of unknown type. For example,
|
48
110
|
# MySQL might perform dangerous castings when comparing a string to a number,
|
49
111
|
# so this method will cast numbers to string.
|
50
|
-
def
|
51
|
-
|
112
|
+
def cast_bound_value(value) # :nodoc:
|
113
|
+
value
|
52
114
|
end
|
53
115
|
|
54
116
|
# If you are having to call this function, you are likely doing something
|
@@ -70,20 +132,20 @@ module ActiveRecord
|
|
70
132
|
s.gsub("\\", '\&\&').gsub("'", "''") # ' (for ruby-mode)
|
71
133
|
end
|
72
134
|
|
73
|
-
# Quotes the column name.
|
135
|
+
# Quotes the column name.
|
74
136
|
def quote_column_name(column_name)
|
75
|
-
column_name
|
137
|
+
self.class.quote_column_name(column_name)
|
76
138
|
end
|
77
139
|
|
78
|
-
# Quotes the table name.
|
140
|
+
# Quotes the table name.
|
79
141
|
def quote_table_name(table_name)
|
80
|
-
|
142
|
+
self.class.quote_table_name(table_name)
|
81
143
|
end
|
82
144
|
|
83
145
|
# Override to return the quoted table name for assignment. Defaults to
|
84
146
|
# table quoting.
|
85
147
|
#
|
86
|
-
# This works for
|
148
|
+
# This works for MySQL where table.column can be used to
|
87
149
|
# resolve ambiguity.
|
88
150
|
#
|
89
151
|
# We override this in the sqlite3 and postgresql adapters to use only
|
@@ -121,7 +183,7 @@ module ActiveRecord
|
|
121
183
|
# if the value is a Time responding to usec.
|
122
184
|
def quoted_date(value)
|
123
185
|
if value.acts_like?(:time)
|
124
|
-
if
|
186
|
+
if default_timezone == :utc
|
125
187
|
value = value.getutc if !value.utc?
|
126
188
|
else
|
127
189
|
value = value.getlocal
|
@@ -158,59 +220,6 @@ module ActiveRecord
|
|
158
220
|
comment
|
159
221
|
end
|
160
222
|
|
161
|
-
def column_name_matcher # :nodoc:
|
162
|
-
COLUMN_NAME
|
163
|
-
end
|
164
|
-
|
165
|
-
def column_name_with_order_matcher # :nodoc:
|
166
|
-
COLUMN_NAME_WITH_ORDER
|
167
|
-
end
|
168
|
-
|
169
|
-
# Regexp for column names (with or without a table name prefix).
|
170
|
-
# Matches the following:
|
171
|
-
#
|
172
|
-
# "#{table_name}.#{column_name}"
|
173
|
-
# "#{column_name}"
|
174
|
-
COLUMN_NAME = /
|
175
|
-
\A
|
176
|
-
(
|
177
|
-
(?:
|
178
|
-
# table_name.column_name | function(one or no argument)
|
179
|
-
((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
|
180
|
-
)
|
181
|
-
(?:(?:\s+AS)?\s+\w+)?
|
182
|
-
)
|
183
|
-
(?:\s*,\s*\g<1>)*
|
184
|
-
\z
|
185
|
-
/ix
|
186
|
-
|
187
|
-
# Regexp for column names with order (with or without a table name prefix,
|
188
|
-
# with or without various order modifiers). Matches the following:
|
189
|
-
#
|
190
|
-
# "#{table_name}.#{column_name}"
|
191
|
-
# "#{table_name}.#{column_name} #{direction}"
|
192
|
-
# "#{table_name}.#{column_name} #{direction} NULLS FIRST"
|
193
|
-
# "#{table_name}.#{column_name} NULLS LAST"
|
194
|
-
# "#{column_name}"
|
195
|
-
# "#{column_name} #{direction}"
|
196
|
-
# "#{column_name} #{direction} NULLS FIRST"
|
197
|
-
# "#{column_name} NULLS LAST"
|
198
|
-
COLUMN_NAME_WITH_ORDER = /
|
199
|
-
\A
|
200
|
-
(
|
201
|
-
(?:
|
202
|
-
# table_name.column_name | function(one or no argument)
|
203
|
-
((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
|
204
|
-
)
|
205
|
-
(?:\s+ASC|\s+DESC)?
|
206
|
-
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
207
|
-
)
|
208
|
-
(?:\s*,\s*\g<1>)*
|
209
|
-
\z
|
210
|
-
/ix
|
211
|
-
|
212
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
213
|
-
|
214
223
|
private
|
215
224
|
def type_casted_binds(binds)
|
216
225
|
binds.map do |value|
|
@@ -2,21 +2,22 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
|
+
# = Active Record Connection Adapters \Savepoints
|
5
6
|
module Savepoints
|
6
7
|
def current_savepoint_name
|
7
8
|
current_transaction.savepoint_name
|
8
9
|
end
|
9
10
|
|
10
11
|
def create_savepoint(name = current_savepoint_name)
|
11
|
-
|
12
|
+
internal_execute("SAVEPOINT #{name}", "TRANSACTION")
|
12
13
|
end
|
13
14
|
|
14
15
|
def exec_rollback_to_savepoint(name = current_savepoint_name)
|
15
|
-
|
16
|
+
internal_execute("ROLLBACK TO SAVEPOINT #{name}", "TRANSACTION")
|
16
17
|
end
|
17
18
|
|
18
19
|
def release_savepoint(name = current_savepoint_name)
|
19
|
-
|
20
|
+
internal_execute("RELEASE SAVEPOINT #{name}", "TRANSACTION")
|
20
21
|
end
|
21
22
|
end
|
22
23
|
end
|
@@ -14,8 +14,10 @@ module ActiveRecord
|
|
14
14
|
end
|
15
15
|
|
16
16
|
delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
|
17
|
-
:options_include_default?, :supports_indexes_in_create?, :
|
17
|
+
:options_include_default?, :supports_indexes_in_create?, :use_foreign_keys?,
|
18
18
|
:quoted_columns_for_index, :supports_partial_index?, :supports_check_constraints?,
|
19
|
+
:supports_index_include?, :supports_exclusion_constraints?, :supports_unique_constraints?,
|
20
|
+
:supports_nulls_not_distinct?,
|
19
21
|
to: :@conn, private: true
|
20
22
|
|
21
23
|
private
|
@@ -51,7 +53,7 @@ module ActiveRecord
|
|
51
53
|
statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
|
52
54
|
end
|
53
55
|
|
54
|
-
if
|
56
|
+
if use_foreign_keys?
|
55
57
|
statements.concat(o.foreign_keys.map { |fk| accept fk })
|
56
58
|
end
|
57
59
|
|
@@ -59,6 +61,14 @@ module ActiveRecord
|
|
59
61
|
statements.concat(o.check_constraints.map { |chk| accept chk })
|
60
62
|
end
|
61
63
|
|
64
|
+
if supports_exclusion_constraints?
|
65
|
+
statements.concat(o.exclusion_constraints.map { |exc| accept exc })
|
66
|
+
end
|
67
|
+
|
68
|
+
if supports_unique_constraints?
|
69
|
+
statements.concat(o.unique_constraints.map { |exc| accept exc })
|
70
|
+
end
|
71
|
+
|
62
72
|
create_sql << "(#{statements.join(', ')})" if statements.present?
|
63
73
|
add_table_options!(create_sql, o)
|
64
74
|
create_sql << " AS #{to_sql(o.as)}" if o.as
|
@@ -70,10 +80,12 @@ module ActiveRecord
|
|
70
80
|
end
|
71
81
|
|
72
82
|
def visit_ForeignKeyDefinition(o)
|
83
|
+
quoted_columns = Array(o.column).map { |c| quote_column_name(c) }
|
84
|
+
quoted_primary_keys = Array(o.primary_key).map { |c| quote_column_name(c) }
|
73
85
|
sql = +<<~SQL
|
74
86
|
CONSTRAINT #{quote_column_name(o.name)}
|
75
|
-
FOREIGN KEY (#{
|
76
|
-
REFERENCES #{quote_table_name(o.to_table)} (#{
|
87
|
+
FOREIGN KEY (#{quoted_columns.join(", ")})
|
88
|
+
REFERENCES #{quote_table_name(o.to_table)} (#{quoted_primary_keys.join(", ")})
|
77
89
|
SQL
|
78
90
|
sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
|
79
91
|
sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
|
@@ -100,6 +112,8 @@ module ActiveRecord
|
|
100
112
|
sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}"
|
101
113
|
sql << "USING #{index.using}" if supports_index_using? && index.using
|
102
114
|
sql << "(#{quoted_columns(index)})"
|
115
|
+
sql << "INCLUDE (#{quoted_include_columns(index.include)})" if supports_index_include? && index.include
|
116
|
+
sql << "NULLS NOT DISTINCT" if supports_nulls_not_distinct? && index.nulls_not_distinct
|
103
117
|
sql << "WHERE #{index.where}" if supports_partial_index? && index.where
|
104
118
|
|
105
119
|
sql.join(" ")
|