activerecord 7.0.8.7 → 7.1.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 +1339 -1572
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +16 -11
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader.rb +12 -9
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +193 -97
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- 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 +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- 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 +289 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
- 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 +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- 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 +17 -12
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
- 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 +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +71 -94
- data/lib/active_record/core.rb +128 -138
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +8 -3
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- 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 +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -26
- data/lib/active_record/errors.rb +89 -15
- data/lib/active_record/explain.rb +23 -3
- 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 +119 -71
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -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 +5 -7
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -5
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +213 -109
- data/lib/active_record/model_schema.rb +47 -27
- data/lib/active_record/nested_attributes.rb +28 -3
- data/lib/active_record/normalization.rb +158 -0
- data/lib/active_record/persistence.rb +183 -33
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- data/lib/active_record/railties/databases.rake +139 -145
- 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 +169 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +152 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- data/lib/active_record/relation/finder_methods.rb +85 -15
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +351 -62
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +41 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -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 +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- 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 +14 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- 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/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- 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 +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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 +52 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -1,70 +1,80 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_record/connection_adapters/abstract_mysql_adapter"
|
4
|
-
require "active_record/connection_adapters/
|
4
|
+
require "active_record/connection_adapters/mysql2/database_statements"
|
5
5
|
|
6
6
|
gem "mysql2", "~> 0.5"
|
7
7
|
require "mysql2"
|
8
8
|
|
9
9
|
module ActiveRecord
|
10
10
|
module ConnectionHandling # :nodoc:
|
11
|
+
def mysql2_adapter_class
|
12
|
+
ConnectionAdapters::Mysql2Adapter
|
13
|
+
end
|
14
|
+
|
11
15
|
# Establishes a connection to the database that's used by all Active Record objects.
|
12
16
|
def mysql2_connection(config)
|
13
|
-
config
|
14
|
-
config[:flags] ||= 0
|
15
|
-
|
16
|
-
if config[:flags].kind_of? Array
|
17
|
-
config[:flags].push "FOUND_ROWS"
|
18
|
-
else
|
19
|
-
config[:flags] |= Mysql2::Client::FOUND_ROWS
|
20
|
-
end
|
21
|
-
|
22
|
-
ConnectionAdapters::Mysql2Adapter.new(
|
23
|
-
ConnectionAdapters::Mysql2Adapter.new_client(config),
|
24
|
-
logger,
|
25
|
-
nil,
|
26
|
-
config,
|
27
|
-
)
|
17
|
+
mysql2_adapter_class.new(config)
|
28
18
|
end
|
29
19
|
end
|
30
20
|
|
31
21
|
module ConnectionAdapters
|
22
|
+
# = Active Record MySQL2 Adapter
|
32
23
|
class Mysql2Adapter < AbstractMysqlAdapter
|
33
|
-
ER_BAD_DB_ERROR
|
34
|
-
|
35
|
-
|
36
|
-
|
24
|
+
ER_BAD_DB_ERROR = 1049
|
25
|
+
ER_DBACCESS_DENIED_ERROR = 1044
|
26
|
+
ER_ACCESS_DENIED_ERROR = 1045
|
27
|
+
ER_CONN_HOST_ERROR = 2003
|
28
|
+
ER_UNKNOWN_HOST_ERROR = 2005
|
37
29
|
|
38
30
|
ADAPTER_NAME = "Mysql2"
|
39
31
|
|
40
|
-
include
|
32
|
+
include Mysql2::DatabaseStatements
|
41
33
|
|
42
34
|
class << self
|
43
35
|
def new_client(config)
|
44
|
-
Mysql2::Client.new(config)
|
45
|
-
rescue Mysql2::Error => error
|
46
|
-
|
36
|
+
::Mysql2::Client.new(config)
|
37
|
+
rescue ::Mysql2::Error => error
|
38
|
+
case error.error_number
|
39
|
+
when ER_BAD_DB_ERROR
|
47
40
|
raise ActiveRecord::NoDatabaseError.db_error(config[:database])
|
48
|
-
|
41
|
+
when ER_DBACCESS_DENIED_ERROR, ER_ACCESS_DENIED_ERROR
|
49
42
|
raise ActiveRecord::DatabaseConnectionError.username_error(config[:username])
|
50
|
-
|
43
|
+
when ER_CONN_HOST_ERROR, ER_UNKNOWN_HOST_ERROR
|
51
44
|
raise ActiveRecord::DatabaseConnectionError.hostname_error(config[:host])
|
52
45
|
else
|
53
46
|
raise ActiveRecord::ConnectionNotEstablished, error.message
|
54
47
|
end
|
55
48
|
end
|
56
|
-
end
|
57
49
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
50
|
+
private
|
51
|
+
def initialize_type_map(m)
|
52
|
+
super
|
53
|
+
|
54
|
+
m.register_type(%r(char)i) do |sql_type|
|
55
|
+
limit = extract_limit(sql_type)
|
56
|
+
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
57
|
+
end
|
58
|
+
|
59
|
+
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
60
|
+
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
61
|
+
end
|
62
62
|
end
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
65
|
+
|
66
|
+
def initialize(...)
|
67
|
+
super
|
68
|
+
|
69
|
+
@config[:flags] ||= 0
|
70
|
+
|
71
|
+
if @config[:flags].kind_of? Array
|
72
|
+
@config[:flags].push "FOUND_ROWS"
|
73
|
+
else
|
74
|
+
@config[:flags] |= ::Mysql2::Client::FOUND_ROWS
|
75
|
+
end
|
76
|
+
|
77
|
+
@connection_parameters ||= @config
|
68
78
|
end
|
69
79
|
|
70
80
|
def supports_json?
|
@@ -83,6 +93,10 @@ module ActiveRecord
|
|
83
93
|
true
|
84
94
|
end
|
85
95
|
|
96
|
+
def savepoint_errors_invalidate_transactions?
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
86
100
|
def supports_lazy_transactions?
|
87
101
|
true
|
88
102
|
end
|
@@ -105,10 +119,11 @@ module ActiveRecord
|
|
105
119
|
# QUOTING ==================================================
|
106
120
|
#++
|
107
121
|
|
122
|
+
# Quotes strings for use in SQL input.
|
108
123
|
def quote_string(string)
|
109
|
-
|
110
|
-
|
111
|
-
|
124
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
125
|
+
connection.escape(string)
|
126
|
+
end
|
112
127
|
end
|
113
128
|
|
114
129
|
#--
|
@@ -116,37 +131,45 @@ module ActiveRecord
|
|
116
131
|
#++
|
117
132
|
|
118
133
|
def active?
|
119
|
-
|
134
|
+
!!@raw_connection&.ping
|
120
135
|
end
|
121
136
|
|
122
|
-
def reconnect!
|
123
|
-
super
|
124
|
-
disconnect!
|
125
|
-
connect
|
126
|
-
end
|
127
137
|
alias :reset! :reconnect!
|
128
138
|
|
129
139
|
# Disconnects from the database if already connected.
|
130
140
|
# Otherwise, this method does nothing.
|
131
141
|
def disconnect!
|
132
142
|
super
|
133
|
-
@
|
143
|
+
@raw_connection&.close
|
144
|
+
@raw_connection = nil
|
134
145
|
end
|
135
146
|
|
136
147
|
def discard! # :nodoc:
|
137
148
|
super
|
138
|
-
@
|
139
|
-
@
|
149
|
+
@raw_connection&.automatic_close = false
|
150
|
+
@raw_connection = nil
|
140
151
|
end
|
141
152
|
|
142
153
|
private
|
154
|
+
def text_type?(type)
|
155
|
+
TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
|
156
|
+
end
|
157
|
+
|
143
158
|
def connect
|
144
|
-
@
|
145
|
-
|
159
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
160
|
+
rescue ConnectionNotEstablished => ex
|
161
|
+
raise ex.set_pool(@pool)
|
162
|
+
end
|
163
|
+
|
164
|
+
def reconnect
|
165
|
+
@raw_connection&.close
|
166
|
+
@raw_connection = nil
|
167
|
+
connect
|
146
168
|
end
|
147
169
|
|
148
170
|
def configure_connection
|
149
|
-
@
|
171
|
+
@raw_connection.query_options[:as] = :array
|
172
|
+
@raw_connection.query_options[:database_timezone] = default_timezone
|
150
173
|
super
|
151
174
|
end
|
152
175
|
|
@@ -155,16 +178,38 @@ module ActiveRecord
|
|
155
178
|
end
|
156
179
|
|
157
180
|
def get_full_version
|
158
|
-
|
181
|
+
any_raw_connection.server_info[:version]
|
159
182
|
end
|
160
183
|
|
161
184
|
def translate_exception(exception, message:, sql:, binds:)
|
162
|
-
if exception.is_a?(Mysql2::Error::TimeoutError) && !exception.error_number
|
163
|
-
ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds)
|
185
|
+
if exception.is_a?(::Mysql2::Error::TimeoutError) && !exception.error_number
|
186
|
+
ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
187
|
+
elsif exception.is_a?(::Mysql2::Error::ConnectionError)
|
188
|
+
if exception.message.match?(/MySQL client is not connected/i)
|
189
|
+
ActiveRecord::ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
190
|
+
else
|
191
|
+
ActiveRecord::ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
192
|
+
end
|
164
193
|
else
|
165
194
|
super
|
166
195
|
end
|
167
196
|
end
|
197
|
+
|
198
|
+
def default_prepared_statements
|
199
|
+
false
|
200
|
+
end
|
201
|
+
|
202
|
+
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
203
|
+
Type::ImmutableString.new(true: "1", false: "0", **args)
|
204
|
+
end
|
205
|
+
|
206
|
+
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
207
|
+
Type::String.new(true: "1", false: "0", **args)
|
208
|
+
end
|
209
|
+
|
210
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
168
211
|
end
|
212
|
+
|
213
|
+
ActiveSupport.run_load_hooks(:active_record_mysql2adapter, Mysql2Adapter)
|
169
214
|
end
|
170
215
|
end
|
@@ -5,8 +5,13 @@ module ActiveRecord
|
|
5
5
|
class PoolConfig # :nodoc:
|
6
6
|
include Mutex_m
|
7
7
|
|
8
|
-
attr_reader :db_config, :
|
9
|
-
|
8
|
+
attr_reader :db_config, :role, :shard
|
9
|
+
attr_writer :schema_reflection
|
10
|
+
attr_accessor :connection_class
|
11
|
+
|
12
|
+
def schema_reflection
|
13
|
+
@schema_reflection ||= SchemaReflection.new(db_config.lazy_schema_cache_path)
|
14
|
+
end
|
10
15
|
|
11
16
|
INSTANCES = ObjectSpace::WeakMap.new
|
12
17
|
private_constant :INSTANCES
|
@@ -15,6 +20,10 @@ module ActiveRecord
|
|
15
20
|
def discard_pools!
|
16
21
|
INSTANCES.each_key(&:discard_pool!)
|
17
22
|
end
|
23
|
+
|
24
|
+
def disconnect_all!
|
25
|
+
INSTANCES.each_key { |c| c.disconnect!(automatic_reconnect: true) }
|
26
|
+
end
|
18
27
|
end
|
19
28
|
|
20
29
|
def initialize(connection_class, db_config, role, shard)
|
@@ -27,7 +36,7 @@ module ActiveRecord
|
|
27
36
|
INSTANCES[self] = self
|
28
37
|
end
|
29
38
|
|
30
|
-
def
|
39
|
+
def connection_name
|
31
40
|
if connection_class.primary_class?
|
32
41
|
"ActiveRecord::Base"
|
33
42
|
else
|
@@ -35,7 +44,7 @@ module ActiveRecord
|
|
35
44
|
end
|
36
45
|
end
|
37
46
|
|
38
|
-
def disconnect!
|
47
|
+
def disconnect!(automatic_reconnect: false)
|
39
48
|
ActiveSupport::ForkTracker.check!
|
40
49
|
|
41
50
|
return unless @pool
|
@@ -43,7 +52,7 @@ module ActiveRecord
|
|
43
52
|
synchronize do
|
44
53
|
return unless @pool
|
45
54
|
|
46
|
-
@pool.automatic_reconnect =
|
55
|
+
@pool.automatic_reconnect = automatic_reconnect
|
47
56
|
@pool.disconnect!
|
48
57
|
end
|
49
58
|
|
@@ -4,40 +4,50 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
class PoolManager # :nodoc:
|
6
6
|
def initialize
|
7
|
-
@
|
7
|
+
@role_to_shard_mapping = Hash.new { |h, k| h[k] = {} }
|
8
8
|
end
|
9
9
|
|
10
10
|
def shard_names
|
11
|
-
@
|
11
|
+
@role_to_shard_mapping.values.flat_map { |shard_map| shard_map.keys }
|
12
12
|
end
|
13
13
|
|
14
14
|
def role_names
|
15
|
-
@
|
15
|
+
@role_to_shard_mapping.keys
|
16
16
|
end
|
17
17
|
|
18
18
|
def pool_configs(role = nil)
|
19
19
|
if role
|
20
|
-
@
|
20
|
+
@role_to_shard_mapping[role].values
|
21
21
|
else
|
22
|
-
@
|
22
|
+
@role_to_shard_mapping.flat_map { |_, shard_map| shard_map.values }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def each_pool_config(role = nil, &block)
|
27
|
+
if role
|
28
|
+
@role_to_shard_mapping[role].each_value(&block)
|
29
|
+
else
|
30
|
+
@role_to_shard_mapping.each_value do |shard_map|
|
31
|
+
shard_map.each_value(&block)
|
32
|
+
end
|
23
33
|
end
|
24
34
|
end
|
25
35
|
|
26
36
|
def remove_role(role)
|
27
|
-
@
|
37
|
+
@role_to_shard_mapping.delete(role)
|
28
38
|
end
|
29
39
|
|
30
40
|
def remove_pool_config(role, shard)
|
31
|
-
@
|
41
|
+
@role_to_shard_mapping[role].delete(shard)
|
32
42
|
end
|
33
43
|
|
34
44
|
def get_pool_config(role, shard)
|
35
|
-
@
|
45
|
+
@role_to_shard_mapping[role][shard]
|
36
46
|
end
|
37
47
|
|
38
48
|
def set_pool_config(role, shard, pool_config)
|
39
49
|
if pool_config
|
40
|
-
@
|
50
|
+
@role_to_shard_mapping[role][shard] = pool_config
|
41
51
|
else
|
42
52
|
raise ArgumentError, "The `pool_config` for the :#{role} role and :#{shard} shard was `nil`. Please check your configuration. If you want your writing role to be something other than `:writing` set `config.active_record.writing_role` in your application configuration. The same setting should be applied for the `reading_role` if applicable."
|
43
53
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/object/blank"
|
4
|
-
|
5
3
|
module ActiveRecord
|
6
4
|
module ConnectionAdapters
|
7
5
|
module PostgreSQL
|
@@ -17,6 +15,7 @@ module ActiveRecord
|
|
17
15
|
def serial?
|
18
16
|
@serial
|
19
17
|
end
|
18
|
+
alias_method :auto_incremented_by_db?, :serial?
|
20
19
|
|
21
20
|
def virtual?
|
22
21
|
# We assume every generated column is virtual, no matter the concrete type
|
@@ -4,19 +4,19 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module PostgreSQL
|
6
6
|
module DatabaseStatements
|
7
|
-
def explain(arel, binds = [])
|
8
|
-
sql
|
9
|
-
|
7
|
+
def explain(arel, binds = [], options = [])
|
8
|
+
sql = build_explain_clause(options) + " " + to_sql(arel, binds)
|
9
|
+
result = internal_exec_query(sql, "EXPLAIN", binds)
|
10
|
+
PostgreSQL::ExplainPrettyPrinter.new.pp(result)
|
10
11
|
end
|
11
12
|
|
12
13
|
# Queries the database and returns the results in an Array-like object
|
13
14
|
def query(sql, name = nil) # :nodoc:
|
14
|
-
materialize_transactions
|
15
15
|
mark_transaction_written_if_write(sql)
|
16
16
|
|
17
17
|
log(sql, name) do
|
18
|
-
|
19
|
-
|
18
|
+
with_raw_connection do |conn|
|
19
|
+
conn.async_exec(sql).map_types!(@type_map_for_results).values
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -34,24 +34,31 @@ module ActiveRecord
|
|
34
34
|
|
35
35
|
# Executes an SQL statement, returning a PG::Result object on success
|
36
36
|
# or raising a PG::Error exception otherwise.
|
37
|
+
#
|
38
|
+
# Setting +allow_retry+ to true causes the db to reconnect and retry
|
39
|
+
# executing the SQL statement in case of a connection-related exception.
|
40
|
+
# This option should only be enabled for known idempotent queries.
|
41
|
+
#
|
37
42
|
# Note: the PG::Result object is manually memory managed; if you don't
|
38
43
|
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
|
39
|
-
def execute(
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
mark_transaction_written_if_write(sql)
|
44
|
+
def execute(...) # :nodoc:
|
45
|
+
super
|
46
|
+
ensure
|
47
|
+
@notice_receiver_sql_warnings = []
|
48
|
+
end
|
45
49
|
|
46
|
-
|
47
|
-
|
48
|
-
|
50
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
51
|
+
log(sql, name, async: async) do
|
52
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
53
|
+
result = conn.async_exec(sql)
|
54
|
+
handle_warnings(result)
|
55
|
+
result
|
49
56
|
end
|
50
57
|
end
|
51
58
|
end
|
52
59
|
|
53
|
-
def
|
54
|
-
execute_and_clear(sql, name, binds, prepare: prepare, async: async) do |result|
|
60
|
+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
|
61
|
+
execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
|
55
62
|
types = {}
|
56
63
|
fields = result.fields
|
57
64
|
fields.each_with_index do |fname, i|
|
@@ -68,26 +75,27 @@ module ActiveRecord
|
|
68
75
|
end
|
69
76
|
alias :exec_update :exec_delete
|
70
77
|
|
71
|
-
def sql_for_insert(sql, pk, binds) # :nodoc:
|
78
|
+
def sql_for_insert(sql, pk, binds, returning) # :nodoc:
|
72
79
|
if pk.nil?
|
73
80
|
# Extract the table from the insert sql. Yuck.
|
74
81
|
table_ref = extract_table_ref_from_insert_sql(sql)
|
75
82
|
pk = primary_key(table_ref) if table_ref
|
76
83
|
end
|
77
84
|
|
78
|
-
|
79
|
-
|
80
|
-
|
85
|
+
returning_columns = returning || Array(pk)
|
86
|
+
|
87
|
+
returning_columns_statement = returning_columns.map { |c| quote_column_name(c) }.join(", ")
|
88
|
+
sql = "#{sql} RETURNING #{returning_columns_statement}" if returning_columns.any?
|
81
89
|
|
82
90
|
super
|
83
91
|
end
|
84
92
|
private :sql_for_insert
|
85
93
|
|
86
|
-
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :nodoc:
|
94
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
87
95
|
if use_insert_returning? || pk == false
|
88
96
|
super
|
89
97
|
else
|
90
|
-
result =
|
98
|
+
result = internal_exec_query(sql, name, binds)
|
91
99
|
unless sequence_name
|
92
100
|
table_ref = extract_table_ref_from_insert_sql(sql)
|
93
101
|
if table_ref
|
@@ -103,22 +111,27 @@ module ActiveRecord
|
|
103
111
|
|
104
112
|
# Begins a transaction.
|
105
113
|
def begin_db_transaction # :nodoc:
|
106
|
-
|
114
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
107
115
|
end
|
108
116
|
|
109
117
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
110
|
-
|
111
|
-
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
118
|
+
internal_execute("BEGIN ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
112
119
|
end
|
113
120
|
|
114
121
|
# Commits a transaction.
|
115
122
|
def commit_db_transaction # :nodoc:
|
116
|
-
|
123
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
117
124
|
end
|
118
125
|
|
119
126
|
# Aborts a transaction.
|
120
127
|
def exec_rollback_db_transaction # :nodoc:
|
121
|
-
|
128
|
+
cancel_any_running_query
|
129
|
+
internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
130
|
+
end
|
131
|
+
|
132
|
+
def exec_restart_db_transaction # :nodoc:
|
133
|
+
cancel_any_running_query
|
134
|
+
internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
122
135
|
end
|
123
136
|
|
124
137
|
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
@@ -129,7 +142,24 @@ module ActiveRecord
|
|
129
142
|
HIGH_PRECISION_CURRENT_TIMESTAMP
|
130
143
|
end
|
131
144
|
|
145
|
+
def build_explain_clause(options = [])
|
146
|
+
return "EXPLAIN" if options.empty?
|
147
|
+
|
148
|
+
"EXPLAIN (#{options.join(", ").upcase})"
|
149
|
+
end
|
150
|
+
|
132
151
|
private
|
152
|
+
IDLE_TRANSACTION_STATUSES = [PG::PQTRANS_IDLE, PG::PQTRANS_INTRANS, PG::PQTRANS_INERROR]
|
153
|
+
private_constant :IDLE_TRANSACTION_STATUSES
|
154
|
+
|
155
|
+
def cancel_any_running_query
|
156
|
+
return if @raw_connection.nil? || IDLE_TRANSACTION_STATUSES.include?(@raw_connection.transaction_status)
|
157
|
+
|
158
|
+
@raw_connection.cancel
|
159
|
+
@raw_connection.block
|
160
|
+
rescue PG::Error
|
161
|
+
end
|
162
|
+
|
133
163
|
def execute_batch(statements, name = nil)
|
134
164
|
execute(combine_multi_statements(statements))
|
135
165
|
end
|
@@ -140,12 +170,29 @@ module ActiveRecord
|
|
140
170
|
|
141
171
|
# Returns the current ID of a table's sequence.
|
142
172
|
def last_insert_id_result(sequence_name)
|
143
|
-
|
173
|
+
internal_exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
|
174
|
+
end
|
175
|
+
|
176
|
+
def returning_column_values(result)
|
177
|
+
result.rows.first
|
144
178
|
end
|
145
179
|
|
146
180
|
def suppress_composite_primary_key(pk)
|
147
181
|
pk unless pk.is_a?(Array)
|
148
182
|
end
|
183
|
+
|
184
|
+
def handle_warnings(sql)
|
185
|
+
@notice_receiver_sql_warnings.each do |warning|
|
186
|
+
next if warning_ignored?(warning)
|
187
|
+
|
188
|
+
warning.sql = sql
|
189
|
+
ActiveRecord.db_warnings_action.call(warning)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def warning_ignored?(warning)
|
194
|
+
["WARNING", "ERROR", "FATAL", "PANIC"].exclude?(warning.level) || super
|
195
|
+
end
|
149
196
|
end
|
150
197
|
end
|
151
198
|
end
|
@@ -18,7 +18,7 @@ module ActiveRecord
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def cast_value(value)
|
21
|
-
return if
|
21
|
+
return if ["empty", ""].include? value
|
22
22
|
return value unless value.is_a?(::String)
|
23
23
|
|
24
24
|
extracted = extract_bounds(value)
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
28
28
|
if !infinity?(from) && extracted[:exclude_start]
|
29
29
|
raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')"
|
30
30
|
end
|
31
|
-
::Range.new(from, to, extracted[:exclude_end])
|
31
|
+
::Range.new(*sanitize_bounds(from, to), extracted[:exclude_end])
|
32
32
|
end
|
33
33
|
|
34
34
|
def serialize(value)
|
@@ -76,6 +76,15 @@ module ActiveRecord
|
|
76
76
|
}
|
77
77
|
end
|
78
78
|
|
79
|
+
INFINITE_FLOAT_RANGE = (-::Float::INFINITY)..(::Float::INFINITY) # :nodoc:
|
80
|
+
|
81
|
+
def sanitize_bounds(from, to)
|
82
|
+
[
|
83
|
+
(from == -::Float::INFINITY && !INFINITE_FLOAT_RANGE.cover?(to)) ? nil : from,
|
84
|
+
(to == ::Float::INFINITY && !INFINITE_FLOAT_RANGE.cover?(from)) ? nil : to
|
85
|
+
]
|
86
|
+
end
|
87
|
+
|
79
88
|
# When formatting the bound values of range types, PostgreSQL quotes
|
80
89
|
# the bound value using double-quotes in certain conditions. Within
|
81
90
|
# a double-quoted string, literal " and \ characters are themselves
|
@@ -15,14 +15,14 @@ module ActiveRecord
|
|
15
15
|
|
16
16
|
# Escapes binary strings for bytea input to the database.
|
17
17
|
def escape_bytea(value)
|
18
|
-
|
18
|
+
valid_raw_connection.escape_bytea(value) if value
|
19
19
|
end
|
20
20
|
|
21
21
|
# Unescapes bytea output from a database to the binary string it represents.
|
22
22
|
# NOTE: This is NOT an inverse of escape_bytea! This is only to be used
|
23
23
|
# on escaped binary output from database drive.
|
24
24
|
def unescape_bytea(value)
|
25
|
-
|
25
|
+
valid_raw_connection.unescape_bytea(value) if value
|
26
26
|
end
|
27
27
|
|
28
28
|
def check_int_in_range(value)
|
@@ -72,7 +72,9 @@ module ActiveRecord
|
|
72
72
|
|
73
73
|
# Quotes strings for use in SQL input.
|
74
74
|
def quote_string(s) # :nodoc:
|
75
|
-
|
75
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
76
|
+
connection.escape(s)
|
77
|
+
end
|
76
78
|
end
|
77
79
|
|
78
80
|
# Checks the following cases:
|
@@ -118,7 +120,7 @@ module ActiveRecord
|
|
118
120
|
def quote_default_expression(value, column) # :nodoc:
|
119
121
|
if value.is_a?(Proc)
|
120
122
|
value.call
|
121
|
-
elsif column.type == :uuid && value.is_a?(String) &&
|
123
|
+
elsif column.type == :uuid && value.is_a?(String) && value.include?("()")
|
122
124
|
value # Does not quote function default values for UUID columns
|
123
125
|
elsif column.respond_to?(:array?)
|
124
126
|
type = lookup_cast_type_from_column(column)
|
@@ -163,7 +165,7 @@ module ActiveRecord
|
|
163
165
|
(
|
164
166
|
(?:
|
165
167
|
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
166
|
-
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?
|
168
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
167
169
|
)
|
168
170
|
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
169
171
|
)
|
@@ -176,8 +178,9 @@ module ActiveRecord
|
|
176
178
|
(
|
177
179
|
(?:
|
178
180
|
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
179
|
-
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?
|
181
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
180
182
|
)
|
183
|
+
(?:\s+COLLATE\s+"\w+")?
|
181
184
|
(?:\s+ASC|\s+DESC)?
|
182
185
|
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
183
186
|
)
|
@@ -38,7 +38,7 @@ Rails needs superuser privileges to disable referential integrity.
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
41
|
+
def check_all_foreign_keys_valid! # :nodoc:
|
42
42
|
sql = <<~SQL
|
43
43
|
do $$
|
44
44
|
declare r record;
|
@@ -61,14 +61,8 @@ Rails needs superuser privileges to disable referential integrity.
|
|
61
61
|
$$;
|
62
62
|
SQL
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
execute(sql)
|
67
|
-
end
|
68
|
-
|
69
|
-
true
|
70
|
-
rescue ActiveRecord::StatementInvalid
|
71
|
-
false
|
64
|
+
transaction(requires_new: true) do
|
65
|
+
execute(sql)
|
72
66
|
end
|
73
67
|
end
|
74
68
|
end
|