activerecord 7.0.4 → 7.1.5.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 +1971 -1243
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- 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 +20 -14
- data/lib/active_record/associations/collection_proxy.rb +20 -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/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/preloader/association.rb +31 -7
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- 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 +333 -222
- 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 +53 -35
- 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 +21 -8
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -4
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -26
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +59 -10
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +16 -32
- 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 +80 -50
- 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 +51 -7
- 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 +155 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +297 -127
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +509 -103
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +254 -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 +23 -144
- data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -14
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +19 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +106 -55
- 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 +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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 +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
- 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 +365 -61
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +354 -193
- 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 +9 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +213 -85
- 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 +258 -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 +181 -154
- data/lib/active_record/counter_cache.rb +52 -27
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +28 -14
- 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 +15 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- 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 +23 -8
- 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 +22 -21
- data/lib/active_record/encryption.rb +3 -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/explain_subscriber.rb +1 -1
- 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 +40 -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/optimistic.rb +33 -19
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- 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 +9 -11
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +105 -7
- data/lib/active_record/migration/compatibility.rb +163 -58
- 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 +271 -114
- data/lib/active_record/model_schema.rb +69 -44
- data/lib/active_record/nested_attributes.rb +37 -8
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +195 -42
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +4 -22
- data/lib/active_record/query_logs.rb +87 -51
- 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 +14 -9
- data/lib/active_record/railties/databases.rake +144 -150
- 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 +189 -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 +232 -81
- 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 +31 -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 +26 -14
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +408 -76
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +103 -37
- data/lib/active_record/result.rb +25 -9
- 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 +50 -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 +9 -9
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +152 -108
- 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 +114 -96
- data/lib/active_record/timestamp.rb +30 -16
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +39 -13
- 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 +8 -4
- 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 +130 -17
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/and.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/filter.rb +1 -1
- 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/tree_manager.rb +5 -1
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +83 -18
- 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 +51 -15
- 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,51 @@ module ActiveRecord
|
|
116
131
|
#++
|
117
132
|
|
118
133
|
def active?
|
119
|
-
@
|
134
|
+
!(@raw_connection.nil? || @raw_connection.closed?) && @lock.synchronize { @raw_connection&.ping } || false
|
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
|
-
|
133
|
-
|
142
|
+
@lock.synchronize do
|
143
|
+
super
|
144
|
+
@raw_connection&.close
|
145
|
+
@raw_connection = nil
|
146
|
+
end
|
134
147
|
end
|
135
148
|
|
136
149
|
def discard! # :nodoc:
|
137
|
-
|
138
|
-
|
139
|
-
|
150
|
+
@lock.synchronize do
|
151
|
+
super
|
152
|
+
@raw_connection&.automatic_close = false
|
153
|
+
@raw_connection = nil
|
154
|
+
end
|
140
155
|
end
|
141
156
|
|
142
157
|
private
|
158
|
+
def text_type?(type)
|
159
|
+
TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
|
160
|
+
end
|
161
|
+
|
143
162
|
def connect
|
144
|
-
@
|
145
|
-
|
163
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
164
|
+
rescue ConnectionNotEstablished => ex
|
165
|
+
raise ex.set_pool(@pool)
|
166
|
+
end
|
167
|
+
|
168
|
+
def reconnect
|
169
|
+
@lock.synchronize do
|
170
|
+
@raw_connection&.close
|
171
|
+
@raw_connection = nil
|
172
|
+
connect
|
173
|
+
end
|
146
174
|
end
|
147
175
|
|
148
176
|
def configure_connection
|
149
|
-
@
|
177
|
+
@raw_connection.query_options[:as] = :array
|
178
|
+
@raw_connection.query_options[:database_timezone] = default_timezone
|
150
179
|
super
|
151
180
|
end
|
152
181
|
|
@@ -155,16 +184,38 @@ module ActiveRecord
|
|
155
184
|
end
|
156
185
|
|
157
186
|
def get_full_version
|
158
|
-
|
187
|
+
any_raw_connection.server_info[:version]
|
159
188
|
end
|
160
189
|
|
161
190
|
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)
|
191
|
+
if exception.is_a?(::Mysql2::Error::TimeoutError) && !exception.error_number
|
192
|
+
ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
193
|
+
elsif exception.is_a?(::Mysql2::Error::ConnectionError)
|
194
|
+
if exception.message.match?(/MySQL client is not connected/i)
|
195
|
+
ActiveRecord::ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
196
|
+
else
|
197
|
+
ActiveRecord::ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
198
|
+
end
|
164
199
|
else
|
165
200
|
super
|
166
201
|
end
|
167
202
|
end
|
203
|
+
|
204
|
+
def default_prepared_statements
|
205
|
+
false
|
206
|
+
end
|
207
|
+
|
208
|
+
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
209
|
+
Type::ImmutableString.new(true: "1", false: "0", **args)
|
210
|
+
end
|
211
|
+
|
212
|
+
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
213
|
+
Type::String.new(true: "1", false: "0", **args)
|
214
|
+
end
|
215
|
+
|
216
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
168
217
|
end
|
218
|
+
|
219
|
+
ActiveSupport.run_load_hooks(:active_record_mysql2adapter, Mysql2Adapter)
|
169
220
|
end
|
170
221
|
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,17 +49,22 @@ module ActiveRecord
|
|
42
49
|
|
43
50
|
def init_with(coder)
|
44
51
|
@serial = coder["serial"]
|
52
|
+
@identity = coder["identity"]
|
53
|
+
@generated = coder["generated"]
|
45
54
|
super
|
46
55
|
end
|
47
56
|
|
48
57
|
def encode_with(coder)
|
49
58
|
coder["serial"] = @serial
|
59
|
+
coder["identity"] = @identity
|
60
|
+
coder["generated"] = @generated
|
50
61
|
super
|
51
62
|
end
|
52
63
|
|
53
64
|
def ==(other)
|
54
65
|
other.is_a?(Column) &&
|
55
66
|
super &&
|
67
|
+
identity? == other.identity? &&
|
56
68
|
serial? == other.serial?
|
57
69
|
end
|
58
70
|
alias :eql? :==
|
@@ -60,6 +72,7 @@ module ActiveRecord
|
|
60
72
|
def hash
|
61
73
|
Column.hash ^
|
62
74
|
super.hash ^
|
75
|
+
identity?.hash ^
|
63
76
|
serial?.hash
|
64
77
|
end
|
65
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,34 +36,38 @@ 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|
|
58
68
|
ftype = result.ftype i
|
59
69
|
fmod = result.fmod i
|
60
|
-
|
61
|
-
when Type::Integer, Type::Float, OID::Decimal, Type::String, Type::DateTime, Type::Boolean
|
62
|
-
# skip if a column has already been type casted by pg decoders
|
63
|
-
else types[fname] = type
|
64
|
-
end
|
70
|
+
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
65
71
|
end
|
66
72
|
build_result(columns: fields, rows: result.values, column_types: types)
|
67
73
|
end
|
@@ -72,26 +78,11 @@ module ActiveRecord
|
|
72
78
|
end
|
73
79
|
alias :exec_update :exec_delete
|
74
80
|
|
75
|
-
def
|
76
|
-
if pk.nil?
|
77
|
-
# Extract the table from the insert sql. Yuck.
|
78
|
-
table_ref = extract_table_ref_from_insert_sql(sql)
|
79
|
-
pk = primary_key(table_ref) if table_ref
|
80
|
-
end
|
81
|
-
|
82
|
-
if pk = suppress_composite_primary_key(pk)
|
83
|
-
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
|
84
|
-
end
|
85
|
-
|
86
|
-
super
|
87
|
-
end
|
88
|
-
private :sql_for_insert
|
89
|
-
|
90
|
-
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:
|
91
82
|
if use_insert_returning? || pk == false
|
92
83
|
super
|
93
84
|
else
|
94
|
-
result =
|
85
|
+
result = internal_exec_query(sql, name, binds)
|
95
86
|
unless sequence_name
|
96
87
|
table_ref = extract_table_ref_from_insert_sql(sql)
|
97
88
|
if table_ref
|
@@ -107,22 +98,27 @@ module ActiveRecord
|
|
107
98
|
|
108
99
|
# Begins a transaction.
|
109
100
|
def begin_db_transaction # :nodoc:
|
110
|
-
|
101
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
111
102
|
end
|
112
103
|
|
113
104
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
114
|
-
|
115
|
-
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)
|
116
106
|
end
|
117
107
|
|
118
108
|
# Commits a transaction.
|
119
109
|
def commit_db_transaction # :nodoc:
|
120
|
-
|
110
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
121
111
|
end
|
122
112
|
|
123
113
|
# Aborts a transaction.
|
124
114
|
def exec_rollback_db_transaction # :nodoc:
|
125
|
-
|
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)
|
126
122
|
end
|
127
123
|
|
128
124
|
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
@@ -133,7 +129,24 @@ module ActiveRecord
|
|
133
129
|
HIGH_PRECISION_CURRENT_TIMESTAMP
|
134
130
|
end
|
135
131
|
|
132
|
+
def build_explain_clause(options = [])
|
133
|
+
return "EXPLAIN" if options.empty?
|
134
|
+
|
135
|
+
"EXPLAIN (#{options.join(", ").upcase})"
|
136
|
+
end
|
137
|
+
|
136
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
|
+
|
137
150
|
def execute_batch(statements, name = nil)
|
138
151
|
execute(combine_multi_statements(statements))
|
139
152
|
end
|
@@ -144,12 +157,29 @@ module ActiveRecord
|
|
144
157
|
|
145
158
|
# Returns the current ID of a table's sequence.
|
146
159
|
def last_insert_id_result(sequence_name)
|
147
|
-
|
160
|
+
internal_exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
|
161
|
+
end
|
162
|
+
|
163
|
+
def returning_column_values(result)
|
164
|
+
result.rows.first
|
148
165
|
end
|
149
166
|
|
150
167
|
def suppress_composite_primary_key(pk)
|
151
168
|
pk unless pk.is_a?(Array)
|
152
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
|
153
183
|
end
|
154
184
|
end
|
155
185
|
end
|
@@ -28,6 +28,12 @@ module ActiveRecord
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
# TODO: Remove when IPAddr#== compares IPAddr#prefix. See
|
32
|
+
# https://github.com/ruby/ipaddr/issues/21
|
33
|
+
def changed?(old_value, new_value, _new_value_before_type_cast)
|
34
|
+
!old_value.eql?(new_value) || !old_value.nil? && old_value.prefix != new_value.prefix
|
35
|
+
end
|
36
|
+
|
31
37
|
def cast_value(value)
|
32
38
|
if value.nil?
|
33
39
|
nil
|
@@ -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)
|