activerecord 7.0.8 → 7.1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1554 -1452
- data/MIT-LICENSE +1 -1
- data/README.rdoc +16 -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 +20 -4
- 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 +15 -9
- data/lib/active_record/associations/collection_proxy.rb +15 -10
- 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 +31 -7
- data/lib/active_record/associations/preloader.rb +13 -10
- 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 +313 -217
- 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 +52 -34
- 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 +74 -51
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- 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 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -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 +18 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -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 +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
- 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/quoting.rb +10 -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 +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
- 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 +52 -39
- 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 +209 -79
- 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 +262 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +175 -153
- 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 +9 -4
- 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 +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
- 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/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 +112 -28
- data/lib/active_record/errors.rb +112 -18
- 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 +135 -71
- data/lib/active_record/future_result.rb +31 -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 +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -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 +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +104 -5
- data/lib/active_record/migration/compatibility.rb +139 -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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +219 -111
- data/lib/active_record/model_schema.rb +64 -44
- data/lib/active_record/nested_attributes.rb +24 -6
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +188 -37
- 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 +109 -47
- data/lib/active_record/railties/controller_runtime.rb +12 -6
- data/lib/active_record/railties/databases.rake +142 -148
- 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 +174 -44
- 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 +187 -63
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- 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/polymorphic_array_value.rb +4 -6
- 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 +352 -63
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +91 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -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 +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +27 -15
- 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 +1 -9
- 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 +48 -12
- 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 }.uniq
|
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,23 +1,30 @@
|
|
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
|
8
6
|
class Column < ConnectionAdapters::Column # :nodoc:
|
9
7
|
delegate :oid, :fmod, to: :sql_type_metadata
|
10
8
|
|
11
|
-
def initialize(*, serial: nil, generated: nil, **)
|
9
|
+
def initialize(*, serial: nil, identity: nil, generated: nil, **)
|
12
10
|
super
|
13
11
|
@serial = serial
|
12
|
+
@identity = identity
|
14
13
|
@generated = generated
|
15
14
|
end
|
16
15
|
|
16
|
+
def identity?
|
17
|
+
@identity
|
18
|
+
end
|
19
|
+
|
17
20
|
def serial?
|
18
21
|
@serial
|
19
22
|
end
|
20
23
|
|
24
|
+
def auto_incremented_by_db?
|
25
|
+
serial? || identity?
|
26
|
+
end
|
27
|
+
|
21
28
|
def virtual?
|
22
29
|
# We assume every generated column is virtual, no matter the concrete type
|
23
30
|
@generated.present?
|
@@ -42,12 +49,14 @@ module ActiveRecord
|
|
42
49
|
|
43
50
|
def init_with(coder)
|
44
51
|
@serial = coder["serial"]
|
52
|
+
@identity = coder["identity"]
|
45
53
|
@generated = coder["generated"]
|
46
54
|
super
|
47
55
|
end
|
48
56
|
|
49
57
|
def encode_with(coder)
|
50
58
|
coder["serial"] = @serial
|
59
|
+
coder["identity"] = @identity
|
51
60
|
coder["generated"] = @generated
|
52
61
|
super
|
53
62
|
end
|
@@ -55,6 +64,7 @@ module ActiveRecord
|
|
55
64
|
def ==(other)
|
56
65
|
other.is_a?(Column) &&
|
57
66
|
super &&
|
67
|
+
identity? == other.identity? &&
|
58
68
|
serial? == other.serial?
|
59
69
|
end
|
60
70
|
alias :eql? :==
|
@@ -62,6 +72,7 @@ module ActiveRecord
|
|
62
72
|
def hash
|
63
73
|
Column.hash ^
|
64
74
|
super.hash ^
|
75
|
+
identity?.hash ^
|
65
76
|
serial?.hash
|
66
77
|
end
|
67
78
|
end
|
@@ -4,19 +4,21 @@ 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
|
+
result = conn.async_exec(sql).map_types!(@type_map_for_results).values
|
20
|
+
verified!
|
21
|
+
result
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|
@@ -34,24 +36,32 @@ module ActiveRecord
|
|
34
36
|
|
35
37
|
# Executes an SQL statement, returning a PG::Result object on success
|
36
38
|
# or raising a PG::Error exception otherwise.
|
39
|
+
#
|
40
|
+
# Setting +allow_retry+ to true causes the db to reconnect and retry
|
41
|
+
# executing the SQL statement in case of a connection-related exception.
|
42
|
+
# This option should only be enabled for known idempotent queries.
|
43
|
+
#
|
37
44
|
# Note: the PG::Result object is manually memory managed; if you don't
|
38
45
|
# 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)
|
46
|
+
def execute(...) # :nodoc:
|
47
|
+
super
|
48
|
+
ensure
|
49
|
+
@notice_receiver_sql_warnings = []
|
50
|
+
end
|
45
51
|
|
46
|
-
|
47
|
-
|
48
|
-
|
52
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
53
|
+
log(sql, name, async: async) do
|
54
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
55
|
+
result = conn.async_exec(sql)
|
56
|
+
verified!
|
57
|
+
handle_warnings(result)
|
58
|
+
result
|
49
59
|
end
|
50
60
|
end
|
51
61
|
end
|
52
62
|
|
53
|
-
def
|
54
|
-
execute_and_clear(sql, name, binds, prepare: prepare, async: async) do |result|
|
63
|
+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
|
64
|
+
execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
|
55
65
|
types = {}
|
56
66
|
fields = result.fields
|
57
67
|
fields.each_with_index do |fname, i|
|
@@ -68,26 +78,11 @@ module ActiveRecord
|
|
68
78
|
end
|
69
79
|
alias :exec_update :exec_delete
|
70
80
|
|
71
|
-
def
|
72
|
-
if pk.nil?
|
73
|
-
# Extract the table from the insert sql. Yuck.
|
74
|
-
table_ref = extract_table_ref_from_insert_sql(sql)
|
75
|
-
pk = primary_key(table_ref) if table_ref
|
76
|
-
end
|
77
|
-
|
78
|
-
if pk = suppress_composite_primary_key(pk)
|
79
|
-
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
|
80
|
-
end
|
81
|
-
|
82
|
-
super
|
83
|
-
end
|
84
|
-
private :sql_for_insert
|
85
|
-
|
86
|
-
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :nodoc:
|
81
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
87
82
|
if use_insert_returning? || pk == false
|
88
83
|
super
|
89
84
|
else
|
90
|
-
result =
|
85
|
+
result = internal_exec_query(sql, name, binds)
|
91
86
|
unless sequence_name
|
92
87
|
table_ref = extract_table_ref_from_insert_sql(sql)
|
93
88
|
if table_ref
|
@@ -103,22 +98,27 @@ module ActiveRecord
|
|
103
98
|
|
104
99
|
# Begins a transaction.
|
105
100
|
def begin_db_transaction # :nodoc:
|
106
|
-
|
101
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
107
102
|
end
|
108
103
|
|
109
104
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
110
|
-
|
111
|
-
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
105
|
+
internal_execute("BEGIN ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
112
106
|
end
|
113
107
|
|
114
108
|
# Commits a transaction.
|
115
109
|
def commit_db_transaction # :nodoc:
|
116
|
-
|
110
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
117
111
|
end
|
118
112
|
|
119
113
|
# Aborts a transaction.
|
120
114
|
def exec_rollback_db_transaction # :nodoc:
|
121
|
-
|
115
|
+
cancel_any_running_query
|
116
|
+
internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
117
|
+
end
|
118
|
+
|
119
|
+
def exec_restart_db_transaction # :nodoc:
|
120
|
+
cancel_any_running_query
|
121
|
+
internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
122
122
|
end
|
123
123
|
|
124
124
|
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
@@ -129,7 +129,24 @@ module ActiveRecord
|
|
129
129
|
HIGH_PRECISION_CURRENT_TIMESTAMP
|
130
130
|
end
|
131
131
|
|
132
|
+
def build_explain_clause(options = [])
|
133
|
+
return "EXPLAIN" if options.empty?
|
134
|
+
|
135
|
+
"EXPLAIN (#{options.join(", ").upcase})"
|
136
|
+
end
|
137
|
+
|
132
138
|
private
|
139
|
+
IDLE_TRANSACTION_STATUSES = [PG::PQTRANS_IDLE, PG::PQTRANS_INTRANS, PG::PQTRANS_INERROR]
|
140
|
+
private_constant :IDLE_TRANSACTION_STATUSES
|
141
|
+
|
142
|
+
def cancel_any_running_query
|
143
|
+
return if @raw_connection.nil? || IDLE_TRANSACTION_STATUSES.include?(@raw_connection.transaction_status)
|
144
|
+
|
145
|
+
@raw_connection.cancel
|
146
|
+
@raw_connection.block
|
147
|
+
rescue PG::Error
|
148
|
+
end
|
149
|
+
|
133
150
|
def execute_batch(statements, name = nil)
|
134
151
|
execute(combine_multi_statements(statements))
|
135
152
|
end
|
@@ -140,12 +157,29 @@ module ActiveRecord
|
|
140
157
|
|
141
158
|
# Returns the current ID of a table's sequence.
|
142
159
|
def last_insert_id_result(sequence_name)
|
143
|
-
|
160
|
+
internal_exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
|
161
|
+
end
|
162
|
+
|
163
|
+
def returning_column_values(result)
|
164
|
+
result.rows.first
|
144
165
|
end
|
145
166
|
|
146
167
|
def suppress_composite_primary_key(pk)
|
147
168
|
pk unless pk.is_a?(Array)
|
148
169
|
end
|
170
|
+
|
171
|
+
def handle_warnings(sql)
|
172
|
+
@notice_receiver_sql_warnings.each do |warning|
|
173
|
+
next if warning_ignored?(warning)
|
174
|
+
|
175
|
+
warning.sql = sql
|
176
|
+
ActiveRecord.db_warnings_action.call(warning)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def warning_ignored?(warning)
|
181
|
+
["WARNING", "ERROR", "FATAL", "PANIC"].exclude?(warning.level) || super
|
182
|
+
end
|
149
183
|
end
|
150
184
|
end
|
151
185
|
end
|
@@ -27,9 +27,10 @@ module ActiveRecord
|
|
27
27
|
value = value.sub(/^\((.+)\)$/, '-\1') # (4)
|
28
28
|
case value
|
29
29
|
when /^-?\D*+[\d,]+\.\d{2}$/ # (1)
|
30
|
-
value.
|
30
|
+
value.delete!("^-0-9.")
|
31
31
|
when /^-?\D*+[\d.]+,\d{2}$/ # (2)
|
32
|
-
value.
|
32
|
+
value.delete!("^-0-9,")
|
33
|
+
value.tr!(",", ".")
|
33
34
|
end
|
34
35
|
|
35
36
|
super(value)
|
@@ -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,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
time = super
|
16
16
|
return time if time.is_a?(ActiveSupport::TimeWithZone) || !time.acts_like?(:time)
|
17
17
|
|
18
|
-
# While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to
|
18
|
+
# While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to PostgreSQL in UTC.
|
19
19
|
# We prefer times always in UTC, so here we convert back.
|
20
20
|
if is_utc?
|
21
21
|
time.getutc
|