activerecord 7.1.6 → 7.2.3
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 +839 -2248
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +15 -8
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- 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 +16 -8
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +7 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- 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 +59 -292
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- 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 +5 -25
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods.rb +51 -60
- data/lib/active_record/attributes.rb +93 -68
- data/lib/active_record/autosave_association.rb +25 -32
- data/lib/active_record/base.rb +4 -5
- data/lib/active_record/callbacks.rb +1 -1
- 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 +294 -72
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -6
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
- data/lib/active_record/connection_adapters/abstract_adapter.rb +46 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +53 -15
- 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 +6 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +19 -18
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -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/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 +30 -8
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +36 -26
- 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 +57 -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 +26 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +133 -78
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
- 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 +68 -49
- data/lib/active_record/core.rb +112 -44
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +19 -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 +42 -18
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +25 -5
- data/lib/active_record/encryption/encryptor.rb +35 -19
- data/lib/active_record/encryption/key_provider.rb +1 -1
- 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/enum.rb +31 -13
- data/lib/active_record/errors.rb +49 -23
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +8 -4
- data/lib/active_record/gem_version.rb +2 -2
- 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/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 +87 -77
- data/lib/active_record/model_schema.rb +31 -68
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +19 -0
- data/lib/active_record/querying.rb +25 -13
- data/lib/active_record/railtie.rb +39 -57
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +42 -44
- data/lib/active_record/reflection.rb +98 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +14 -8
- data/lib/active_record/relation/calculations.rb +127 -89
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +26 -12
- 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/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +238 -65
- 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 +15 -21
- data/lib/active_record/relation.rb +508 -74
- 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 +48 -20
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/scoping/named.rb +1 -0
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +27 -7
- 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 +69 -41
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +8 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +86 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -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 +132 -0
- data/lib/active_record/transactions.rb +73 -15
- 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 +15 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +148 -39
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +3 -1
- 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/crud.rb +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/delete_statement.rb +4 -2
- 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/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +7 -3
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +31 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +16 -10
|
@@ -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,22 @@ 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
|
+
if connected?
|
|
117
|
+
@lock.synchronize do
|
|
118
|
+
if @raw_connection&.ping
|
|
119
|
+
verified!
|
|
120
|
+
true
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end || false
|
|
135
124
|
end
|
|
136
125
|
|
|
137
126
|
alias :reset! :reconnect!
|
|
@@ -180,7 +169,7 @@ module ActiveRecord
|
|
|
180
169
|
end
|
|
181
170
|
|
|
182
171
|
def full_version
|
|
183
|
-
|
|
172
|
+
database_version.full_version_string
|
|
184
173
|
end
|
|
185
174
|
|
|
186
175
|
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
|
|
@@ -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)
|
|
@@ -229,6 +229,8 @@ module ActiveRecord
|
|
|
229
229
|
end
|
|
230
230
|
|
|
231
231
|
def defined_for?(name: nil, column: nil, **options)
|
|
232
|
+
options = options.slice(*self.options.keys)
|
|
233
|
+
|
|
232
234
|
(name.nil? || self.name == name.to_s) &&
|
|
233
235
|
(column.nil? || Array(self.column) == Array(column).map(&:to_s)) &&
|
|
234
236
|
options.all? { |k, v| self.options[k].to_s == v.to_s }
|
|
@@ -302,8 +304,8 @@ module ActiveRecord
|
|
|
302
304
|
# t.exclusion_constraint("price WITH =, availability_range WITH &&", using: :gist, name: "price_check")
|
|
303
305
|
#
|
|
304
306
|
# See {connection.add_exclusion_constraint}[rdoc-ref:SchemaStatements#add_exclusion_constraint]
|
|
305
|
-
def exclusion_constraint(
|
|
306
|
-
@base.add_exclusion_constraint(name,
|
|
307
|
+
def exclusion_constraint(...)
|
|
308
|
+
@base.add_exclusion_constraint(name, ...)
|
|
307
309
|
end
|
|
308
310
|
|
|
309
311
|
# Removes the given exclusion constraint from the table.
|
|
@@ -311,8 +313,8 @@ module ActiveRecord
|
|
|
311
313
|
# t.remove_exclusion_constraint(name: "price_check")
|
|
312
314
|
#
|
|
313
315
|
# See {connection.remove_exclusion_constraint}[rdoc-ref:SchemaStatements#remove_exclusion_constraint]
|
|
314
|
-
def remove_exclusion_constraint(
|
|
315
|
-
@base.remove_exclusion_constraint(name,
|
|
316
|
+
def remove_exclusion_constraint(...)
|
|
317
|
+
@base.remove_exclusion_constraint(name, ...)
|
|
316
318
|
end
|
|
317
319
|
|
|
318
320
|
# Adds a unique constraint.
|
|
@@ -320,8 +322,8 @@ module ActiveRecord
|
|
|
320
322
|
# t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred)
|
|
321
323
|
#
|
|
322
324
|
# See {connection.add_unique_constraint}[rdoc-ref:SchemaStatements#add_unique_constraint]
|
|
323
|
-
def unique_constraint(
|
|
324
|
-
@base.add_unique_constraint(name,
|
|
325
|
+
def unique_constraint(...)
|
|
326
|
+
@base.add_unique_constraint(name, ...)
|
|
325
327
|
end
|
|
326
328
|
|
|
327
329
|
# Removes the given unique constraint from the table.
|
|
@@ -329,8 +331,28 @@ module ActiveRecord
|
|
|
329
331
|
# t.remove_unique_constraint(name: "unique_position")
|
|
330
332
|
#
|
|
331
333
|
# See {connection.remove_unique_constraint}[rdoc-ref:SchemaStatements#remove_unique_constraint]
|
|
332
|
-
def remove_unique_constraint(
|
|
333
|
-
@base.remove_unique_constraint(name,
|
|
334
|
+
def remove_unique_constraint(...)
|
|
335
|
+
@base.remove_unique_constraint(name, ...)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Validates the given constraint on the table.
|
|
339
|
+
#
|
|
340
|
+
# t.check_constraint("price > 0", name: "price_check", validate: false)
|
|
341
|
+
# t.validate_constraint "price_check"
|
|
342
|
+
#
|
|
343
|
+
# See {connection.validate_constraint}[rdoc-ref:SchemaStatements#validate_constraint]
|
|
344
|
+
def validate_constraint(...)
|
|
345
|
+
@base.validate_constraint(name, ...)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Validates the given check constraint on the table
|
|
349
|
+
#
|
|
350
|
+
# t.check_constraint("price > 0", name: "price_check", validate: false)
|
|
351
|
+
# t.validate_check_constraint name: "price_check"
|
|
352
|
+
#
|
|
353
|
+
# See {connection.validate_check_constraint}[rdoc-ref:SchemaStatements#validate_check_constraint]
|
|
354
|
+
def validate_check_constraint(...)
|
|
355
|
+
@base.validate_check_constraint(name, ...)
|
|
334
356
|
end
|
|
335
357
|
end
|
|
336
358
|
|
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
|
22
22
|
stream.puts " # Custom types defined in this database."
|
|
23
23
|
stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
|
|
24
24
|
types.sort.each do |name, values|
|
|
25
|
-
stream.puts " create_enum #{name.inspect}, #{values.
|
|
25
|
+
stream.puts " create_enum #{name.inspect}, #{values.inspect}"
|
|
26
26
|
end
|
|
27
27
|
stream.puts
|
|
28
28
|
end
|
|
@@ -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.
|
|
@@ -524,14 +532,6 @@ module ActiveRecord
|
|
|
524
532
|
end
|
|
525
533
|
|
|
526
534
|
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
535
|
assert_valid_deferrable(options[:deferrable])
|
|
536
536
|
|
|
537
537
|
super
|
|
@@ -692,6 +692,10 @@ module ActiveRecord
|
|
|
692
692
|
# The constraint name. Defaults to <tt>excl_rails_<identifier></tt>.
|
|
693
693
|
# [<tt>:deferrable</tt>]
|
|
694
694
|
# 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+.
|
|
695
|
+
# [<tt>:using</tt>]
|
|
696
|
+
# Specify which index method to use when creating this exclusion constraint (e.g. +:btree+, +:gist+ etc).
|
|
697
|
+
# [<tt>:where</tt>]
|
|
698
|
+
# Specify an exclusion constraint on a subset of the table (internally PostgreSQL creates a partial index for this).
|
|
695
699
|
def add_exclusion_constraint(table_name, expression, **options)
|
|
696
700
|
options = exclusion_constraint_options(table_name, expression, options)
|
|
697
701
|
at = create_alter_table(table_name)
|
|
@@ -875,7 +879,7 @@ module ActiveRecord
|
|
|
875
879
|
#
|
|
876
880
|
# validate_check_constraint :products, name: "price_check"
|
|
877
881
|
#
|
|
878
|
-
# The +options+ hash accepts the same keys as add_check_constraint[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
|
882
|
+
# The +options+ hash accepts the same keys as {add_check_constraint}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
|
879
883
|
def validate_check_constraint(table_name, **options)
|
|
880
884
|
chk_name_to_validate = check_constraint_for!(table_name, **options).name
|
|
881
885
|
|