activerecord 7.1.3.3 → 7.2.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +507 -2128
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +18 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +4 -2
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +3 -3
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +5 -7
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +34 -11
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods.rb +87 -58
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +14 -30
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -58
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +160 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -61
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +109 -77
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +59 -38
- data/lib/active_record/counter_cache.rb +23 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +44 -36
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +30 -6
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
- data/lib/active_record/encryption/encryptor.rb +17 -2
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -4
- data/lib/active_record/enum.rb +11 -2
- data/lib/active_record/errors.rb +16 -11
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +17 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -1
- data/lib/active_record/message_pack.rb +2 -2
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +11 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +85 -76
- data/lib/active_record/model_schema.rb +34 -69
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +32 -354
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +52 -64
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +98 -37
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -43
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +500 -66
- data/lib/active_record/result.rb +32 -45
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/signed_id.rb +11 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +70 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +82 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +68 -0
- data/lib/active_record/transactions.rb +43 -14
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +31 -17
- data/lib/arel.rb +7 -3
- metadata +16 -11
@@ -6,9 +6,52 @@ module ActiveRecord
|
|
6
6
|
module ConnectionAdapters
|
7
7
|
module MySQL
|
8
8
|
module Quoting # :nodoc:
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
9
11
|
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
10
12
|
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
11
13
|
|
14
|
+
module ClassMethods # :nodoc:
|
15
|
+
def column_name_matcher
|
16
|
+
/
|
17
|
+
\A
|
18
|
+
(
|
19
|
+
(?:
|
20
|
+
# `table_name`.`column_name` | function(one or no argument)
|
21
|
+
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
|
22
|
+
)
|
23
|
+
(?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
|
24
|
+
)
|
25
|
+
(?:\s*,\s*\g<1>)*
|
26
|
+
\z
|
27
|
+
/ix
|
28
|
+
end
|
29
|
+
|
30
|
+
def column_name_with_order_matcher
|
31
|
+
/
|
32
|
+
\A
|
33
|
+
(
|
34
|
+
(?:
|
35
|
+
# `table_name`.`column_name` | function(one or no argument)
|
36
|
+
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
|
37
|
+
)
|
38
|
+
(?:\s+COLLATE\s+(?:\w+|"\w+"))?
|
39
|
+
(?:\s+ASC|\s+DESC)?
|
40
|
+
)
|
41
|
+
(?:\s*,\s*\g<1>)*
|
42
|
+
\z
|
43
|
+
/ix
|
44
|
+
end
|
45
|
+
|
46
|
+
def quote_column_name(name)
|
47
|
+
QUOTED_COLUMN_NAMES[name] ||= "`#{name.to_s.gsub('`', '``')}`".freeze
|
48
|
+
end
|
49
|
+
|
50
|
+
def quote_table_name(name)
|
51
|
+
QUOTED_TABLE_NAMES[name] ||= "`#{name.to_s.gsub('`', '``').gsub(".", "`.`")}`".freeze
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
12
55
|
def cast_bound_value(value)
|
13
56
|
case value
|
14
57
|
when Rational
|
@@ -21,22 +64,11 @@ module ActiveRecord
|
|
21
64
|
"1"
|
22
65
|
when false
|
23
66
|
"0"
|
24
|
-
when ActiveSupport::Duration
|
25
|
-
warn_quote_duration_deprecated
|
26
|
-
value.to_s
|
27
67
|
else
|
28
68
|
value
|
29
69
|
end
|
30
70
|
end
|
31
71
|
|
32
|
-
def quote_column_name(name)
|
33
|
-
QUOTED_COLUMN_NAMES[name] ||= "`#{super.gsub('`', '``')}`"
|
34
|
-
end
|
35
|
-
|
36
|
-
def quote_table_name(name)
|
37
|
-
QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "`.`").freeze
|
38
|
-
end
|
39
|
-
|
40
72
|
def unquoted_true
|
41
73
|
1
|
42
74
|
end
|
@@ -84,43 +116,6 @@ module ActiveRecord
|
|
84
116
|
super
|
85
117
|
end
|
86
118
|
end
|
87
|
-
|
88
|
-
def column_name_matcher
|
89
|
-
COLUMN_NAME
|
90
|
-
end
|
91
|
-
|
92
|
-
def column_name_with_order_matcher
|
93
|
-
COLUMN_NAME_WITH_ORDER
|
94
|
-
end
|
95
|
-
|
96
|
-
COLUMN_NAME = /
|
97
|
-
\A
|
98
|
-
(
|
99
|
-
(?:
|
100
|
-
# `table_name`.`column_name` | function(one or no argument)
|
101
|
-
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
|
102
|
-
)
|
103
|
-
(?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
|
104
|
-
)
|
105
|
-
(?:\s*,\s*\g<1>)*
|
106
|
-
\z
|
107
|
-
/ix
|
108
|
-
|
109
|
-
COLUMN_NAME_WITH_ORDER = /
|
110
|
-
\A
|
111
|
-
(
|
112
|
-
(?:
|
113
|
-
# `table_name`.`column_name` | function(one or no argument)
|
114
|
-
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
|
115
|
-
)
|
116
|
-
(?:\s+COLLATE\s+(?:\w+|"\w+"))?
|
117
|
-
(?:\s+ASC|\s+DESC)?
|
118
|
-
)
|
119
|
-
(?:\s*,\s*\g<1>)*
|
120
|
-
\z
|
121
|
-
/ix
|
122
|
-
|
123
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
124
119
|
end
|
125
120
|
end
|
126
121
|
end
|
@@ -68,6 +68,12 @@ module ActiveRecord
|
|
68
68
|
|
69
69
|
IndexDefinition.new(*index, **options)
|
70
70
|
end
|
71
|
+
rescue StatementInvalid => e
|
72
|
+
if e.message.match?(/Table '.+' doesn't exist/)
|
73
|
+
[]
|
74
|
+
else
|
75
|
+
raise
|
76
|
+
end
|
71
77
|
end
|
72
78
|
|
73
79
|
def remove_column(table_name, column_name, type = nil, **options)
|
@@ -185,6 +191,7 @@ module ActiveRecord
|
|
185
191
|
default, default_function = nil, default
|
186
192
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
187
193
|
default = +"(#{default})" unless default.start_with?("(")
|
194
|
+
default = default.gsub("\\'", "'")
|
188
195
|
default, default_function = nil, default
|
189
196
|
elsif type_metadata.type == :text && default&.start_with?("'")
|
190
197
|
# strip and unescape quotes
|
@@ -18,9 +18,9 @@ module ActiveRecord
|
|
18
18
|
result
|
19
19
|
end
|
20
20
|
|
21
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
21
|
+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
|
22
22
|
if without_prepared_statement?(binds)
|
23
|
-
execute_and_free(sql, name, async: async) do |result|
|
23
|
+
execute_and_free(sql, name, async: async, allow_retry: allow_retry) do |result|
|
24
24
|
if result
|
25
25
|
build_result(columns: result.fields, rows: result.to_a)
|
26
26
|
else
|
@@ -66,7 +66,11 @@ module ActiveRecord
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def last_inserted_id(result)
|
69
|
-
|
69
|
+
if supports_insert_returning?
|
70
|
+
super
|
71
|
+
else
|
72
|
+
@raw_connection&.last_id
|
73
|
+
end
|
70
74
|
end
|
71
75
|
|
72
76
|
def multi_statements_enabled?
|
@@ -94,12 +98,13 @@ module ActiveRecord
|
|
94
98
|
end
|
95
99
|
|
96
100
|
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
97
|
-
log(sql, name, async: async) do
|
101
|
+
log(sql, name, async: async) do |notification_payload|
|
98
102
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
99
103
|
sync_timezone_changes(conn)
|
100
104
|
result = conn.query(sql)
|
101
105
|
verified!
|
102
106
|
handle_warnings(sql)
|
107
|
+
notification_payload[:row_count] = result&.size || 0
|
103
108
|
result
|
104
109
|
end
|
105
110
|
end
|
@@ -113,7 +118,7 @@ module ActiveRecord
|
|
113
118
|
|
114
119
|
type_casted_binds = type_casted_binds(binds)
|
115
120
|
|
116
|
-
log(sql, name, binds, type_casted_binds, async: async) do
|
121
|
+
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
117
122
|
with_raw_connection do |conn|
|
118
123
|
sync_timezone_changes(conn)
|
119
124
|
|
@@ -139,6 +144,7 @@ module ActiveRecord
|
|
139
144
|
end
|
140
145
|
|
141
146
|
ret = yield stmt, result
|
147
|
+
notification_payload[:row_count] = result&.size || 0
|
142
148
|
result.free if result
|
143
149
|
stmt.close unless cache_stmt
|
144
150
|
ret
|
@@ -7,17 +7,6 @@ gem "mysql2", "~> 0.5"
|
|
7
7
|
require "mysql2"
|
8
8
|
|
9
9
|
module ActiveRecord
|
10
|
-
module ConnectionHandling # :nodoc:
|
11
|
-
def mysql2_adapter_class
|
12
|
-
ConnectionAdapters::Mysql2Adapter
|
13
|
-
end
|
14
|
-
|
15
|
-
# Establishes a connection to the database that's used by all Active Record objects.
|
16
|
-
def mysql2_connection(config)
|
17
|
-
mysql2_adapter_class.new(config)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
10
|
module ConnectionAdapters
|
22
11
|
# = Active Record MySQL2 Adapter
|
23
12
|
class Mysql2Adapter < AbstractMysqlAdapter
|
@@ -116,22 +105,15 @@ module ActiveRecord
|
|
116
105
|
end
|
117
106
|
|
118
107
|
#--
|
119
|
-
#
|
108
|
+
# CONNECTION MANAGEMENT ====================================
|
120
109
|
#++
|
121
110
|
|
122
|
-
|
123
|
-
|
124
|
-
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
125
|
-
connection.escape(string)
|
126
|
-
end
|
111
|
+
def connected?
|
112
|
+
!(@raw_connection.nil? || @raw_connection.closed?)
|
127
113
|
end
|
128
114
|
|
129
|
-
#--
|
130
|
-
# CONNECTION MANAGEMENT ====================================
|
131
|
-
#++
|
132
|
-
|
133
115
|
def active?
|
134
|
-
|
116
|
+
connected? && @lock.synchronize { @raw_connection&.ping } || false
|
135
117
|
end
|
136
118
|
|
137
119
|
alias :reset! :reconnect!
|
@@ -139,15 +121,19 @@ module ActiveRecord
|
|
139
121
|
# Disconnects from the database if already connected.
|
140
122
|
# Otherwise, this method does nothing.
|
141
123
|
def disconnect!
|
142
|
-
|
143
|
-
|
144
|
-
|
124
|
+
@lock.synchronize do
|
125
|
+
super
|
126
|
+
@raw_connection&.close
|
127
|
+
@raw_connection = nil
|
128
|
+
end
|
145
129
|
end
|
146
130
|
|
147
131
|
def discard! # :nodoc:
|
148
|
-
|
149
|
-
|
150
|
-
|
132
|
+
@lock.synchronize do
|
133
|
+
super
|
134
|
+
@raw_connection&.automatic_close = false
|
135
|
+
@raw_connection = nil
|
136
|
+
end
|
151
137
|
end
|
152
138
|
|
153
139
|
private
|
@@ -162,9 +148,11 @@ module ActiveRecord
|
|
162
148
|
end
|
163
149
|
|
164
150
|
def reconnect
|
165
|
-
@
|
166
|
-
|
167
|
-
|
151
|
+
@lock.synchronize do
|
152
|
+
@raw_connection&.close
|
153
|
+
@raw_connection = nil
|
154
|
+
connect
|
155
|
+
end
|
168
156
|
end
|
169
157
|
|
170
158
|
def configure_connection
|
@@ -174,7 +162,7 @@ module ActiveRecord
|
|
174
162
|
end
|
175
163
|
|
176
164
|
def full_version
|
177
|
-
|
165
|
+
database_version.full_version_string
|
178
166
|
end
|
179
167
|
|
180
168
|
def get_full_version
|
@@ -3,10 +3,10 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
5
|
class PoolConfig # :nodoc:
|
6
|
-
include
|
6
|
+
include MonitorMixin
|
7
7
|
|
8
8
|
attr_reader :db_config, :role, :shard
|
9
|
-
attr_writer :schema_reflection
|
9
|
+
attr_writer :schema_reflection, :server_version
|
10
10
|
attr_accessor :connection_class
|
11
11
|
|
12
12
|
def schema_reflection
|
@@ -28,6 +28,7 @@ module ActiveRecord
|
|
28
28
|
|
29
29
|
def initialize(connection_class, db_config, role, shard)
|
30
30
|
super()
|
31
|
+
@server_version = nil
|
31
32
|
@connection_class = connection_class
|
32
33
|
@db_config = db_config
|
33
34
|
@role = role
|
@@ -36,6 +37,10 @@ module ActiveRecord
|
|
36
37
|
INSTANCES[self] = self
|
37
38
|
end
|
38
39
|
|
40
|
+
def server_version(connection)
|
41
|
+
@server_version || synchronize { @server_version ||= connection.get_database_version }
|
42
|
+
end
|
43
|
+
|
39
44
|
def connection_name
|
40
45
|
if connection_class.primary_class?
|
41
46
|
"ActiveRecord::Base"
|
@@ -45,8 +50,6 @@ module ActiveRecord
|
|
45
50
|
end
|
46
51
|
|
47
52
|
def disconnect!(automatic_reconnect: false)
|
48
|
-
ActiveSupport::ForkTracker.check!
|
49
|
-
|
50
53
|
return unless @pool
|
51
54
|
|
52
55
|
synchronize do
|
@@ -60,8 +63,6 @@ module ActiveRecord
|
|
60
63
|
end
|
61
64
|
|
62
65
|
def pool
|
63
|
-
ActiveSupport::ForkTracker.check!
|
64
|
-
|
65
66
|
@pool || synchronize { @pool ||= ConnectionAdapters::ConnectionPool.new(self) }
|
66
67
|
end
|
67
68
|
|
@@ -14,10 +14,11 @@ module ActiveRecord
|
|
14
14
|
def query(sql, name = nil) # :nodoc:
|
15
15
|
mark_transaction_written_if_write(sql)
|
16
16
|
|
17
|
-
log(sql, name) do
|
17
|
+
log(sql, name) do |notification_payload|
|
18
18
|
with_raw_connection do |conn|
|
19
19
|
result = conn.async_exec(sql).map_types!(@type_map_for_results).values
|
20
20
|
verified!
|
21
|
+
notification_payload[:row_count] = result.count
|
21
22
|
result
|
22
23
|
end
|
23
24
|
end
|
@@ -50,11 +51,12 @@ module ActiveRecord
|
|
50
51
|
end
|
51
52
|
|
52
53
|
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
53
|
-
log(sql, name, async: async) do
|
54
|
+
log(sql, name, async: async) do |notification_payload|
|
54
55
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
55
56
|
result = conn.async_exec(sql)
|
56
57
|
verified!
|
57
58
|
handle_warnings(result)
|
59
|
+
notification_payload[:row_count] = result.count
|
58
60
|
result
|
59
61
|
end
|
60
62
|
end
|
@@ -69,7 +71,7 @@ module ActiveRecord
|
|
69
71
|
fmod = result.fmod i
|
70
72
|
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
71
73
|
end
|
72
|
-
build_result(columns: fields, rows: result.values, column_types: types)
|
74
|
+
build_result(columns: fields, rows: result.values, column_types: types.freeze)
|
73
75
|
end
|
74
76
|
end
|
75
77
|
|
@@ -122,7 +124,7 @@ module ActiveRecord
|
|
122
124
|
end
|
123
125
|
|
124
126
|
# From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
|
125
|
-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
|
127
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP", retryable: true).freeze # :nodoc:
|
126
128
|
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
127
129
|
|
128
130
|
def high_precision_current_timestamp
|
@@ -135,6 +137,27 @@ module ActiveRecord
|
|
135
137
|
"EXPLAIN (#{options.join(", ").upcase})"
|
136
138
|
end
|
137
139
|
|
140
|
+
# Set when constraints will be checked for the current transaction.
|
141
|
+
#
|
142
|
+
# Not passing any specific constraint names will set the value for all deferrable constraints.
|
143
|
+
#
|
144
|
+
# [<tt>deferred</tt>]
|
145
|
+
# Valid values are +:deferred+ or +:immediate+.
|
146
|
+
#
|
147
|
+
# See https://www.postgresql.org/docs/current/sql-set-constraints.html
|
148
|
+
def set_constraints(deferred, *constraints)
|
149
|
+
unless %i[deferred immediate].include?(deferred)
|
150
|
+
raise ArgumentError, "deferred must be :deferred or :immediate"
|
151
|
+
end
|
152
|
+
|
153
|
+
constraints = if constraints.empty?
|
154
|
+
"ALL"
|
155
|
+
else
|
156
|
+
constraints.map { |c| quote_table_name(c) }.join(", ")
|
157
|
+
end
|
158
|
+
execute("SET CONSTRAINTS #{constraints} #{deferred.to_s.upcase}")
|
159
|
+
end
|
160
|
+
|
138
161
|
private
|
139
162
|
IDLE_TRANSACTION_STATUSES = [PG::PQTRANS_IDLE, PG::PQTRANS_INTRANS, PG::PQTRANS_INERROR]
|
140
163
|
private_constant :IDLE_TRANSACTION_STATUSES
|
@@ -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
|
+
super || !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
|
@@ -33,7 +33,7 @@ module ActiveRecord
|
|
33
33
|
when ::Numeric
|
34
34
|
# Sometimes operations on Times returns just float number of seconds so we need to handle that.
|
35
35
|
# Example: Time.current - (Time.current + 1.hour) # => -3600.000001776 (Float)
|
36
|
-
value.
|
36
|
+
ActiveSupport::Duration.build(value).iso8601(precision: self.precision)
|
37
37
|
else
|
38
38
|
super
|
39
39
|
end
|
@@ -6,6 +6,7 @@ module ActiveRecord
|
|
6
6
|
module OID # :nodoc:
|
7
7
|
class Uuid < Type::Value # :nodoc:
|
8
8
|
ACCEPTABLE_UUID = %r{\A(\{)?([a-fA-F0-9]{4}-?){8}(?(1)\}|)\z}
|
9
|
+
CANONICAL_UUID = %r{\A[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}\z}
|
9
10
|
|
10
11
|
alias :serialize :deserialize
|
11
12
|
|
@@ -15,18 +16,27 @@ module ActiveRecord
|
|
15
16
|
|
16
17
|
def changed?(old_value, new_value, _new_value_before_type_cast)
|
17
18
|
old_value.class != new_value.class ||
|
18
|
-
new_value
|
19
|
+
new_value != old_value
|
19
20
|
end
|
20
21
|
|
21
22
|
def changed_in_place?(raw_old_value, new_value)
|
22
23
|
raw_old_value.class != new_value.class ||
|
23
|
-
new_value
|
24
|
+
new_value != raw_old_value
|
24
25
|
end
|
25
26
|
|
26
27
|
private
|
27
28
|
def cast_value(value)
|
28
|
-
|
29
|
-
|
29
|
+
value = value.to_s
|
30
|
+
format_uuid(value) if value.match?(ACCEPTABLE_UUID)
|
31
|
+
end
|
32
|
+
|
33
|
+
def format_uuid(uuid)
|
34
|
+
if uuid.match?(CANONICAL_UUID)
|
35
|
+
uuid
|
36
|
+
else
|
37
|
+
uuid = uuid.delete("{}-").downcase
|
38
|
+
"#{uuid[..7]}-#{uuid[8..11]}-#{uuid[12..15]}-#{uuid[16..19]}-#{uuid[20..]}"
|
39
|
+
end
|
30
40
|
end
|
31
41
|
end
|
32
42
|
end
|
@@ -4,9 +4,62 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module PostgreSQL
|
6
6
|
module Quoting
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
7
9
|
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
8
10
|
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
9
11
|
|
12
|
+
module ClassMethods # :nodoc:
|
13
|
+
def column_name_matcher
|
14
|
+
/
|
15
|
+
\A
|
16
|
+
(
|
17
|
+
(?:
|
18
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
19
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
20
|
+
)
|
21
|
+
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
22
|
+
)
|
23
|
+
(?:\s*,\s*\g<1>)*
|
24
|
+
\z
|
25
|
+
/ix
|
26
|
+
end
|
27
|
+
|
28
|
+
def column_name_with_order_matcher
|
29
|
+
/
|
30
|
+
\A
|
31
|
+
(
|
32
|
+
(?:
|
33
|
+
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
34
|
+
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
35
|
+
)
|
36
|
+
(?:\s+COLLATE\s+"\w+")?
|
37
|
+
(?:\s+ASC|\s+DESC)?
|
38
|
+
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
39
|
+
)
|
40
|
+
(?:\s*,\s*\g<1>)*
|
41
|
+
\z
|
42
|
+
/ix
|
43
|
+
end
|
44
|
+
|
45
|
+
# Quotes column names for use in SQL queries.
|
46
|
+
def quote_column_name(name) # :nodoc:
|
47
|
+
QUOTED_COLUMN_NAMES[name] ||= PG::Connection.quote_ident(name.to_s).freeze
|
48
|
+
end
|
49
|
+
|
50
|
+
# Checks the following cases:
|
51
|
+
#
|
52
|
+
# - table_name
|
53
|
+
# - "table.name"
|
54
|
+
# - schema_name.table_name
|
55
|
+
# - schema_name."table.name"
|
56
|
+
# - "schema.name".table_name
|
57
|
+
# - "schema.name"."table.name"
|
58
|
+
def quote_table_name(name) # :nodoc:
|
59
|
+
QUOTED_TABLE_NAMES[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
10
63
|
class IntegerOutOf64BitRange < StandardError
|
11
64
|
def initialize(msg)
|
12
65
|
super(msg)
|
@@ -77,30 +130,13 @@ module ActiveRecord
|
|
77
130
|
end
|
78
131
|
end
|
79
132
|
|
80
|
-
# Checks the following cases:
|
81
|
-
#
|
82
|
-
# - table_name
|
83
|
-
# - "table.name"
|
84
|
-
# - schema_name.table_name
|
85
|
-
# - schema_name."table.name"
|
86
|
-
# - "schema.name".table_name
|
87
|
-
# - "schema.name"."table.name"
|
88
|
-
def quote_table_name(name) # :nodoc:
|
89
|
-
QUOTED_TABLE_NAMES[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
|
90
|
-
end
|
91
|
-
|
92
|
-
# Quotes schema names for use in SQL queries.
|
93
|
-
def quote_schema_name(name)
|
94
|
-
PG::Connection.quote_ident(name)
|
95
|
-
end
|
96
|
-
|
97
133
|
def quote_table_name_for_assignment(table, attr)
|
98
134
|
quote_column_name(attr)
|
99
135
|
end
|
100
136
|
|
101
|
-
# Quotes
|
102
|
-
def
|
103
|
-
|
137
|
+
# Quotes schema names for use in SQL queries.
|
138
|
+
def quote_schema_name(schema_name)
|
139
|
+
quote_column_name(schema_name)
|
104
140
|
end
|
105
141
|
|
106
142
|
# Quote date/time values for use in SQL input.
|
@@ -143,6 +179,8 @@ module ActiveRecord
|
|
143
179
|
encode_array(value)
|
144
180
|
when Range
|
145
181
|
encode_range(value)
|
182
|
+
when Rational
|
183
|
+
value.to_f
|
146
184
|
else
|
147
185
|
super
|
148
186
|
end
|
@@ -153,44 +191,6 @@ module ActiveRecord
|
|
153
191
|
type_map.lookup(column.oid, column.fmod, column.sql_type)
|
154
192
|
end
|
155
193
|
|
156
|
-
def column_name_matcher
|
157
|
-
COLUMN_NAME
|
158
|
-
end
|
159
|
-
|
160
|
-
def column_name_with_order_matcher
|
161
|
-
COLUMN_NAME_WITH_ORDER
|
162
|
-
end
|
163
|
-
|
164
|
-
COLUMN_NAME = /
|
165
|
-
\A
|
166
|
-
(
|
167
|
-
(?:
|
168
|
-
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
169
|
-
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
170
|
-
)
|
171
|
-
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
|
172
|
-
)
|
173
|
-
(?:\s*,\s*\g<1>)*
|
174
|
-
\z
|
175
|
-
/ix
|
176
|
-
|
177
|
-
COLUMN_NAME_WITH_ORDER = /
|
178
|
-
\A
|
179
|
-
(
|
180
|
-
(?:
|
181
|
-
# "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
|
182
|
-
((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
|
183
|
-
)
|
184
|
-
(?:\s+COLLATE\s+"\w+")?
|
185
|
-
(?:\s+ASC|\s+DESC)?
|
186
|
-
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
187
|
-
)
|
188
|
-
(?:\s*,\s*\g<1>)*
|
189
|
-
\z
|
190
|
-
/ix
|
191
|
-
|
192
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
193
|
-
|
194
194
|
private
|
195
195
|
def lookup_cast_type(sql_type)
|
196
196
|
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
|
@@ -332,6 +332,26 @@ module ActiveRecord
|
|
332
332
|
def remove_unique_constraint(*args)
|
333
333
|
@base.remove_unique_constraint(name, *args)
|
334
334
|
end
|
335
|
+
|
336
|
+
# Validates the given constraint on the table.
|
337
|
+
#
|
338
|
+
# t.check_constraint("price > 0", name: "price_check", validate: false)
|
339
|
+
# t.validate_constraint "price_check"
|
340
|
+
#
|
341
|
+
# See {connection.validate_constraint}[rdoc-ref:SchemaStatements#validate_constraint]
|
342
|
+
def validate_constraint(*args)
|
343
|
+
@base.validate_constraint(name, *args)
|
344
|
+
end
|
345
|
+
|
346
|
+
# Validates the given check constraint on the table
|
347
|
+
#
|
348
|
+
# t.check_constraint("price > 0", name: "price_check", validate: false)
|
349
|
+
# t.validate_check_constraint name: "price_check"
|
350
|
+
#
|
351
|
+
# See {connection.validate_check_constraint}[rdoc-ref:SchemaStatements#validate_check_constraint]
|
352
|
+
def validate_check_constraint(*args)
|
353
|
+
@base.validate_check_constraint(name, *args)
|
354
|
+
end
|
335
355
|
end
|
336
356
|
|
337
357
|
# = Active Record PostgreSQL Adapter Alter \Table
|