activerecord 7.0.8.7 → 7.1.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 +1339 -1572
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +16 -11
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader.rb +12 -9
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +193 -97
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +71 -94
- data/lib/active_record/core.rb +128 -138
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +8 -3
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -26
- data/lib/active_record/errors.rb +89 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +119 -71
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +5 -7
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -5
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +213 -109
- data/lib/active_record/model_schema.rb +47 -27
- data/lib/active_record/nested_attributes.rb +28 -3
- data/lib/active_record/normalization.rb +158 -0
- data/lib/active_record/persistence.rb +183 -33
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- data/lib/active_record/railties/databases.rake +139 -145
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +169 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +152 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- data/lib/active_record/relation/finder_methods.rb +85 -15
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +351 -62
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +41 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +52 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -21,28 +21,19 @@ require "active_record/connection_adapters/postgresql/utils"
|
|
21
21
|
|
22
22
|
module ActiveRecord
|
23
23
|
module ConnectionHandling # :nodoc:
|
24
|
+
def postgresql_adapter_class
|
25
|
+
ConnectionAdapters::PostgreSQLAdapter
|
26
|
+
end
|
27
|
+
|
24
28
|
# Establishes a connection to the database that's used by all Active Record objects
|
25
29
|
def postgresql_connection(config)
|
26
|
-
|
27
|
-
|
28
|
-
# Map ActiveRecords param names to PGs.
|
29
|
-
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
30
|
-
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
31
|
-
|
32
|
-
# Forward only valid config params to PG::Connection.connect.
|
33
|
-
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
34
|
-
conn_params.slice!(*valid_conn_param_keys)
|
35
|
-
|
36
|
-
ConnectionAdapters::PostgreSQLAdapter.new(
|
37
|
-
ConnectionAdapters::PostgreSQLAdapter.new_client(conn_params),
|
38
|
-
logger,
|
39
|
-
conn_params,
|
40
|
-
config,
|
41
|
-
)
|
30
|
+
postgresql_adapter_class.new(config)
|
42
31
|
end
|
43
32
|
end
|
44
33
|
|
45
34
|
module ConnectionAdapters
|
35
|
+
# = Active Record PostgreSQL Adapter
|
36
|
+
#
|
46
37
|
# The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
|
47
38
|
#
|
48
39
|
# Options:
|
@@ -77,16 +68,37 @@ module ActiveRecord
|
|
77
68
|
def new_client(conn_params)
|
78
69
|
PG.connect(**conn_params)
|
79
70
|
rescue ::PG::Error => error
|
80
|
-
if conn_params && conn_params[:dbname]
|
71
|
+
if conn_params && conn_params[:dbname] == "postgres"
|
72
|
+
raise ActiveRecord::ConnectionNotEstablished, error.message
|
73
|
+
elsif conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
|
81
74
|
raise ActiveRecord::NoDatabaseError.db_error(conn_params[:dbname])
|
82
75
|
elsif conn_params && conn_params[:user] && error.message.include?(conn_params[:user])
|
83
76
|
raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:user])
|
84
|
-
elsif conn_params && conn_params[:
|
85
|
-
raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:
|
77
|
+
elsif conn_params && conn_params[:host] && error.message.include?(conn_params[:host])
|
78
|
+
raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:host])
|
86
79
|
else
|
87
80
|
raise ActiveRecord::ConnectionNotEstablished, error.message
|
88
81
|
end
|
89
82
|
end
|
83
|
+
|
84
|
+
def dbconsole(config, options = {})
|
85
|
+
pg_config = config.configuration_hash
|
86
|
+
|
87
|
+
ENV["PGUSER"] = pg_config[:username] if pg_config[:username]
|
88
|
+
ENV["PGHOST"] = pg_config[:host] if pg_config[:host]
|
89
|
+
ENV["PGPORT"] = pg_config[:port].to_s if pg_config[:port]
|
90
|
+
ENV["PGPASSWORD"] = pg_config[:password].to_s if pg_config[:password] && options[:include_password]
|
91
|
+
ENV["PGSSLMODE"] = pg_config[:sslmode].to_s if pg_config[:sslmode]
|
92
|
+
ENV["PGSSLCERT"] = pg_config[:sslcert].to_s if pg_config[:sslcert]
|
93
|
+
ENV["PGSSLKEY"] = pg_config[:sslkey].to_s if pg_config[:sslkey]
|
94
|
+
ENV["PGSSLROOTCERT"] = pg_config[:sslrootcert].to_s if pg_config[:sslrootcert]
|
95
|
+
if pg_config[:variables]
|
96
|
+
ENV["PGOPTIONS"] = pg_config[:variables].filter_map do |name, value|
|
97
|
+
"-c #{name}=#{value.to_s.gsub(/[ \\]/, '\\\\\0')}" unless value == ":default" || value == :default
|
98
|
+
end.join(" ")
|
99
|
+
end
|
100
|
+
find_cmd_and_exec("psql", config.database)
|
101
|
+
end
|
90
102
|
end
|
91
103
|
|
92
104
|
##
|
@@ -105,7 +117,7 @@ module ActiveRecord
|
|
105
117
|
##
|
106
118
|
# :singleton-method:
|
107
119
|
# PostgreSQL supports multiple types for DateTimes. By default, if you use +datetime+
|
108
|
-
# in migrations, Rails will translate this to a PostgreSQL "timestamp without time zone".
|
120
|
+
# in migrations, \Rails will translate this to a PostgreSQL "timestamp without time zone".
|
109
121
|
# Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
|
110
122
|
# store DateTimes as "timestamp with time zone":
|
111
123
|
#
|
@@ -183,13 +195,17 @@ module ActiveRecord
|
|
183
195
|
end
|
184
196
|
|
185
197
|
def supports_partitioned_indexes?
|
186
|
-
database_version >=
|
198
|
+
database_version >= 11_00_00 # >= 11.0
|
187
199
|
end
|
188
200
|
|
189
201
|
def supports_partial_index?
|
190
202
|
true
|
191
203
|
end
|
192
204
|
|
205
|
+
def supports_index_include?
|
206
|
+
database_version >= 11_00_00 # >= 11.0
|
207
|
+
end
|
208
|
+
|
193
209
|
def supports_expression_index?
|
194
210
|
true
|
195
211
|
end
|
@@ -206,6 +222,14 @@ module ActiveRecord
|
|
206
222
|
true
|
207
223
|
end
|
208
224
|
|
225
|
+
def supports_exclusion_constraints?
|
226
|
+
true
|
227
|
+
end
|
228
|
+
|
229
|
+
def supports_unique_keys?
|
230
|
+
true
|
231
|
+
end
|
232
|
+
|
209
233
|
def supports_validate_constraints?
|
210
234
|
true
|
211
235
|
end
|
@@ -234,25 +258,37 @@ module ActiveRecord
|
|
234
258
|
true
|
235
259
|
end
|
236
260
|
|
261
|
+
def supports_restart_db_transaction?
|
262
|
+
database_version >= 12_00_00 # >= 12.0
|
263
|
+
end
|
264
|
+
|
237
265
|
def supports_insert_returning?
|
238
266
|
true
|
239
267
|
end
|
240
268
|
|
241
269
|
def supports_insert_on_conflict?
|
242
|
-
database_version >=
|
270
|
+
database_version >= 9_05_00 # >= 9.5
|
243
271
|
end
|
244
272
|
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
245
273
|
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
246
274
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
247
275
|
|
248
276
|
def supports_virtual_columns?
|
249
|
-
database_version >=
|
277
|
+
database_version >= 12_00_00 # >= 12.0
|
278
|
+
end
|
279
|
+
|
280
|
+
def supports_nulls_not_distinct?
|
281
|
+
database_version >= 15_00_00 # >= 15.0
|
250
282
|
end
|
251
283
|
|
252
284
|
def index_algorithms
|
253
285
|
{ concurrently: "CONCURRENTLY" }
|
254
286
|
end
|
255
287
|
|
288
|
+
def return_value_after_insert?(column) # :nodoc:
|
289
|
+
column.auto_populated?
|
290
|
+
end
|
291
|
+
|
256
292
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
257
293
|
def initialize(connection, max)
|
258
294
|
super(max)
|
@@ -266,47 +302,46 @@ module ActiveRecord
|
|
266
302
|
|
267
303
|
private
|
268
304
|
def dealloc(key)
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
305
|
+
# This is ugly, but safe: the statement pool is only
|
306
|
+
# accessed while holding the connection's lock. (And we
|
307
|
+
# don't need the complication of with_raw_connection because
|
308
|
+
# a reconnect would invalidate the entire statement pool.)
|
309
|
+
if conn = @connection.instance_variable_get(:@raw_connection)
|
310
|
+
conn.query "DEALLOCATE #{key}" if conn.status == PG::CONNECTION_OK
|
311
|
+
end
|
275
312
|
rescue PG::Error
|
276
|
-
false
|
277
313
|
end
|
278
314
|
end
|
279
315
|
|
280
316
|
# Initializes and connects a PostgreSQL adapter.
|
281
|
-
def initialize(
|
282
|
-
super
|
317
|
+
def initialize(...)
|
318
|
+
super
|
283
319
|
|
284
|
-
|
320
|
+
conn_params = @config.compact
|
285
321
|
|
286
|
-
#
|
287
|
-
|
288
|
-
|
322
|
+
# Map ActiveRecords param names to PGs.
|
323
|
+
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
324
|
+
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
289
325
|
|
290
|
-
|
291
|
-
|
292
|
-
|
326
|
+
# Forward only valid config params to PG::Connection.connect.
|
327
|
+
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
328
|
+
conn_params.slice!(*valid_conn_param_keys)
|
293
329
|
|
294
|
-
@
|
295
|
-
initialize_type_map
|
296
|
-
@local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
|
297
|
-
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
298
|
-
end
|
330
|
+
@connection_parameters = conn_params
|
299
331
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
332
|
+
@max_identifier_length = nil
|
333
|
+
@type_map = nil
|
334
|
+
@raw_connection = nil
|
335
|
+
@notice_receiver_sql_warnings = []
|
336
|
+
|
337
|
+
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
304
338
|
end
|
305
339
|
|
306
340
|
# Is this connection alive and ready for queries?
|
307
341
|
def active?
|
308
342
|
@lock.synchronize do
|
309
|
-
@
|
343
|
+
return false unless @raw_connection
|
344
|
+
@raw_connection.query ";"
|
310
345
|
end
|
311
346
|
true
|
312
347
|
rescue PG::Error
|
@@ -314,31 +349,27 @@ module ActiveRecord
|
|
314
349
|
end
|
315
350
|
|
316
351
|
def reload_type_map # :nodoc:
|
317
|
-
type_map.clear
|
318
|
-
initialize_type_map
|
319
|
-
end
|
320
|
-
|
321
|
-
# Close then reopen the connection.
|
322
|
-
def reconnect!
|
323
352
|
@lock.synchronize do
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
353
|
+
if @type_map
|
354
|
+
type_map.clear
|
355
|
+
else
|
356
|
+
@type_map = Type::HashLookupTypeMap.new
|
357
|
+
end
|
358
|
+
|
359
|
+
initialize_type_map
|
330
360
|
end
|
331
361
|
end
|
332
362
|
|
333
363
|
def reset!
|
334
364
|
@lock.synchronize do
|
335
|
-
|
336
|
-
|
337
|
-
unless @
|
338
|
-
@
|
365
|
+
return connect! unless @raw_connection
|
366
|
+
|
367
|
+
unless @raw_connection.transaction_status == ::PG::PQTRANS_IDLE
|
368
|
+
@raw_connection.query "ROLLBACK"
|
339
369
|
end
|
340
|
-
@
|
341
|
-
|
370
|
+
@raw_connection.query "DISCARD ALL"
|
371
|
+
|
372
|
+
super
|
342
373
|
end
|
343
374
|
end
|
344
375
|
|
@@ -347,14 +378,15 @@ module ActiveRecord
|
|
347
378
|
def disconnect!
|
348
379
|
@lock.synchronize do
|
349
380
|
super
|
350
|
-
@
|
381
|
+
@raw_connection&.close rescue nil
|
382
|
+
@raw_connection = nil
|
351
383
|
end
|
352
384
|
end
|
353
385
|
|
354
386
|
def discard! # :nodoc:
|
355
387
|
super
|
356
|
-
@
|
357
|
-
@
|
388
|
+
@raw_connection&.socket_io&.reopen(IO::NULL) rescue nil
|
389
|
+
@raw_connection = nil
|
358
390
|
end
|
359
391
|
|
360
392
|
def native_database_types # :nodoc:
|
@@ -370,7 +402,7 @@ module ActiveRecord
|
|
370
402
|
end
|
371
403
|
|
372
404
|
def set_standard_conforming_strings
|
373
|
-
|
405
|
+
internal_execute("SET standard_conforming_strings = on")
|
374
406
|
end
|
375
407
|
|
376
408
|
def supports_ddl_transactions?
|
@@ -398,7 +430,7 @@ module ActiveRecord
|
|
398
430
|
end
|
399
431
|
|
400
432
|
def supports_pgcrypto_uuid?
|
401
|
-
database_version >=
|
433
|
+
database_version >= 9_04_00 # >= 9.4
|
402
434
|
end
|
403
435
|
|
404
436
|
def supports_optimizer_hints?
|
@@ -430,14 +462,21 @@ module ActiveRecord
|
|
430
462
|
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
431
463
|
end
|
432
464
|
|
433
|
-
def enable_extension(name)
|
434
|
-
|
435
|
-
|
436
|
-
}
|
465
|
+
def enable_extension(name, **)
|
466
|
+
schema, name = name.to_s.split(".").values_at(-2, -1)
|
467
|
+
sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
|
468
|
+
sql << " SCHEMA #{schema}" if schema
|
469
|
+
|
470
|
+
internal_exec_query(sql).tap { reload_type_map }
|
437
471
|
end
|
438
472
|
|
439
|
-
|
440
|
-
|
473
|
+
# Removes an extension from the database.
|
474
|
+
#
|
475
|
+
# [<tt>:force</tt>]
|
476
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
477
|
+
# Defaults to false.
|
478
|
+
def disable_extension(name, force: false)
|
479
|
+
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
|
441
480
|
reload_type_map
|
442
481
|
}
|
443
482
|
end
|
@@ -451,7 +490,7 @@ module ActiveRecord
|
|
451
490
|
end
|
452
491
|
|
453
492
|
def extensions
|
454
|
-
|
493
|
+
internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
|
455
494
|
end
|
456
495
|
|
457
496
|
# Returns a list of defined enum types, and their values.
|
@@ -459,31 +498,97 @@ module ActiveRecord
|
|
459
498
|
query = <<~SQL
|
460
499
|
SELECT
|
461
500
|
type.typname AS name,
|
501
|
+
type.OID AS oid,
|
502
|
+
n.nspname AS schema,
|
462
503
|
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
|
463
504
|
FROM pg_enum AS enum
|
464
|
-
JOIN pg_type AS type
|
465
|
-
|
466
|
-
|
505
|
+
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
506
|
+
JOIN pg_namespace n ON type.typnamespace = n.oid
|
507
|
+
WHERE n.nspname = ANY (current_schemas(false))
|
508
|
+
GROUP BY type.OID, n.nspname, type.typname;
|
467
509
|
SQL
|
468
|
-
|
510
|
+
|
511
|
+
internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
|
512
|
+
name, schema = row[0], row[2]
|
513
|
+
schema = nil if schema == current_schema
|
514
|
+
full_name = [schema, name].compact.join(".")
|
515
|
+
memo[full_name] = row.last
|
516
|
+
end.to_a
|
469
517
|
end
|
470
518
|
|
471
519
|
# Given a name and an array of values, creates an enum type.
|
472
|
-
def create_enum(name, values)
|
473
|
-
sql_values = values.map { |s|
|
520
|
+
def create_enum(name, values, **options)
|
521
|
+
sql_values = values.map { |s| quote(s) }.join(", ")
|
522
|
+
scope = quoted_scope(name)
|
474
523
|
query = <<~SQL
|
475
524
|
DO $$
|
476
525
|
BEGIN
|
477
526
|
IF NOT EXISTS (
|
478
|
-
SELECT 1
|
479
|
-
|
527
|
+
SELECT 1
|
528
|
+
FROM pg_type t
|
529
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
530
|
+
WHERE t.typname = #{scope[:name]}
|
531
|
+
AND n.nspname = #{scope[:schema]}
|
480
532
|
) THEN
|
481
|
-
CREATE TYPE
|
533
|
+
CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
|
482
534
|
END IF;
|
483
535
|
END
|
484
536
|
$$;
|
485
537
|
SQL
|
486
|
-
|
538
|
+
internal_exec_query(query)
|
539
|
+
end
|
540
|
+
|
541
|
+
# Drops an enum type.
|
542
|
+
#
|
543
|
+
# If the <tt>if_exists: true</tt> option is provided, the enum is dropped
|
544
|
+
# only if it exists. Otherwise, if the enum doesn't exist, an error is
|
545
|
+
# raised.
|
546
|
+
#
|
547
|
+
# The +values+ parameter will be ignored if present. It can be helpful
|
548
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
549
|
+
# In that case, +values+ will be used by #create_enum.
|
550
|
+
def drop_enum(name, values = nil, **options)
|
551
|
+
query = <<~SQL
|
552
|
+
DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
|
553
|
+
SQL
|
554
|
+
internal_exec_query(query)
|
555
|
+
end
|
556
|
+
|
557
|
+
# Rename an existing enum type to something else.
|
558
|
+
def rename_enum(name, options = {})
|
559
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
560
|
+
|
561
|
+
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
|
562
|
+
end
|
563
|
+
|
564
|
+
# Add enum value to an existing enum type.
|
565
|
+
def add_enum_value(type_name, value, options = {})
|
566
|
+
before, after = options.values_at(:before, :after)
|
567
|
+
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
|
568
|
+
|
569
|
+
if before && after
|
570
|
+
raise ArgumentError, "Cannot have both :before and :after at the same time"
|
571
|
+
elsif before
|
572
|
+
sql << " BEFORE '#{before}'"
|
573
|
+
elsif after
|
574
|
+
sql << " AFTER '#{after}'"
|
575
|
+
end
|
576
|
+
|
577
|
+
execute(sql).tap { reload_type_map }
|
578
|
+
end
|
579
|
+
|
580
|
+
# Rename enum value on an existing enum type.
|
581
|
+
def rename_enum_value(type_name, options = {})
|
582
|
+
unless database_version >= 10_00_00 # >= 10.0
|
583
|
+
raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
|
584
|
+
end
|
585
|
+
|
586
|
+
from = options.fetch(:from) { raise ArgumentError, ":from is required" }
|
587
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
588
|
+
|
589
|
+
execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
|
590
|
+
reload_type_map
|
591
|
+
}
|
487
592
|
end
|
488
593
|
|
489
594
|
# Returns the configured supported identifier length supported by PostgreSQL
|
@@ -491,10 +596,18 @@ module ActiveRecord
|
|
491
596
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
492
597
|
end
|
493
598
|
|
599
|
+
# Returns the maximum length of a table name.
|
600
|
+
def table_name_length
|
601
|
+
# PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
|
602
|
+
# truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
|
603
|
+
# We allow smaller table names to be able to correctly rename this index when renaming the table.
|
604
|
+
max_identifier_length - "_pkey".length
|
605
|
+
end
|
606
|
+
|
494
607
|
# Set the authorized user for this session
|
495
608
|
def session_auth=(user)
|
496
609
|
clear_cache!
|
497
|
-
|
610
|
+
internal_execute("SET SESSION AUTHORIZATION #{user}", nil, materialize_transactions: true)
|
498
611
|
end
|
499
612
|
|
500
613
|
def use_insert_returning?
|
@@ -503,7 +616,7 @@ module ActiveRecord
|
|
503
616
|
|
504
617
|
# Returns the version of the connected PostgreSQL server.
|
505
618
|
def get_database_version # :nodoc:
|
506
|
-
|
619
|
+
valid_raw_connection.server_version
|
507
620
|
end
|
508
621
|
alias :postgresql_version :database_version
|
509
622
|
|
@@ -531,7 +644,7 @@ module ActiveRecord
|
|
531
644
|
end
|
532
645
|
|
533
646
|
def check_version # :nodoc:
|
534
|
-
if database_version <
|
647
|
+
if database_version < 9_03_00 # < 9.3
|
535
648
|
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
536
649
|
end
|
537
650
|
end
|
@@ -575,10 +688,6 @@ module ActiveRecord
|
|
575
688
|
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
576
689
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
577
690
|
|
578
|
-
register_class_with_precision m, "time", Type::Time
|
579
|
-
register_class_with_precision m, "timestamp", OID::Timestamp
|
580
|
-
register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
|
581
|
-
|
582
691
|
m.register_type "numeric" do |_, fmod, sql_type|
|
583
692
|
precision = extract_precision(sql_type)
|
584
693
|
scale = extract_scale(sql_type)
|
@@ -613,6 +722,11 @@ module ActiveRecord
|
|
613
722
|
|
614
723
|
def initialize_type_map(m = type_map)
|
615
724
|
self.class.initialize_type_map(m)
|
725
|
+
|
726
|
+
self.class.register_class_with_precision m, "time", Type::Time, timezone: @default_timezone
|
727
|
+
self.class.register_class_with_precision m, "timestamp", OID::Timestamp, timezone: @default_timezone
|
728
|
+
self.class.register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
|
729
|
+
|
616
730
|
load_additional_types
|
617
731
|
end
|
618
732
|
|
@@ -669,35 +783,53 @@ module ActiveRecord
|
|
669
783
|
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
670
784
|
when nil
|
671
785
|
if exception.message.match?(/connection is closed/i)
|
672
|
-
ConnectionNotEstablished.new(exception)
|
786
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
787
|
+
elsif exception.is_a?(PG::ConnectionBad)
|
788
|
+
# libpq message style always ends with a newline; the pg gem's internal
|
789
|
+
# errors do not. We separate these cases because a pg-internal
|
790
|
+
# ConnectionBad means it failed before it managed to send the query,
|
791
|
+
# whereas a libpq failure could have occurred at any time (meaning the
|
792
|
+
# server may have already executed part or all of the query).
|
793
|
+
if exception.message.end_with?("\n")
|
794
|
+
ConnectionFailed.new(exception, connection_pool: @pool)
|
795
|
+
else
|
796
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
797
|
+
end
|
673
798
|
else
|
674
799
|
super
|
675
800
|
end
|
676
801
|
when UNIQUE_VIOLATION
|
677
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
802
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
678
803
|
when FOREIGN_KEY_VIOLATION
|
679
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
804
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
680
805
|
when VALUE_LIMIT_VIOLATION
|
681
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
806
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
682
807
|
when NUMERIC_VALUE_OUT_OF_RANGE
|
683
|
-
RangeError.new(message, sql: sql, binds: binds)
|
808
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
684
809
|
when NOT_NULL_VIOLATION
|
685
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
810
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
686
811
|
when SERIALIZATION_FAILURE
|
687
|
-
SerializationFailure.new(message, sql: sql, binds: binds)
|
812
|
+
SerializationFailure.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
688
813
|
when DEADLOCK_DETECTED
|
689
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
814
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
690
815
|
when DUPLICATE_DATABASE
|
691
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
816
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
692
817
|
when LOCK_NOT_AVAILABLE
|
693
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
818
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
694
819
|
when QUERY_CANCELED
|
695
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
820
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
696
821
|
else
|
697
822
|
super
|
698
823
|
end
|
699
824
|
end
|
700
825
|
|
826
|
+
def retryable_query_error?(exception)
|
827
|
+
# We cannot retry anything if we're inside a broken transaction; we need to at
|
828
|
+
# least raise until the innermost savepoint is rolled back
|
829
|
+
@raw_connection&.transaction_status != ::PG::PQTRANS_INERROR &&
|
830
|
+
super
|
831
|
+
end
|
832
|
+
|
701
833
|
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
702
834
|
if !type_map.key?(oid)
|
703
835
|
load_additional_types([oid])
|
@@ -714,7 +846,7 @@ module ActiveRecord
|
|
714
846
|
def load_additional_types(oids = nil)
|
715
847
|
initializer = OID::TypeMapInitializer.new(type_map)
|
716
848
|
load_types_queries(initializer, oids) do |query|
|
717
|
-
execute_and_clear(query, "SCHEMA", []) do |records|
|
849
|
+
execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
|
718
850
|
initializer.run(records)
|
719
851
|
end
|
720
852
|
end
|
@@ -737,14 +869,14 @@ module ActiveRecord
|
|
737
869
|
|
738
870
|
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
739
871
|
|
740
|
-
def execute_and_clear(sql, name, binds, prepare: false, async: false)
|
872
|
+
def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
|
741
873
|
sql = transform_query(sql)
|
742
874
|
check_if_write_query(sql)
|
743
875
|
|
744
876
|
if !prepare || without_prepared_statement?(binds)
|
745
|
-
result = exec_no_cache(sql, name, binds, async: async)
|
877
|
+
result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
746
878
|
else
|
747
|
-
result = exec_cache(sql, name, binds, async: async)
|
879
|
+
result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
748
880
|
end
|
749
881
|
begin
|
750
882
|
ret = yield result
|
@@ -754,8 +886,7 @@ module ActiveRecord
|
|
754
886
|
ret
|
755
887
|
end
|
756
888
|
|
757
|
-
def exec_no_cache(sql, name, binds, async:
|
758
|
-
materialize_transactions
|
889
|
+
def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
759
890
|
mark_transaction_written_if_write(sql)
|
760
891
|
|
761
892
|
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
@@ -764,23 +895,23 @@ module ActiveRecord
|
|
764
895
|
|
765
896
|
type_casted_binds = type_casted_binds(binds)
|
766
897
|
log(sql, name, binds, type_casted_binds, async: async) do
|
767
|
-
|
768
|
-
|
898
|
+
with_raw_connection do |conn|
|
899
|
+
conn.exec_params(sql, type_casted_binds)
|
769
900
|
end
|
770
901
|
end
|
771
902
|
end
|
772
903
|
|
773
|
-
def exec_cache(sql, name, binds, async:
|
774
|
-
materialize_transactions
|
904
|
+
def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
775
905
|
mark_transaction_written_if_write(sql)
|
906
|
+
|
776
907
|
update_typemap_for_default_timezone
|
777
908
|
|
778
909
|
stmt_key = prepare_statement(sql, binds)
|
779
910
|
type_casted_binds = type_casted_binds(binds)
|
780
911
|
|
781
|
-
|
782
|
-
|
783
|
-
|
912
|
+
with_raw_connection do |conn|
|
913
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
914
|
+
conn.exec_prepared(stmt_key, type_casted_binds)
|
784
915
|
end
|
785
916
|
end
|
786
917
|
rescue ActiveRecord::StatementInvalid => e
|
@@ -829,17 +960,17 @@ module ActiveRecord
|
|
829
960
|
# Prepare the statement if it hasn't been prepared, return
|
830
961
|
# the statement key.
|
831
962
|
def prepare_statement(sql, binds)
|
832
|
-
|
963
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
833
964
|
sql_key = sql_key(sql)
|
834
965
|
unless @statements.key? sql_key
|
835
966
|
nextkey = @statements.next_key
|
836
967
|
begin
|
837
|
-
|
968
|
+
conn.prepare nextkey, sql
|
838
969
|
rescue => e
|
839
970
|
raise translate_exception_class(e, sql, binds)
|
840
971
|
end
|
841
972
|
# Clear the queue
|
842
|
-
|
973
|
+
conn.get_last_result
|
843
974
|
@statements[sql_key] = nextkey
|
844
975
|
end
|
845
976
|
@statements[sql_key]
|
@@ -849,49 +980,79 @@ module ActiveRecord
|
|
849
980
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
850
981
|
# connected server's characteristics.
|
851
982
|
def connect
|
852
|
-
@
|
853
|
-
|
854
|
-
|
855
|
-
|
983
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
984
|
+
rescue ConnectionNotEstablished => ex
|
985
|
+
raise ex.set_pool(@pool)
|
986
|
+
end
|
987
|
+
|
988
|
+
def reconnect
|
989
|
+
begin
|
990
|
+
@raw_connection&.reset
|
991
|
+
rescue PG::ConnectionBad
|
992
|
+
@raw_connection = nil
|
993
|
+
end
|
994
|
+
|
995
|
+
connect unless @raw_connection
|
856
996
|
end
|
857
997
|
|
858
998
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
859
999
|
# This is called by #connect and should not be called manually.
|
860
1000
|
def configure_connection
|
861
1001
|
if @config[:encoding]
|
862
|
-
@
|
1002
|
+
@raw_connection.set_client_encoding(@config[:encoding])
|
863
1003
|
end
|
864
1004
|
self.client_min_messages = @config[:min_messages] || "warning"
|
865
1005
|
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
|
866
1006
|
|
1007
|
+
unless ActiveRecord.db_warnings_action.nil?
|
1008
|
+
@raw_connection.set_notice_receiver do |result|
|
1009
|
+
message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY)
|
1010
|
+
code = result.error_field(PG::Result::PG_DIAG_SQLSTATE)
|
1011
|
+
level = result.error_field(PG::Result::PG_DIAG_SEVERITY)
|
1012
|
+
@notice_receiver_sql_warnings << SQLWarning.new(message, code, level, nil, @pool)
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
|
867
1016
|
# Use standard-conforming strings so we don't have to do the E'...' dance.
|
868
1017
|
set_standard_conforming_strings
|
869
1018
|
|
870
1019
|
variables = @config.fetch(:variables, {}).stringify_keys
|
871
1020
|
|
872
|
-
# If using Active Record's time zone support configure the connection to return
|
873
|
-
# TIMESTAMP WITH ZONE types in UTC.
|
874
|
-
unless variables["timezone"]
|
875
|
-
if ActiveRecord.default_timezone == :utc
|
876
|
-
variables["timezone"] = "UTC"
|
877
|
-
elsif @local_tz
|
878
|
-
variables["timezone"] = @local_tz
|
879
|
-
end
|
880
|
-
end
|
881
|
-
|
882
1021
|
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
883
|
-
|
1022
|
+
internal_execute("SET intervalstyle = iso_8601")
|
884
1023
|
|
885
1024
|
# SET statements from :variables config hash
|
886
1025
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
887
1026
|
variables.map do |k, v|
|
888
1027
|
if v == ":default" || v == :default
|
889
1028
|
# Sets the value to the global or compile default
|
890
|
-
|
1029
|
+
internal_execute("SET SESSION #{k} TO DEFAULT")
|
891
1030
|
elsif !v.nil?
|
892
|
-
|
1031
|
+
internal_execute("SET SESSION #{k} TO #{quote(v)}")
|
893
1032
|
end
|
894
1033
|
end
|
1034
|
+
|
1035
|
+
add_pg_encoders
|
1036
|
+
add_pg_decoders
|
1037
|
+
|
1038
|
+
reload_type_map
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
def reconfigure_connection_timezone
|
1042
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
1043
|
+
|
1044
|
+
# If it's been directly configured as a connection variable, we don't
|
1045
|
+
# need to do anything here; it will be set up by configure_connection
|
1046
|
+
# and then never changed.
|
1047
|
+
return if variables["timezone"]
|
1048
|
+
|
1049
|
+
# If using Active Record's time zone support configure the connection
|
1050
|
+
# to return TIMESTAMP WITH ZONE types in UTC.
|
1051
|
+
if default_timezone == :utc
|
1052
|
+
internal_execute("SET SESSION timezone TO 'UTC'")
|
1053
|
+
else
|
1054
|
+
internal_execute("SET SESSION timezone TO DEFAULT")
|
1055
|
+
end
|
895
1056
|
end
|
896
1057
|
|
897
1058
|
# Returns the list of a table's column names, data types, and default values.
|
@@ -938,27 +1099,32 @@ module ActiveRecord
|
|
938
1099
|
end
|
939
1100
|
|
940
1101
|
def build_statement_pool
|
941
|
-
StatementPool.new(
|
1102
|
+
StatementPool.new(self, self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
942
1103
|
end
|
943
1104
|
|
944
1105
|
def can_perform_case_insensitive_comparison_for?(column)
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
1106
|
+
# NOTE: citext is an exception. It is possible to perform a
|
1107
|
+
# case-insensitive comparison using `LOWER()`, but it is
|
1108
|
+
# unnecessary, as `citext` is case-insensitive by definition.
|
1109
|
+
@case_insensitive_cache ||= { "citext" => false }
|
1110
|
+
@case_insensitive_cache.fetch(column.sql_type) do
|
1111
|
+
@case_insensitive_cache[column.sql_type] = begin
|
1112
|
+
sql = <<~SQL
|
1113
|
+
SELECT exists(
|
1114
|
+
SELECT * FROM pg_proc
|
1115
|
+
WHERE proname = 'lower'
|
1116
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
1117
|
+
) OR exists(
|
1118
|
+
SELECT * FROM pg_proc
|
1119
|
+
INNER JOIN pg_cast
|
1120
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
1121
|
+
WHERE proname = 'lower'
|
1122
|
+
AND castsource = #{quote column.sql_type}::regtype
|
1123
|
+
)
|
1124
|
+
SQL
|
1125
|
+
execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
|
1126
|
+
result.getvalue(0, 0)
|
1127
|
+
end
|
962
1128
|
end
|
963
1129
|
end
|
964
1130
|
end
|
@@ -968,28 +1134,30 @@ module ActiveRecord
|
|
968
1134
|
map[Integer] = PG::TextEncoder::Integer.new
|
969
1135
|
map[TrueClass] = PG::TextEncoder::Boolean.new
|
970
1136
|
map[FalseClass] = PG::TextEncoder::Boolean.new
|
971
|
-
@
|
1137
|
+
@raw_connection.type_map_for_queries = map
|
972
1138
|
end
|
973
1139
|
|
974
1140
|
def update_typemap_for_default_timezone
|
975
|
-
if @
|
976
|
-
decoder_class =
|
1141
|
+
if @raw_connection && @mapped_default_timezone != default_timezone && @timestamp_decoder
|
1142
|
+
decoder_class = default_timezone == :utc ?
|
977
1143
|
PG::TextDecoder::TimestampUtc :
|
978
1144
|
PG::TextDecoder::TimestampWithoutTimeZone
|
979
1145
|
|
980
1146
|
@timestamp_decoder = decoder_class.new(**@timestamp_decoder.to_h)
|
981
|
-
@
|
1147
|
+
@raw_connection.type_map_for_results.add_coder(@timestamp_decoder)
|
982
1148
|
|
983
|
-
@
|
1149
|
+
@mapped_default_timezone = default_timezone
|
984
1150
|
|
985
1151
|
# if default timezone has changed, we need to reconfigure the connection
|
986
1152
|
# (specifically, the session time zone)
|
987
|
-
|
1153
|
+
reconfigure_connection_timezone
|
1154
|
+
|
1155
|
+
true
|
988
1156
|
end
|
989
1157
|
end
|
990
1158
|
|
991
1159
|
def add_pg_decoders
|
992
|
-
@
|
1160
|
+
@mapped_default_timezone = nil
|
993
1161
|
@timestamp_decoder = nil
|
994
1162
|
|
995
1163
|
coders_by_name = {
|
@@ -1011,13 +1179,13 @@ module ActiveRecord
|
|
1011
1179
|
FROM pg_type as t
|
1012
1180
|
WHERE t.typname IN (%s)
|
1013
1181
|
SQL
|
1014
|
-
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
1182
|
+
coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
|
1015
1183
|
result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
1016
1184
|
end
|
1017
1185
|
|
1018
1186
|
map = PG::TypeMapByOid.new
|
1019
1187
|
coders.each { |coder| map.add_coder(coder) }
|
1020
|
-
@
|
1188
|
+
@raw_connection.type_map_for_results = map
|
1021
1189
|
|
1022
1190
|
@type_map_for_results = PG::TypeMapByOid.new
|
1023
1191
|
@type_map_for_results.default_type_map = map
|