activerecord 7.0.8 → 7.1.3.4
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 +1554 -1452
- data/MIT-LICENSE +1 -1
- data/README.rdoc +16 -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 +20 -4
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +15 -9
- data/lib/active_record/associations/collection_proxy.rb +15 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +31 -7
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +313 -217
- 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 +52 -34
- 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 +74 -51
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +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 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -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 +18 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -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 +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -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 +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +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 +209 -79
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +175 -153
- 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 +9 -4
- 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 +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +112 -28
- data/lib/active_record/errors.rb +112 -18
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +135 -71
- data/lib/active_record/future_result.rb +31 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/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 +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +104 -5
- data/lib/active_record/migration/compatibility.rb +139 -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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +219 -111
- data/lib/active_record/model_schema.rb +64 -44
- data/lib/active_record/nested_attributes.rb +24 -6
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +188 -37
- 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 +109 -47
- data/lib/active_record/railties/controller_runtime.rb +12 -6
- data/lib/active_record/railties/databases.rake +142 -148
- 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 +174 -44
- 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 +187 -63
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- 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 +352 -63
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +91 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -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 +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +27 -15
- 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 +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/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 +48 -12
- 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
|
##
|
@@ -96,16 +108,17 @@ module ActiveRecord
|
|
96
108
|
# but significantly increases the risk of data loss if the database
|
97
109
|
# crashes. As a result, this should not be used in production
|
98
110
|
# environments. If you would like all created tables to be unlogged in
|
99
|
-
# the test environment you can add the following
|
100
|
-
# file:
|
111
|
+
# the test environment you can add the following to your test.rb file:
|
101
112
|
#
|
102
|
-
#
|
113
|
+
# ActiveSupport.on_load(:active_record_postgresqladapter) do
|
114
|
+
# self.create_unlogged_tables = true
|
115
|
+
# end
|
103
116
|
class_attribute :create_unlogged_tables, default: false
|
104
117
|
|
105
118
|
##
|
106
119
|
# :singleton-method:
|
107
120
|
# 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".
|
121
|
+
# in migrations, \Rails will translate this to a PostgreSQL "timestamp without time zone".
|
109
122
|
# Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
|
110
123
|
# store DateTimes as "timestamp with time zone":
|
111
124
|
#
|
@@ -183,13 +196,17 @@ module ActiveRecord
|
|
183
196
|
end
|
184
197
|
|
185
198
|
def supports_partitioned_indexes?
|
186
|
-
database_version >=
|
199
|
+
database_version >= 11_00_00 # >= 11.0
|
187
200
|
end
|
188
201
|
|
189
202
|
def supports_partial_index?
|
190
203
|
true
|
191
204
|
end
|
192
205
|
|
206
|
+
def supports_index_include?
|
207
|
+
database_version >= 11_00_00 # >= 11.0
|
208
|
+
end
|
209
|
+
|
193
210
|
def supports_expression_index?
|
194
211
|
true
|
195
212
|
end
|
@@ -206,6 +223,14 @@ module ActiveRecord
|
|
206
223
|
true
|
207
224
|
end
|
208
225
|
|
226
|
+
def supports_exclusion_constraints?
|
227
|
+
true
|
228
|
+
end
|
229
|
+
|
230
|
+
def supports_unique_constraints?
|
231
|
+
true
|
232
|
+
end
|
233
|
+
|
209
234
|
def supports_validate_constraints?
|
210
235
|
true
|
211
236
|
end
|
@@ -234,25 +259,41 @@ module ActiveRecord
|
|
234
259
|
true
|
235
260
|
end
|
236
261
|
|
262
|
+
def supports_restart_db_transaction?
|
263
|
+
database_version >= 12_00_00 # >= 12.0
|
264
|
+
end
|
265
|
+
|
237
266
|
def supports_insert_returning?
|
238
267
|
true
|
239
268
|
end
|
240
269
|
|
241
270
|
def supports_insert_on_conflict?
|
242
|
-
database_version >=
|
271
|
+
database_version >= 9_05_00 # >= 9.5
|
243
272
|
end
|
244
273
|
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
245
274
|
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
246
275
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
247
276
|
|
248
277
|
def supports_virtual_columns?
|
249
|
-
database_version >=
|
278
|
+
database_version >= 12_00_00 # >= 12.0
|
279
|
+
end
|
280
|
+
|
281
|
+
def supports_identity_columns? # :nodoc:
|
282
|
+
database_version >= 10_00_00 # >= 10.0
|
283
|
+
end
|
284
|
+
|
285
|
+
def supports_nulls_not_distinct?
|
286
|
+
database_version >= 15_00_00 # >= 15.0
|
250
287
|
end
|
251
288
|
|
252
289
|
def index_algorithms
|
253
290
|
{ concurrently: "CONCURRENTLY" }
|
254
291
|
end
|
255
292
|
|
293
|
+
def return_value_after_insert?(column) # :nodoc:
|
294
|
+
column.auto_populated?
|
295
|
+
end
|
296
|
+
|
256
297
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
257
298
|
def initialize(connection, max)
|
258
299
|
super(max)
|
@@ -266,47 +307,46 @@ module ActiveRecord
|
|
266
307
|
|
267
308
|
private
|
268
309
|
def dealloc(key)
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
310
|
+
# This is ugly, but safe: the statement pool is only
|
311
|
+
# accessed while holding the connection's lock. (And we
|
312
|
+
# don't need the complication of with_raw_connection because
|
313
|
+
# a reconnect would invalidate the entire statement pool.)
|
314
|
+
if conn = @connection.instance_variable_get(:@raw_connection)
|
315
|
+
conn.query "DEALLOCATE #{key}" if conn.status == PG::CONNECTION_OK
|
316
|
+
end
|
275
317
|
rescue PG::Error
|
276
|
-
false
|
277
318
|
end
|
278
319
|
end
|
279
320
|
|
280
321
|
# Initializes and connects a PostgreSQL adapter.
|
281
|
-
def initialize(
|
282
|
-
super
|
322
|
+
def initialize(...)
|
323
|
+
super
|
283
324
|
|
284
|
-
|
325
|
+
conn_params = @config.compact
|
285
326
|
|
286
|
-
#
|
287
|
-
|
288
|
-
|
327
|
+
# Map ActiveRecords param names to PGs.
|
328
|
+
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
329
|
+
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
289
330
|
|
290
|
-
|
291
|
-
|
292
|
-
|
331
|
+
# Forward only valid config params to PG::Connection.connect.
|
332
|
+
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
333
|
+
conn_params.slice!(*valid_conn_param_keys)
|
293
334
|
|
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
|
335
|
+
@connection_parameters = conn_params
|
299
336
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
337
|
+
@max_identifier_length = nil
|
338
|
+
@type_map = nil
|
339
|
+
@raw_connection = nil
|
340
|
+
@notice_receiver_sql_warnings = []
|
341
|
+
|
342
|
+
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
304
343
|
end
|
305
344
|
|
306
345
|
# Is this connection alive and ready for queries?
|
307
346
|
def active?
|
308
347
|
@lock.synchronize do
|
309
|
-
@
|
348
|
+
return false unless @raw_connection
|
349
|
+
@raw_connection.query ";"
|
310
350
|
end
|
311
351
|
true
|
312
352
|
rescue PG::Error
|
@@ -314,31 +354,27 @@ module ActiveRecord
|
|
314
354
|
end
|
315
355
|
|
316
356
|
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
357
|
@lock.synchronize do
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
358
|
+
if @type_map
|
359
|
+
type_map.clear
|
360
|
+
else
|
361
|
+
@type_map = Type::HashLookupTypeMap.new
|
362
|
+
end
|
363
|
+
|
364
|
+
initialize_type_map
|
330
365
|
end
|
331
366
|
end
|
332
367
|
|
333
368
|
def reset!
|
334
369
|
@lock.synchronize do
|
335
|
-
|
336
|
-
|
337
|
-
unless @
|
338
|
-
@
|
370
|
+
return connect! unless @raw_connection
|
371
|
+
|
372
|
+
unless @raw_connection.transaction_status == ::PG::PQTRANS_IDLE
|
373
|
+
@raw_connection.query "ROLLBACK"
|
339
374
|
end
|
340
|
-
@
|
341
|
-
|
375
|
+
@raw_connection.query "DISCARD ALL"
|
376
|
+
|
377
|
+
super
|
342
378
|
end
|
343
379
|
end
|
344
380
|
|
@@ -347,14 +383,15 @@ module ActiveRecord
|
|
347
383
|
def disconnect!
|
348
384
|
@lock.synchronize do
|
349
385
|
super
|
350
|
-
@
|
386
|
+
@raw_connection&.close rescue nil
|
387
|
+
@raw_connection = nil
|
351
388
|
end
|
352
389
|
end
|
353
390
|
|
354
391
|
def discard! # :nodoc:
|
355
392
|
super
|
356
|
-
@
|
357
|
-
@
|
393
|
+
@raw_connection&.socket_io&.reopen(IO::NULL) rescue nil
|
394
|
+
@raw_connection = nil
|
358
395
|
end
|
359
396
|
|
360
397
|
def native_database_types # :nodoc:
|
@@ -370,7 +407,7 @@ module ActiveRecord
|
|
370
407
|
end
|
371
408
|
|
372
409
|
def set_standard_conforming_strings
|
373
|
-
|
410
|
+
internal_execute("SET standard_conforming_strings = on")
|
374
411
|
end
|
375
412
|
|
376
413
|
def supports_ddl_transactions?
|
@@ -398,7 +435,7 @@ module ActiveRecord
|
|
398
435
|
end
|
399
436
|
|
400
437
|
def supports_pgcrypto_uuid?
|
401
|
-
database_version >=
|
438
|
+
database_version >= 9_04_00 # >= 9.4
|
402
439
|
end
|
403
440
|
|
404
441
|
def supports_optimizer_hints?
|
@@ -430,14 +467,21 @@ module ActiveRecord
|
|
430
467
|
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
431
468
|
end
|
432
469
|
|
433
|
-
def enable_extension(name)
|
434
|
-
|
435
|
-
|
436
|
-
}
|
470
|
+
def enable_extension(name, **)
|
471
|
+
schema, name = name.to_s.split(".").values_at(-2, -1)
|
472
|
+
sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
|
473
|
+
sql << " SCHEMA #{schema}" if schema
|
474
|
+
|
475
|
+
internal_exec_query(sql).tap { reload_type_map }
|
437
476
|
end
|
438
477
|
|
439
|
-
|
440
|
-
|
478
|
+
# Removes an extension from the database.
|
479
|
+
#
|
480
|
+
# [<tt>:force</tt>]
|
481
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
482
|
+
# Defaults to false.
|
483
|
+
def disable_extension(name, force: false)
|
484
|
+
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
|
441
485
|
reload_type_map
|
442
486
|
}
|
443
487
|
end
|
@@ -451,7 +495,7 @@ module ActiveRecord
|
|
451
495
|
end
|
452
496
|
|
453
497
|
def extensions
|
454
|
-
|
498
|
+
internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
|
455
499
|
end
|
456
500
|
|
457
501
|
# Returns a list of defined enum types, and their values.
|
@@ -459,31 +503,97 @@ module ActiveRecord
|
|
459
503
|
query = <<~SQL
|
460
504
|
SELECT
|
461
505
|
type.typname AS name,
|
506
|
+
type.OID AS oid,
|
507
|
+
n.nspname AS schema,
|
462
508
|
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
|
463
509
|
FROM pg_enum AS enum
|
464
|
-
JOIN pg_type AS type
|
465
|
-
|
466
|
-
|
510
|
+
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
511
|
+
JOIN pg_namespace n ON type.typnamespace = n.oid
|
512
|
+
WHERE n.nspname = ANY (current_schemas(false))
|
513
|
+
GROUP BY type.OID, n.nspname, type.typname;
|
467
514
|
SQL
|
468
|
-
|
515
|
+
|
516
|
+
internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
|
517
|
+
name, schema = row[0], row[2]
|
518
|
+
schema = nil if schema == current_schema
|
519
|
+
full_name = [schema, name].compact.join(".")
|
520
|
+
memo[full_name] = row.last
|
521
|
+
end.to_a
|
469
522
|
end
|
470
523
|
|
471
524
|
# Given a name and an array of values, creates an enum type.
|
472
|
-
def create_enum(name, values)
|
473
|
-
sql_values = values.map { |s|
|
525
|
+
def create_enum(name, values, **options)
|
526
|
+
sql_values = values.map { |s| quote(s) }.join(", ")
|
527
|
+
scope = quoted_scope(name)
|
474
528
|
query = <<~SQL
|
475
529
|
DO $$
|
476
530
|
BEGIN
|
477
531
|
IF NOT EXISTS (
|
478
|
-
SELECT 1
|
479
|
-
|
532
|
+
SELECT 1
|
533
|
+
FROM pg_type t
|
534
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
535
|
+
WHERE t.typname = #{scope[:name]}
|
536
|
+
AND n.nspname = #{scope[:schema]}
|
480
537
|
) THEN
|
481
|
-
CREATE TYPE
|
538
|
+
CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
|
482
539
|
END IF;
|
483
540
|
END
|
484
541
|
$$;
|
485
542
|
SQL
|
486
|
-
|
543
|
+
internal_exec_query(query).tap { reload_type_map }
|
544
|
+
end
|
545
|
+
|
546
|
+
# Drops an enum type.
|
547
|
+
#
|
548
|
+
# If the <tt>if_exists: true</tt> option is provided, the enum is dropped
|
549
|
+
# only if it exists. Otherwise, if the enum doesn't exist, an error is
|
550
|
+
# raised.
|
551
|
+
#
|
552
|
+
# The +values+ parameter will be ignored if present. It can be helpful
|
553
|
+
# to provide this in a migration's +change+ method so it can be reverted.
|
554
|
+
# In that case, +values+ will be used by #create_enum.
|
555
|
+
def drop_enum(name, values = nil, **options)
|
556
|
+
query = <<~SQL
|
557
|
+
DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
|
558
|
+
SQL
|
559
|
+
internal_exec_query(query).tap { reload_type_map }
|
560
|
+
end
|
561
|
+
|
562
|
+
# Rename an existing enum type to something else.
|
563
|
+
def rename_enum(name, options = {})
|
564
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
565
|
+
|
566
|
+
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
|
567
|
+
end
|
568
|
+
|
569
|
+
# Add enum value to an existing enum type.
|
570
|
+
def add_enum_value(type_name, value, options = {})
|
571
|
+
before, after = options.values_at(:before, :after)
|
572
|
+
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
|
573
|
+
|
574
|
+
if before && after
|
575
|
+
raise ArgumentError, "Cannot have both :before and :after at the same time"
|
576
|
+
elsif before
|
577
|
+
sql << " BEFORE '#{before}'"
|
578
|
+
elsif after
|
579
|
+
sql << " AFTER '#{after}'"
|
580
|
+
end
|
581
|
+
|
582
|
+
execute(sql).tap { reload_type_map }
|
583
|
+
end
|
584
|
+
|
585
|
+
# Rename enum value on an existing enum type.
|
586
|
+
def rename_enum_value(type_name, options = {})
|
587
|
+
unless database_version >= 10_00_00 # >= 10.0
|
588
|
+
raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
|
589
|
+
end
|
590
|
+
|
591
|
+
from = options.fetch(:from) { raise ArgumentError, ":from is required" }
|
592
|
+
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
593
|
+
|
594
|
+
execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
|
595
|
+
reload_type_map
|
596
|
+
}
|
487
597
|
end
|
488
598
|
|
489
599
|
# Returns the configured supported identifier length supported by PostgreSQL
|
@@ -494,7 +604,7 @@ module ActiveRecord
|
|
494
604
|
# Set the authorized user for this session
|
495
605
|
def session_auth=(user)
|
496
606
|
clear_cache!
|
497
|
-
|
607
|
+
internal_execute("SET SESSION AUTHORIZATION #{user}", nil, materialize_transactions: true)
|
498
608
|
end
|
499
609
|
|
500
610
|
def use_insert_returning?
|
@@ -503,7 +613,7 @@ module ActiveRecord
|
|
503
613
|
|
504
614
|
# Returns the version of the connected PostgreSQL server.
|
505
615
|
def get_database_version # :nodoc:
|
506
|
-
|
616
|
+
valid_raw_connection.server_version
|
507
617
|
end
|
508
618
|
alias :postgresql_version :database_version
|
509
619
|
|
@@ -531,7 +641,7 @@ module ActiveRecord
|
|
531
641
|
end
|
532
642
|
|
533
643
|
def check_version # :nodoc:
|
534
|
-
if database_version <
|
644
|
+
if database_version < 9_03_00 # < 9.3
|
535
645
|
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
536
646
|
end
|
537
647
|
end
|
@@ -575,10 +685,6 @@ module ActiveRecord
|
|
575
685
|
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
576
686
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
577
687
|
|
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
688
|
m.register_type "numeric" do |_, fmod, sql_type|
|
583
689
|
precision = extract_precision(sql_type)
|
584
690
|
scale = extract_scale(sql_type)
|
@@ -607,12 +713,15 @@ module ActiveRecord
|
|
607
713
|
end
|
608
714
|
|
609
715
|
private
|
610
|
-
|
611
|
-
@type_map ||= Type::HashLookupTypeMap.new
|
612
|
-
end
|
716
|
+
attr_reader :type_map
|
613
717
|
|
614
718
|
def initialize_type_map(m = type_map)
|
615
719
|
self.class.initialize_type_map(m)
|
720
|
+
|
721
|
+
self.class.register_class_with_precision m, "time", Type::Time, timezone: @default_timezone
|
722
|
+
self.class.register_class_with_precision m, "timestamp", OID::Timestamp, timezone: @default_timezone
|
723
|
+
self.class.register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
|
724
|
+
|
616
725
|
load_additional_types
|
617
726
|
end
|
618
727
|
|
@@ -669,35 +778,53 @@ module ActiveRecord
|
|
669
778
|
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
670
779
|
when nil
|
671
780
|
if exception.message.match?(/connection is closed/i)
|
672
|
-
ConnectionNotEstablished.new(exception)
|
781
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
782
|
+
elsif exception.is_a?(PG::ConnectionBad)
|
783
|
+
# libpq message style always ends with a newline; the pg gem's internal
|
784
|
+
# errors do not. We separate these cases because a pg-internal
|
785
|
+
# ConnectionBad means it failed before it managed to send the query,
|
786
|
+
# whereas a libpq failure could have occurred at any time (meaning the
|
787
|
+
# server may have already executed part or all of the query).
|
788
|
+
if exception.message.end_with?("\n")
|
789
|
+
ConnectionFailed.new(exception, connection_pool: @pool)
|
790
|
+
else
|
791
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
792
|
+
end
|
673
793
|
else
|
674
794
|
super
|
675
795
|
end
|
676
796
|
when UNIQUE_VIOLATION
|
677
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
797
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
678
798
|
when FOREIGN_KEY_VIOLATION
|
679
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
799
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
680
800
|
when VALUE_LIMIT_VIOLATION
|
681
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
801
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
682
802
|
when NUMERIC_VALUE_OUT_OF_RANGE
|
683
|
-
RangeError.new(message, sql: sql, binds: binds)
|
803
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
684
804
|
when NOT_NULL_VIOLATION
|
685
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
805
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
686
806
|
when SERIALIZATION_FAILURE
|
687
|
-
SerializationFailure.new(message, sql: sql, binds: binds)
|
807
|
+
SerializationFailure.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
688
808
|
when DEADLOCK_DETECTED
|
689
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
809
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
690
810
|
when DUPLICATE_DATABASE
|
691
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
811
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
692
812
|
when LOCK_NOT_AVAILABLE
|
693
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
813
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
694
814
|
when QUERY_CANCELED
|
695
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
815
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
696
816
|
else
|
697
817
|
super
|
698
818
|
end
|
699
819
|
end
|
700
820
|
|
821
|
+
def retryable_query_error?(exception)
|
822
|
+
# We cannot retry anything if we're inside a broken transaction; we need to at
|
823
|
+
# least raise until the innermost savepoint is rolled back
|
824
|
+
@raw_connection&.transaction_status != ::PG::PQTRANS_INERROR &&
|
825
|
+
super
|
826
|
+
end
|
827
|
+
|
701
828
|
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
702
829
|
if !type_map.key?(oid)
|
703
830
|
load_additional_types([oid])
|
@@ -714,7 +841,7 @@ module ActiveRecord
|
|
714
841
|
def load_additional_types(oids = nil)
|
715
842
|
initializer = OID::TypeMapInitializer.new(type_map)
|
716
843
|
load_types_queries(initializer, oids) do |query|
|
717
|
-
execute_and_clear(query, "SCHEMA", []) do |records|
|
844
|
+
execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
|
718
845
|
initializer.run(records)
|
719
846
|
end
|
720
847
|
end
|
@@ -737,14 +864,14 @@ module ActiveRecord
|
|
737
864
|
|
738
865
|
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
739
866
|
|
740
|
-
def execute_and_clear(sql, name, binds, prepare: false, async: false)
|
867
|
+
def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
|
741
868
|
sql = transform_query(sql)
|
742
869
|
check_if_write_query(sql)
|
743
870
|
|
744
871
|
if !prepare || without_prepared_statement?(binds)
|
745
|
-
result = exec_no_cache(sql, name, binds, async: async)
|
872
|
+
result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
746
873
|
else
|
747
|
-
result = exec_cache(sql, name, binds, async: async)
|
874
|
+
result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
748
875
|
end
|
749
876
|
begin
|
750
877
|
ret = yield result
|
@@ -754,8 +881,7 @@ module ActiveRecord
|
|
754
881
|
ret
|
755
882
|
end
|
756
883
|
|
757
|
-
def exec_no_cache(sql, name, binds, async:
|
758
|
-
materialize_transactions
|
884
|
+
def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
759
885
|
mark_transaction_written_if_write(sql)
|
760
886
|
|
761
887
|
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
@@ -764,23 +890,27 @@ module ActiveRecord
|
|
764
890
|
|
765
891
|
type_casted_binds = type_casted_binds(binds)
|
766
892
|
log(sql, name, binds, type_casted_binds, async: async) do
|
767
|
-
|
768
|
-
|
893
|
+
with_raw_connection do |conn|
|
894
|
+
result = conn.exec_params(sql, type_casted_binds)
|
895
|
+
verified!
|
896
|
+
result
|
769
897
|
end
|
770
898
|
end
|
771
899
|
end
|
772
900
|
|
773
|
-
def exec_cache(sql, name, binds, async:
|
774
|
-
materialize_transactions
|
901
|
+
def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
775
902
|
mark_transaction_written_if_write(sql)
|
903
|
+
|
776
904
|
update_typemap_for_default_timezone
|
777
905
|
|
778
|
-
|
779
|
-
|
906
|
+
with_raw_connection do |conn|
|
907
|
+
stmt_key = prepare_statement(sql, binds, conn)
|
908
|
+
type_casted_binds = type_casted_binds(binds)
|
780
909
|
|
781
|
-
|
782
|
-
|
783
|
-
|
910
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
911
|
+
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
912
|
+
verified!
|
913
|
+
result
|
784
914
|
end
|
785
915
|
end
|
786
916
|
rescue ActiveRecord::StatementInvalid => e
|
@@ -828,70 +958,98 @@ module ActiveRecord
|
|
828
958
|
|
829
959
|
# Prepare the statement if it hasn't been prepared, return
|
830
960
|
# the statement key.
|
831
|
-
def prepare_statement(sql, binds)
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
raise translate_exception_class(e, sql, binds)
|
840
|
-
end
|
841
|
-
# Clear the queue
|
842
|
-
@connection.get_last_result
|
843
|
-
@statements[sql_key] = nextkey
|
961
|
+
def prepare_statement(sql, binds, conn)
|
962
|
+
sql_key = sql_key(sql)
|
963
|
+
unless @statements.key? sql_key
|
964
|
+
nextkey = @statements.next_key
|
965
|
+
begin
|
966
|
+
conn.prepare nextkey, sql
|
967
|
+
rescue => e
|
968
|
+
raise translate_exception_class(e, sql, binds)
|
844
969
|
end
|
845
|
-
|
970
|
+
# Clear the queue
|
971
|
+
conn.get_last_result
|
972
|
+
@statements[sql_key] = nextkey
|
846
973
|
end
|
974
|
+
@statements[sql_key]
|
847
975
|
end
|
848
976
|
|
849
977
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
850
978
|
# connected server's characteristics.
|
851
979
|
def connect
|
852
|
-
@
|
853
|
-
|
854
|
-
|
855
|
-
|
980
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
981
|
+
rescue ConnectionNotEstablished => ex
|
982
|
+
raise ex.set_pool(@pool)
|
983
|
+
end
|
984
|
+
|
985
|
+
def reconnect
|
986
|
+
begin
|
987
|
+
@raw_connection&.reset
|
988
|
+
rescue PG::ConnectionBad
|
989
|
+
@raw_connection = nil
|
990
|
+
end
|
991
|
+
|
992
|
+
connect unless @raw_connection
|
856
993
|
end
|
857
994
|
|
858
995
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
859
996
|
# This is called by #connect and should not be called manually.
|
860
997
|
def configure_connection
|
861
998
|
if @config[:encoding]
|
862
|
-
@
|
999
|
+
@raw_connection.set_client_encoding(@config[:encoding])
|
863
1000
|
end
|
864
1001
|
self.client_min_messages = @config[:min_messages] || "warning"
|
865
1002
|
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
|
866
1003
|
|
1004
|
+
unless ActiveRecord.db_warnings_action.nil?
|
1005
|
+
@raw_connection.set_notice_receiver do |result|
|
1006
|
+
message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY)
|
1007
|
+
code = result.error_field(PG::Result::PG_DIAG_SQLSTATE)
|
1008
|
+
level = result.error_field(PG::Result::PG_DIAG_SEVERITY)
|
1009
|
+
@notice_receiver_sql_warnings << SQLWarning.new(message, code, level, nil, @pool)
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
|
867
1013
|
# Use standard-conforming strings so we don't have to do the E'...' dance.
|
868
1014
|
set_standard_conforming_strings
|
869
1015
|
|
870
1016
|
variables = @config.fetch(:variables, {}).stringify_keys
|
871
1017
|
|
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
1018
|
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
883
|
-
|
1019
|
+
internal_execute("SET intervalstyle = iso_8601")
|
884
1020
|
|
885
1021
|
# SET statements from :variables config hash
|
886
1022
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
887
1023
|
variables.map do |k, v|
|
888
1024
|
if v == ":default" || v == :default
|
889
1025
|
# Sets the value to the global or compile default
|
890
|
-
|
1026
|
+
internal_execute("SET SESSION #{k} TO DEFAULT")
|
891
1027
|
elsif !v.nil?
|
892
|
-
|
1028
|
+
internal_execute("SET SESSION #{k} TO #{quote(v)}")
|
893
1029
|
end
|
894
1030
|
end
|
1031
|
+
|
1032
|
+
add_pg_encoders
|
1033
|
+
add_pg_decoders
|
1034
|
+
|
1035
|
+
reload_type_map
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def reconfigure_connection_timezone
|
1039
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
1040
|
+
|
1041
|
+
# If it's been directly configured as a connection variable, we don't
|
1042
|
+
# need to do anything here; it will be set up by configure_connection
|
1043
|
+
# and then never changed.
|
1044
|
+
return if variables["timezone"]
|
1045
|
+
|
1046
|
+
# If using Active Record's time zone support configure the connection
|
1047
|
+
# to return TIMESTAMP WITH ZONE types in UTC.
|
1048
|
+
if default_timezone == :utc
|
1049
|
+
internal_execute("SET SESSION timezone TO 'UTC'")
|
1050
|
+
else
|
1051
|
+
internal_execute("SET SESSION timezone TO DEFAULT")
|
1052
|
+
end
|
895
1053
|
end
|
896
1054
|
|
897
1055
|
# Returns the list of a table's column names, data types, and default values.
|
@@ -917,6 +1075,7 @@ module ActiveRecord
|
|
917
1075
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
918
1076
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
919
1077
|
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
1078
|
+
#{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
|
920
1079
|
#{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
|
921
1080
|
FROM pg_attribute a
|
922
1081
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
@@ -928,37 +1087,37 @@ module ActiveRecord
|
|
928
1087
|
SQL
|
929
1088
|
end
|
930
1089
|
|
931
|
-
def extract_table_ref_from_insert_sql(sql)
|
932
|
-
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
|
933
|
-
$1.strip if $1
|
934
|
-
end
|
935
|
-
|
936
1090
|
def arel_visitor
|
937
1091
|
Arel::Visitors::PostgreSQL.new(self)
|
938
1092
|
end
|
939
1093
|
|
940
1094
|
def build_statement_pool
|
941
|
-
StatementPool.new(
|
1095
|
+
StatementPool.new(self, self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
942
1096
|
end
|
943
1097
|
|
944
1098
|
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
|
-
|
1099
|
+
# NOTE: citext is an exception. It is possible to perform a
|
1100
|
+
# case-insensitive comparison using `LOWER()`, but it is
|
1101
|
+
# unnecessary, as `citext` is case-insensitive by definition.
|
1102
|
+
@case_insensitive_cache ||= { "citext" => false }
|
1103
|
+
@case_insensitive_cache.fetch(column.sql_type) do
|
1104
|
+
@case_insensitive_cache[column.sql_type] = begin
|
1105
|
+
sql = <<~SQL
|
1106
|
+
SELECT exists(
|
1107
|
+
SELECT * FROM pg_proc
|
1108
|
+
WHERE proname = 'lower'
|
1109
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
1110
|
+
) OR exists(
|
1111
|
+
SELECT * FROM pg_proc
|
1112
|
+
INNER JOIN pg_cast
|
1113
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
1114
|
+
WHERE proname = 'lower'
|
1115
|
+
AND castsource = #{quote column.sql_type}::regtype
|
1116
|
+
)
|
1117
|
+
SQL
|
1118
|
+
execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
|
1119
|
+
result.getvalue(0, 0)
|
1120
|
+
end
|
962
1121
|
end
|
963
1122
|
end
|
964
1123
|
end
|
@@ -968,28 +1127,30 @@ module ActiveRecord
|
|
968
1127
|
map[Integer] = PG::TextEncoder::Integer.new
|
969
1128
|
map[TrueClass] = PG::TextEncoder::Boolean.new
|
970
1129
|
map[FalseClass] = PG::TextEncoder::Boolean.new
|
971
|
-
@
|
1130
|
+
@raw_connection.type_map_for_queries = map
|
972
1131
|
end
|
973
1132
|
|
974
1133
|
def update_typemap_for_default_timezone
|
975
|
-
if @
|
976
|
-
decoder_class =
|
1134
|
+
if @raw_connection && @mapped_default_timezone != default_timezone && @timestamp_decoder
|
1135
|
+
decoder_class = default_timezone == :utc ?
|
977
1136
|
PG::TextDecoder::TimestampUtc :
|
978
1137
|
PG::TextDecoder::TimestampWithoutTimeZone
|
979
1138
|
|
980
1139
|
@timestamp_decoder = decoder_class.new(**@timestamp_decoder.to_h)
|
981
|
-
@
|
1140
|
+
@raw_connection.type_map_for_results.add_coder(@timestamp_decoder)
|
982
1141
|
|
983
|
-
@
|
1142
|
+
@mapped_default_timezone = default_timezone
|
984
1143
|
|
985
1144
|
# if default timezone has changed, we need to reconfigure the connection
|
986
1145
|
# (specifically, the session time zone)
|
987
|
-
|
1146
|
+
reconfigure_connection_timezone
|
1147
|
+
|
1148
|
+
true
|
988
1149
|
end
|
989
1150
|
end
|
990
1151
|
|
991
1152
|
def add_pg_decoders
|
992
|
-
@
|
1153
|
+
@mapped_default_timezone = nil
|
993
1154
|
@timestamp_decoder = nil
|
994
1155
|
|
995
1156
|
coders_by_name = {
|
@@ -1011,13 +1172,13 @@ module ActiveRecord
|
|
1011
1172
|
FROM pg_type as t
|
1012
1173
|
WHERE t.typname IN (%s)
|
1013
1174
|
SQL
|
1014
|
-
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
1175
|
+
coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
|
1015
1176
|
result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
1016
1177
|
end
|
1017
1178
|
|
1018
1179
|
map = PG::TypeMapByOid.new
|
1019
1180
|
coders.each { |coder| map.add_coder(coder) }
|
1020
|
-
@
|
1181
|
+
@raw_connection.type_map_for_results = map
|
1021
1182
|
|
1022
1183
|
@type_map_for_results = PG::TypeMapByOid.new
|
1023
1184
|
@type_map_for_results.default_type_map = map
|