activerecord 7.1.5.1 → 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 +515 -2445
- 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 +14 -7
- 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 +6 -4
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
- data/lib/active_record/associations/join_dependency.rb +5 -5
- 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 +33 -16
- 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 +4 -16
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +60 -71
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +13 -32
- 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 -65
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
- 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 +14 -5
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
- 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 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
- 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 +1 -1
- 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 +15 -13
- 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 +107 -75
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +53 -37
- data/lib/active_record/counter_cache.rb +18 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +38 -34
- 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 +24 -0
- 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 +22 -2
- 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.rb +0 -2
- data/lib/active_record/enum.rb +10 -1
- 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 +8 -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 +7 -6
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -4
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +5 -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 +28 -68
- data/lib/active_record/nested_attributes.rb +13 -16
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +50 -62
- 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 +90 -35
- 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.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -57
- 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 +496 -72
- data/lib/active_record/result.rb +31 -44
- 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 +76 -70
- 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 +81 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +1 -1
- 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 +3 -2
- 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 +29 -16
- data/lib/arel.rb +7 -3
- metadata +20 -15
@@ -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!
|
@@ -180,7 +162,7 @@ module ActiveRecord
|
|
180
162
|
end
|
181
163
|
|
182
164
|
def full_version
|
183
|
-
|
165
|
+
database_version.full_version_string
|
184
166
|
end
|
185
167
|
|
186
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
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
# TODO: Remove when IPAddr#== compares IPAddr#prefix. See
|
32
32
|
# https://github.com/ruby/ipaddr/issues/21
|
33
33
|
def changed?(old_value, new_value, _new_value_before_type_cast)
|
34
|
-
|
34
|
+
super || !old_value.nil? && old_value.prefix != new_value.prefix
|
35
35
|
end
|
36
36
|
|
37
37
|
def cast_value(value)
|
@@ -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
|
@@ -112,7 +112,7 @@ module ActiveRecord
|
|
112
112
|
|
113
113
|
orders = {}
|
114
114
|
opclasses = {}
|
115
|
-
include_columns = include ? include.split(",").map(
|
115
|
+
include_columns = include ? include.split(",").map { |c| Utils.unquote_identifier(c.strip.gsub('""', '"')) } : []
|
116
116
|
|
117
117
|
if indkey.include?(0)
|
118
118
|
columns = expressions
|
@@ -209,8 +209,16 @@ module ActiveRecord
|
|
209
209
|
end
|
210
210
|
|
211
211
|
# Creates a schema for the given schema name.
|
212
|
-
def create_schema(schema_name)
|
213
|
-
|
212
|
+
def create_schema(schema_name, force: nil, if_not_exists: nil)
|
213
|
+
if force && if_not_exists
|
214
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
215
|
+
end
|
216
|
+
|
217
|
+
if force
|
218
|
+
drop_schema(schema_name, if_exists: true)
|
219
|
+
end
|
220
|
+
|
221
|
+
execute("CREATE SCHEMA#{' IF NOT EXISTS' if if_not_exists} #{quote_schema_name(schema_name)}")
|
214
222
|
end
|
215
223
|
|
216
224
|
# Drops the schema for the given schema name.
|
@@ -247,8 +255,6 @@ module ActiveRecord
|
|
247
255
|
|
248
256
|
# Returns the sequence name for a table's primary key or some other specified key.
|
249
257
|
def default_sequence_name(table_name, pk = "id") # :nodoc:
|
250
|
-
return nil if pk.is_a?(Array)
|
251
|
-
|
252
258
|
result = serial_sequence(table_name, pk)
|
253
259
|
return nil unless result
|
254
260
|
Utils.extract_schema_qualified_name(result).to_s
|
@@ -524,14 +530,6 @@ module ActiveRecord
|
|
524
530
|
end
|
525
531
|
|
526
532
|
def add_foreign_key(from_table, to_table, **options)
|
527
|
-
if options[:deferrable] == true
|
528
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
529
|
-
`deferrable: true` is deprecated in favor of `deferrable: :immediate`, and will be removed in Rails 7.2.
|
530
|
-
MSG
|
531
|
-
|
532
|
-
options[:deferrable] = :immediate
|
533
|
-
end
|
534
|
-
|
535
533
|
assert_valid_deferrable(options[:deferrable])
|
536
534
|
|
537
535
|
super
|
@@ -692,6 +690,10 @@ module ActiveRecord
|
|
692
690
|
# The constraint name. Defaults to <tt>excl_rails_<identifier></tt>.
|
693
691
|
# [<tt>:deferrable</tt>]
|
694
692
|
# Specify whether or not the exclusion constraint should be deferrable. Valid values are +false+ or +:immediate+ or +:deferred+ to specify the default behavior. Defaults to +false+.
|
693
|
+
# [<tt>:using</tt>]
|
694
|
+
# Specify which index method to use when creating this exclusion constraint (e.g. +:btree+, +:gist+ etc).
|
695
|
+
# [<tt>:where</tt>]
|
696
|
+
# Specify an exclusion constraint on a subset of the table (internally PostgreSQL creates a partial index for this).
|
695
697
|
def add_exclusion_constraint(table_name, expression, **options)
|
696
698
|
options = exclusion_constraint_options(table_name, expression, options)
|
697
699
|
at = create_alter_table(table_name)
|
@@ -20,17 +20,6 @@ require "active_record/connection_adapters/postgresql/type_metadata"
|
|
20
20
|
require "active_record/connection_adapters/postgresql/utils"
|
21
21
|
|
22
22
|
module ActiveRecord
|
23
|
-
module ConnectionHandling # :nodoc:
|
24
|
-
def postgresql_adapter_class
|
25
|
-
ConnectionAdapters::PostgreSQLAdapter
|
26
|
-
end
|
27
|
-
|
28
|
-
# Establishes a connection to the database that's used by all Active Record objects
|
29
|
-
def postgresql_connection(config)
|
30
|
-
postgresql_adapter_class.new(config)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
23
|
module ConnectionAdapters
|
35
24
|
# = Active Record PostgreSQL Adapter
|
36
25
|
#
|
@@ -133,6 +122,15 @@ module ActiveRecord
|
|
133
122
|
# setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
|
134
123
|
class_attribute :datetime_type, default: :timestamp
|
135
124
|
|
125
|
+
##
|
126
|
+
# :singleton-method:
|
127
|
+
# Toggles automatic decoding of date columns.
|
128
|
+
#
|
129
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> String
|
130
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.decode_dates = true
|
131
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> Date
|
132
|
+
class_attribute :decode_dates, default: false
|
133
|
+
|
136
134
|
NATIVE_DATABASE_TYPES = {
|
137
135
|
primary_key: "bigserial primary key",
|
138
136
|
string: { name: "character varying" },
|
@@ -290,10 +288,6 @@ module ActiveRecord
|
|
290
288
|
{ concurrently: "CONCURRENTLY" }
|
291
289
|
end
|
292
290
|
|
293
|
-
def return_value_after_insert?(column) # :nodoc:
|
294
|
-
column.auto_populated?
|
295
|
-
end
|
296
|
-
|
297
291
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
298
292
|
def initialize(connection, max)
|
299
293
|
super(max)
|
@@ -342,6 +336,10 @@ module ActiveRecord
|
|
342
336
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
343
337
|
end
|
344
338
|
|
339
|
+
def connected?
|
340
|
+
!(@raw_connection.nil? || @raw_connection.finished?)
|
341
|
+
end
|
342
|
+
|
345
343
|
# Is this connection alive and ready for queries?
|
346
344
|
def active?
|
347
345
|
@lock.synchronize do
|
@@ -613,7 +611,9 @@ module ActiveRecord
|
|
613
611
|
|
614
612
|
# Returns the version of the connected PostgreSQL server.
|
615
613
|
def get_database_version # :nodoc:
|
616
|
-
|
614
|
+
with_raw_connection do |conn|
|
615
|
+
conn.server_version
|
616
|
+
end
|
617
617
|
end
|
618
618
|
alias :postgresql_version :database_version
|
619
619
|
|
@@ -889,10 +889,11 @@ module ActiveRecord
|
|
889
889
|
update_typemap_for_default_timezone
|
890
890
|
|
891
891
|
type_casted_binds = type_casted_binds(binds)
|
892
|
-
log(sql, name, binds, type_casted_binds, async: async) do
|
893
|
-
with_raw_connection do |conn|
|
892
|
+
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
893
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
894
894
|
result = conn.exec_params(sql, type_casted_binds)
|
895
895
|
verified!
|
896
|
+
notification_payload[:row_count] = result.count
|
896
897
|
result
|
897
898
|
end
|
898
899
|
end
|
@@ -903,13 +904,14 @@ module ActiveRecord
|
|
903
904
|
|
904
905
|
update_typemap_for_default_timezone
|
905
906
|
|
906
|
-
with_raw_connection do |conn|
|
907
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
907
908
|
stmt_key = prepare_statement(sql, binds, conn)
|
908
909
|
type_casted_binds = type_casted_binds(binds)
|
909
910
|
|
910
|
-
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
911
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do |notification_payload|
|
911
912
|
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
912
913
|
verified!
|
914
|
+
notification_payload[:row_count] = result.count
|
913
915
|
result
|
914
916
|
end
|
915
917
|
end
|
@@ -919,7 +921,7 @@ module ActiveRecord
|
|
919
921
|
# Nothing we can do if we are in a transaction because all commands
|
920
922
|
# will raise InFailedSQLTransaction
|
921
923
|
if in_transaction?
|
922
|
-
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
|
924
|
+
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message, connection_pool: @pool)
|
923
925
|
else
|
924
926
|
@lock.synchronize do
|
925
927
|
# outside of transactions we can simply flush this query and retry
|
@@ -995,6 +997,8 @@ module ActiveRecord
|
|
995
997
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
996
998
|
# This is called by #connect and should not be called manually.
|
997
999
|
def configure_connection
|
1000
|
+
super
|
1001
|
+
|
998
1002
|
if @config[:encoding]
|
999
1003
|
@raw_connection.set_client_encoding(@config[:encoding])
|
1000
1004
|
end
|
@@ -1165,6 +1169,7 @@ module ActiveRecord
|
|
1165
1169
|
"timestamp" => PG::TextDecoder::TimestampUtc,
|
1166
1170
|
"timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
|
1167
1171
|
}
|
1172
|
+
coders_by_name["date"] = PG::TextDecoder::Date if decode_dates
|
1168
1173
|
|
1169
1174
|
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
1170
1175
|
query = <<~SQL % known_coder_types.join(", ")
|