activerecord 7.1.5.2 → 8.1.2.1

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.
Files changed (244) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +567 -2443
  3. data/README.rdoc +16 -16
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +3 -2
  6. data/lib/active_record/associations/alias_tracker.rb +31 -23
  7. data/lib/active_record/associations/association.rb +43 -12
  8. data/lib/active_record/associations/belongs_to_association.rb +31 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +23 -11
  11. data/lib/active_record/associations/builder/belongs_to.rb +18 -4
  12. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  14. data/lib/active_record/associations/builder/has_many.rb +3 -4
  15. data/lib/active_record/associations/builder/has_one.rb +3 -4
  16. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  17. data/lib/active_record/associations/collection_association.rb +17 -9
  18. data/lib/active_record/associations/collection_proxy.rb +36 -5
  19. data/lib/active_record/associations/deprecation.rb +88 -0
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  21. data/lib/active_record/associations/errors.rb +268 -0
  22. data/lib/active_record/associations/has_many_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  24. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +4 -2
  26. data/lib/active_record/associations/nested_error.rb +47 -0
  27. data/lib/active_record/associations/preloader/association.rb +4 -3
  28. data/lib/active_record/associations/preloader/batch.rb +7 -1
  29. data/lib/active_record/associations/preloader/branch.rb +8 -1
  30. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  31. data/lib/active_record/associations/singular_association.rb +14 -3
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +250 -315
  34. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  35. data/lib/active_record/attribute_assignment.rb +0 -2
  36. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  37. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  38. data/lib/active_record/attribute_methods/query.rb +34 -0
  39. data/lib/active_record/attribute_methods/read.rb +1 -13
  40. data/lib/active_record/attribute_methods/serialization.rb +20 -27
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
  42. data/lib/active_record/attribute_methods.rb +71 -75
  43. data/lib/active_record/attributes.rb +96 -68
  44. data/lib/active_record/autosave_association.rb +93 -58
  45. data/lib/active_record/base.rb +5 -7
  46. data/lib/active_record/callbacks.rb +1 -1
  47. data/lib/active_record/coders/json.rb +14 -5
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
  62. data/lib/active_record/connection_adapters/column.rb +17 -4
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
  68. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
  70. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  74. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
  85. data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
  89. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  91. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
  94. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  95. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
  96. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  97. data/lib/active_record/connection_adapters.rb +66 -0
  98. data/lib/active_record/connection_handling.rb +88 -46
  99. data/lib/active_record/core.rb +142 -58
  100. data/lib/active_record/counter_cache.rb +52 -18
  101. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  102. data/lib/active_record/database_configurations/database_config.rb +28 -5
  103. data/lib/active_record/database_configurations/hash_config.rb +103 -41
  104. data/lib/active_record/database_configurations/url_config.rb +30 -1
  105. data/lib/active_record/database_configurations.rb +8 -4
  106. data/lib/active_record/delegated_type.rb +43 -19
  107. data/lib/active_record/dynamic_matchers.rb +56 -71
  108. data/lib/active_record/encryption/config.rb +3 -1
  109. data/lib/active_record/encryption/encryptable_record.rb +12 -12
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
  111. data/lib/active_record/encryption/encryptor.rb +58 -22
  112. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  113. data/lib/active_record/encryption/key_provider.rb +1 -1
  114. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  115. data/lib/active_record/encryption/message_serializer.rb +4 -0
  116. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  117. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  118. data/lib/active_record/encryption/scheme.rb +9 -2
  119. data/lib/active_record/enum.rb +57 -36
  120. data/lib/active_record/errors.rb +76 -26
  121. data/lib/active_record/explain.rb +14 -25
  122. data/lib/active_record/explain_registry.rb +51 -2
  123. data/lib/active_record/filter_attribute_handler.rb +73 -0
  124. data/lib/active_record/fixture_set/table_row.rb +19 -2
  125. data/lib/active_record/fixtures.rb +39 -35
  126. data/lib/active_record/future_result.rb +21 -13
  127. data/lib/active_record/gem_version.rb +3 -3
  128. data/lib/active_record/inheritance.rb +5 -3
  129. data/lib/active_record/insert_all.rb +28 -20
  130. data/lib/active_record/integration.rb +4 -1
  131. data/lib/active_record/internal_metadata.rb +48 -34
  132. data/lib/active_record/locking/optimistic.rb +15 -7
  133. data/lib/active_record/locking/pessimistic.rb +5 -0
  134. data/lib/active_record/log_subscriber.rb +3 -34
  135. data/lib/active_record/message_pack.rb +1 -1
  136. data/lib/active_record/middleware/shard_selector.rb +34 -17
  137. data/lib/active_record/migration/command_recorder.rb +46 -14
  138. data/lib/active_record/migration/compatibility.rb +40 -25
  139. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  140. data/lib/active_record/migration/default_strategy.rb +4 -5
  141. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  142. data/lib/active_record/migration.rb +119 -103
  143. data/lib/active_record/model_schema.rb +66 -78
  144. data/lib/active_record/nested_attributes.rb +17 -9
  145. data/lib/active_record/persistence.rb +161 -454
  146. data/lib/active_record/query_cache.rb +33 -15
  147. data/lib/active_record/query_logs.rb +106 -39
  148. data/lib/active_record/query_logs_formatter.rb +17 -28
  149. data/lib/active_record/querying.rb +28 -16
  150. data/lib/active_record/railtie.rb +61 -74
  151. data/lib/active_record/railties/controller_runtime.rb +19 -5
  152. data/lib/active_record/railties/databases.rake +64 -78
  153. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  154. data/lib/active_record/railties/job_runtime.rb +10 -11
  155. data/lib/active_record/reflection.rb +147 -53
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  157. data/lib/active_record/relation/batches.rb +153 -73
  158. data/lib/active_record/relation/calculations.rb +146 -96
  159. data/lib/active_record/relation/delegation.rb +30 -23
  160. data/lib/active_record/relation/finder_methods.rb +64 -34
  161. data/lib/active_record/relation/merger.rb +12 -14
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  163. data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  165. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  166. data/lib/active_record/relation/predicate_builder.rb +25 -10
  167. data/lib/active_record/relation/query_attribute.rb +4 -2
  168. data/lib/active_record/relation/query_methods.rb +351 -124
  169. data/lib/active_record/relation/spawn_methods.rb +9 -25
  170. data/lib/active_record/relation/where_clause.rb +17 -30
  171. data/lib/active_record/relation.rb +590 -124
  172. data/lib/active_record/result.rb +131 -59
  173. data/lib/active_record/runtime_registry.rb +44 -21
  174. data/lib/active_record/sanitization.rb +33 -25
  175. data/lib/active_record/schema.rb +8 -6
  176. data/lib/active_record/schema_dumper.rb +66 -31
  177. data/lib/active_record/schema_migration.rb +31 -14
  178. data/lib/active_record/scoping/named.rb +6 -2
  179. data/lib/active_record/scoping.rb +0 -1
  180. data/lib/active_record/secure_token.rb +3 -3
  181. data/lib/active_record/signed_id.rb +68 -20
  182. data/lib/active_record/statement_cache.rb +29 -25
  183. data/lib/active_record/store.rb +51 -22
  184. data/lib/active_record/structured_event_subscriber.rb +85 -0
  185. data/lib/active_record/table_metadata.rb +7 -33
  186. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  187. data/lib/active_record/tasks/database_tasks.rb +118 -90
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
  191. data/lib/active_record/test_databases.rb +14 -4
  192. data/lib/active_record/test_fixtures.rb +123 -89
  193. data/lib/active_record/testing/query_assertions.rb +127 -0
  194. data/lib/active_record/timestamp.rb +6 -4
  195. data/lib/active_record/token_for.rb +22 -12
  196. data/lib/active_record/touch_later.rb +1 -1
  197. data/lib/active_record/transaction.rb +129 -0
  198. data/lib/active_record/transactions.rb +97 -18
  199. data/lib/active_record/translation.rb +0 -2
  200. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  201. data/lib/active_record/type/internal/timezone.rb +7 -0
  202. data/lib/active_record/type/json.rb +15 -2
  203. data/lib/active_record/type/serialized.rb +12 -7
  204. data/lib/active_record/type/type_map.rb +1 -1
  205. data/lib/active_record/type_caster/connection.rb +5 -4
  206. data/lib/active_record/validations/associated.rb +10 -4
  207. data/lib/active_record/validations/uniqueness.rb +23 -18
  208. data/lib/active_record/validations.rb +4 -1
  209. data/lib/active_record.rb +203 -59
  210. data/lib/arel/alias_predication.rb +3 -1
  211. data/lib/arel/collectors/bind.rb +4 -2
  212. data/lib/arel/collectors/composite.rb +7 -0
  213. data/lib/arel/collectors/sql_string.rb +2 -2
  214. data/lib/arel/collectors/substitute_binds.rb +3 -3
  215. data/lib/arel/crud.rb +8 -11
  216. data/lib/arel/delete_manager.rb +5 -0
  217. data/lib/arel/nodes/binary.rb +1 -7
  218. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  219. data/lib/arel/nodes/count.rb +2 -2
  220. data/lib/arel/nodes/delete_statement.rb +4 -2
  221. data/lib/arel/nodes/function.rb +4 -10
  222. data/lib/arel/nodes/named_function.rb +2 -2
  223. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  224. data/lib/arel/nodes/node.rb +6 -5
  225. data/lib/arel/nodes/sql_literal.rb +8 -1
  226. data/lib/arel/nodes/update_statement.rb +4 -2
  227. data/lib/arel/nodes.rb +2 -4
  228. data/lib/arel/predications.rb +1 -1
  229. data/lib/arel/select_manager.rb +14 -5
  230. data/lib/arel/table.rb +3 -7
  231. data/lib/arel/tree_manager.rb +3 -2
  232. data/lib/arel/update_manager.rb +7 -1
  233. data/lib/arel/visitors/dot.rb +3 -3
  234. data/lib/arel/visitors/mysql.rb +9 -4
  235. data/lib/arel/visitors/postgresql.rb +56 -12
  236. data/lib/arel/visitors/sqlite.rb +80 -8
  237. data/lib/arel/visitors/to_sql.rb +34 -37
  238. data/lib/arel.rb +10 -4
  239. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  240. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  241. metadata +23 -14
  242. data/lib/active_record/explain_subscriber.rb +0 -34
  243. data/lib/active_record/normalization.rb +0 -167
  244. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -20,23 +20,12 @@ require "active_record/connection_adapters/postgresql/type_metadata"
20
20
  require "active_record/connection_adapters/postgresql/utils"
21
21
 
22
22
  module ActiveRecord
23
- module ConnectionHandling # :nodoc:
24
- def postgresql_adapter_class
25
- ConnectionAdapters::PostgreSQLAdapter
26
- end
27
-
28
- # Establishes a connection to the database that's used by all Active Record objects
29
- def postgresql_connection(config)
30
- postgresql_adapter_class.new(config)
31
- end
32
- end
33
-
34
23
  module ConnectionAdapters
35
24
  # = Active Record PostgreSQL Adapter
36
25
  #
37
26
  # The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
38
27
  #
39
- # Options:
28
+ # ==== Options
40
29
  #
41
30
  # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
42
31
  # the default is to connect to localhost.
@@ -97,7 +86,7 @@ module ActiveRecord
97
86
  "-c #{name}=#{value.to_s.gsub(/[ \\]/, '\\\\\0')}" unless value == ":default" || value == :default
98
87
  end.join(" ")
99
88
  end
100
- find_cmd_and_exec("psql", config.database)
89
+ find_cmd_and_exec(ActiveRecord.database_cli[:postgresql], config.database)
101
90
  end
102
91
  end
103
92
 
@@ -133,6 +122,15 @@ module ActiveRecord
133
122
  # setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
134
123
  class_attribute :datetime_type, default: :timestamp
135
124
 
125
+ ##
126
+ # :singleton-method:
127
+ # Toggles automatic decoding of date columns.
128
+ #
129
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> String
130
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.decode_dates = true
131
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> Date
132
+ class_attribute :decode_dates, default: false
133
+
136
134
  NATIVE_DATABASE_TYPES = {
137
135
  primary_key: "bigserial primary key",
138
136
  string: { name: "character varying" },
@@ -286,12 +284,22 @@ module ActiveRecord
286
284
  database_version >= 15_00_00 # >= 15.0
287
285
  end
288
286
 
289
- def index_algorithms
290
- { concurrently: "CONCURRENTLY" }
287
+ def supports_native_partitioning? # :nodoc:
288
+ database_version >= 10_00_00 # >= 10.0
291
289
  end
292
290
 
293
- def return_value_after_insert?(column) # :nodoc:
294
- column.auto_populated?
291
+ if PG::Connection.method_defined?(:close_prepared) # pg 1.6.0 & libpq 17
292
+ def supports_close_prepared? # :nodoc:
293
+ database_version >= 17_00_00
294
+ end
295
+ else
296
+ def supports_close_prepared? # :nodoc:
297
+ false
298
+ end
299
+ end
300
+
301
+ def index_algorithms
302
+ { concurrently: "CONCURRENTLY" }
295
303
  end
296
304
 
297
305
  class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
@@ -311,8 +319,12 @@ module ActiveRecord
311
319
  # accessed while holding the connection's lock. (And we
312
320
  # don't need the complication of with_raw_connection because
313
321
  # 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
322
+ if (conn = @connection.instance_variable_get(:@raw_connection)) && conn.status == PG::CONNECTION_OK
323
+ if @connection.supports_close_prepared?
324
+ conn.close_prepared key
325
+ else
326
+ conn.query "DEALLOCATE #{key}"
327
+ end
316
328
  end
317
329
  rescue PG::Error
318
330
  end
@@ -342,11 +354,16 @@ module ActiveRecord
342
354
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
343
355
  end
344
356
 
357
+ def connected?
358
+ !(@raw_connection.nil? || @raw_connection.finished?)
359
+ end
360
+
345
361
  # Is this connection alive and ready for queries?
346
362
  def active?
347
363
  @lock.synchronize do
348
364
  return false unless @raw_connection
349
365
  @raw_connection.query ";"
366
+ verified!
350
367
  end
351
368
  true
352
369
  rescue PG::Error
@@ -378,6 +395,11 @@ module ActiveRecord
378
395
  end
379
396
  end
380
397
 
398
+ def clear_cache!(new_connection: false)
399
+ super
400
+ @schema_search_path = nil if new_connection
401
+ end
402
+
381
403
  # Disconnects from the database if already connected. Otherwise, this
382
404
  # method does nothing.
383
405
  def disconnect!
@@ -394,10 +416,6 @@ module ActiveRecord
394
416
  @raw_connection = nil
395
417
  end
396
418
 
397
- def native_database_types # :nodoc:
398
- self.class.native_database_types
399
- end
400
-
401
419
  def self.native_database_types # :nodoc:
402
420
  @native_database_types ||= begin
403
421
  types = NATIVE_DATABASE_TYPES.dup
@@ -407,7 +425,7 @@ module ActiveRecord
407
425
  end
408
426
 
409
427
  def set_standard_conforming_strings
410
- internal_execute("SET standard_conforming_strings = on")
428
+ internal_execute("SET standard_conforming_strings = on", "SCHEMA")
411
429
  end
412
430
 
413
431
  def supports_ddl_transactions?
@@ -481,6 +499,7 @@ module ActiveRecord
481
499
  # Set to +:cascade+ to drop dependent objects as well.
482
500
  # Defaults to false.
483
501
  def disable_extension(name, force: false)
502
+ _schema, name = name.to_s.split(".").values_at(-2, -1)
484
503
  internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
485
504
  reload_type_map
486
505
  }
@@ -495,7 +514,19 @@ module ActiveRecord
495
514
  end
496
515
 
497
516
  def extensions
498
- internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
517
+ query = <<~SQL
518
+ SELECT
519
+ pg_extension.extname,
520
+ n.nspname AS schema
521
+ FROM pg_extension
522
+ JOIN pg_namespace n ON pg_extension.extnamespace = n.oid
523
+ SQL
524
+
525
+ internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.map do |row|
526
+ name, schema = row[0], row[1]
527
+ schema = nil if schema == current_schema
528
+ [schema, name].compact.join(".")
529
+ end
499
530
  end
500
531
 
501
532
  # Returns a list of defined enum types, and their values.
@@ -505,7 +536,7 @@ module ActiveRecord
505
536
  type.typname AS name,
506
537
  type.OID AS oid,
507
538
  n.nspname AS schema,
508
- string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
539
+ array_agg(enum.enumlabel ORDER BY enum.enumsortorder) AS value
509
540
  FROM pg_enum AS enum
510
541
  JOIN pg_type AS type ON (type.oid = enum.enumtypid)
511
542
  JOIN pg_namespace n ON type.typnamespace = n.oid
@@ -560,30 +591,34 @@ module ActiveRecord
560
591
  end
561
592
 
562
593
  # Rename an existing enum type to something else.
563
- def rename_enum(name, options = {})
564
- to = options.fetch(:to) { raise ArgumentError, ":to is required" }
594
+ def rename_enum(name, new_name = nil, **options)
595
+ new_name ||= options.fetch(:to) do
596
+ raise ArgumentError, "rename_enum requires two from/to name positional arguments."
597
+ end
565
598
 
566
- exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
599
+ exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}").tap { reload_type_map }
567
600
  end
568
601
 
569
602
  # Add enum value to an existing enum type.
570
- def add_enum_value(type_name, value, options = {})
603
+ def add_enum_value(type_name, value, **options)
571
604
  before, after = options.values_at(:before, :after)
572
- sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
605
+ sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE"
606
+ sql << " IF NOT EXISTS" if options[:if_not_exists]
607
+ sql << " #{quote(value)}"
573
608
 
574
609
  if before && after
575
610
  raise ArgumentError, "Cannot have both :before and :after at the same time"
576
611
  elsif before
577
- sql << " BEFORE '#{before}'"
612
+ sql << " BEFORE #{quote(before)}"
578
613
  elsif after
579
- sql << " AFTER '#{after}'"
614
+ sql << " AFTER #{quote(after)}"
580
615
  end
581
616
 
582
617
  execute(sql).tap { reload_type_map }
583
618
  end
584
619
 
585
620
  # Rename enum value on an existing enum type.
586
- def rename_enum_value(type_name, options = {})
621
+ def rename_enum_value(type_name, **options)
587
622
  unless database_version >= 10_00_00 # >= 10.0
588
623
  raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
589
624
  end
@@ -591,12 +626,12 @@ module ActiveRecord
591
626
  from = options.fetch(:from) { raise ArgumentError, ":from is required" }
592
627
  to = options.fetch(:to) { raise ArgumentError, ":to is required" }
593
628
 
594
- execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
629
+ execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE #{quote(from)} TO #{quote(to)}").tap {
595
630
  reload_type_map
596
631
  }
597
632
  end
598
633
 
599
- # Returns the configured supported identifier length supported by PostgreSQL
634
+ # Returns the configured maximum supported identifier length supported by PostgreSQL
600
635
  def max_identifier_length
601
636
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
602
637
  end
@@ -613,7 +648,13 @@ module ActiveRecord
613
648
 
614
649
  # Returns the version of the connected PostgreSQL server.
615
650
  def get_database_version # :nodoc:
616
- valid_raw_connection.server_version
651
+ with_raw_connection do |conn|
652
+ version = conn.server_version
653
+ if version == 0
654
+ raise ActiveRecord::ConnectionNotEstablished, "Could not determine PostgreSQL version"
655
+ end
656
+ version
657
+ end
617
658
  end
618
659
  alias :postgresql_version :database_version
619
660
 
@@ -652,8 +693,8 @@ module ActiveRecord
652
693
  m.register_type "int4", Type::Integer.new(limit: 4)
653
694
  m.register_type "int8", Type::Integer.new(limit: 8)
654
695
  m.register_type "oid", OID::Oid.new
655
- m.register_type "float4", Type::Float.new
656
- m.alias_type "float8", "float4"
696
+ m.register_type "float4", Type::Float.new(limit: 24)
697
+ m.register_type "float8", Type::Float.new
657
698
  m.register_type "text", Type::Text.new
658
699
  register_class_with_limit m, "varchar", Type::String
659
700
  m.alias_type "char", "varchar"
@@ -766,6 +807,8 @@ module ActiveRecord
766
807
  NOT_NULL_VIOLATION = "23502"
767
808
  FOREIGN_KEY_VIOLATION = "23503"
768
809
  UNIQUE_VIOLATION = "23505"
810
+ CHECK_VIOLATION = "23514"
811
+ EXCLUSION_VIOLATION = "23P01"
769
812
  SERIALIZATION_FAILURE = "40001"
770
813
  DEADLOCK_DETECTED = "40P01"
771
814
  DUPLICATE_DATABASE = "42P04"
@@ -777,7 +820,7 @@ module ActiveRecord
777
820
 
778
821
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
779
822
  when nil
780
- if exception.message.match?(/connection is closed/i)
823
+ if exception.message.match?(/connection is closed/i) || exception.message.match?(/no connection to the server/i)
781
824
  ConnectionNotEstablished.new(exception, connection_pool: @pool)
782
825
  elsif exception.is_a?(PG::ConnectionBad)
783
826
  # libpq message style always ends with a newline; the pg gem's internal
@@ -797,6 +840,10 @@ module ActiveRecord
797
840
  RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
798
841
  when FOREIGN_KEY_VIOLATION
799
842
  InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
843
+ when CHECK_VIOLATION
844
+ CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
845
+ when EXCLUSION_VIOLATION
846
+ ExclusionViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
800
847
  when VALUE_LIMIT_VIOLATION
801
848
  ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
802
849
  when NUMERIC_VALUE_OUT_OF_RANGE
@@ -841,9 +888,8 @@ module ActiveRecord
841
888
  def load_additional_types(oids = nil)
842
889
  initializer = OID::TypeMapInitializer.new(type_map)
843
890
  load_types_queries(initializer, oids) do |query|
844
- execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
845
- initializer.run(records)
846
- end
891
+ records = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
892
+ initializer.run(records)
847
893
  end
848
894
  end
849
895
 
@@ -864,71 +910,6 @@ module ActiveRecord
864
910
 
865
911
  FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
866
912
 
867
- def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
868
- sql = transform_query(sql)
869
- check_if_write_query(sql)
870
-
871
- if !prepare || without_prepared_statement?(binds)
872
- result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
873
- else
874
- result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
875
- end
876
- begin
877
- ret = yield result
878
- ensure
879
- result.clear
880
- end
881
- ret
882
- end
883
-
884
- def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
885
- mark_transaction_written_if_write(sql)
886
-
887
- # make sure we carry over any changes to ActiveRecord.default_timezone that have been
888
- # made since we established the connection
889
- update_typemap_for_default_timezone
890
-
891
- type_casted_binds = type_casted_binds(binds)
892
- log(sql, name, binds, type_casted_binds, async: async) do
893
- with_raw_connection do |conn|
894
- result = conn.exec_params(sql, type_casted_binds)
895
- verified!
896
- result
897
- end
898
- end
899
- end
900
-
901
- def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
902
- mark_transaction_written_if_write(sql)
903
-
904
- update_typemap_for_default_timezone
905
-
906
- with_raw_connection do |conn|
907
- stmt_key = prepare_statement(sql, binds, conn)
908
- type_casted_binds = type_casted_binds(binds)
909
-
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
914
- end
915
- end
916
- rescue ActiveRecord::StatementInvalid => e
917
- raise unless is_cached_plan_failure?(e)
918
-
919
- # Nothing we can do if we are in a transaction because all commands
920
- # will raise InFailedSQLTransaction
921
- if in_transaction?
922
- raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
923
- else
924
- @lock.synchronize do
925
- # outside of transactions we can simply flush this query and retry
926
- @statements.delete sql_key(sql)
927
- end
928
- retry
929
- end
930
- end
931
-
932
913
  # Annoyingly, the code for prepared statements whose return value may
933
914
  # have changed is FEATURE_NOT_SUPPORTED.
934
915
  #
@@ -938,8 +919,7 @@ module ActiveRecord
938
919
  #
939
920
  # Check here for more details:
940
921
  # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
941
- def is_cached_plan_failure?(e)
942
- pgerror = e.cause
922
+ def is_cached_plan_failure?(pgerror)
943
923
  pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
944
924
  pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
945
925
  rescue
@@ -995,6 +975,8 @@ module ActiveRecord
995
975
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
996
976
  # This is called by #connect and should not be called manually.
997
977
  def configure_connection
978
+ super
979
+
998
980
  if @config[:encoding]
999
981
  @raw_connection.set_client_encoding(@config[:encoding])
1000
982
  end
@@ -1016,22 +998,24 @@ module ActiveRecord
1016
998
  variables = @config.fetch(:variables, {}).stringify_keys
1017
999
 
1018
1000
  # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
1019
- internal_execute("SET intervalstyle = iso_8601")
1001
+ internal_execute("SET intervalstyle = iso_8601", "SCHEMA")
1020
1002
 
1021
1003
  # SET statements from :variables config hash
1022
1004
  # https://www.postgresql.org/docs/current/static/sql-set.html
1023
1005
  variables.map do |k, v|
1024
1006
  if v == ":default" || v == :default
1025
1007
  # Sets the value to the global or compile default
1026
- internal_execute("SET SESSION #{k} TO DEFAULT")
1008
+ internal_execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
1027
1009
  elsif !v.nil?
1028
- internal_execute("SET SESSION #{k} TO #{quote(v)}")
1010
+ internal_execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
1029
1011
  end
1030
1012
  end
1031
1013
 
1032
1014
  add_pg_encoders
1033
1015
  add_pg_decoders
1034
1016
 
1017
+ schema_search_path # populate cache
1018
+
1035
1019
  reload_type_map
1036
1020
  end
1037
1021
 
@@ -1046,9 +1030,9 @@ module ActiveRecord
1046
1030
  # If using Active Record's time zone support configure the connection
1047
1031
  # to return TIMESTAMP WITH ZONE types in UTC.
1048
1032
  if default_timezone == :utc
1049
- internal_execute("SET SESSION timezone TO 'UTC'")
1033
+ raw_execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
1050
1034
  else
1051
- internal_execute("SET SESSION timezone TO DEFAULT")
1035
+ raw_execute("SET SESSION timezone TO DEFAULT", "SCHEMA")
1052
1036
  end
1053
1037
  end
1054
1038
 
@@ -1115,9 +1099,8 @@ module ActiveRecord
1115
1099
  AND castsource = #{quote column.sql_type}::regtype
1116
1100
  )
1117
1101
  SQL
1118
- execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1119
- result.getvalue(0, 0)
1120
- end
1102
+ result = internal_execute(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
1103
+ result.getvalue(0, 0)
1121
1104
  end
1122
1105
  end
1123
1106
  end
@@ -1165,6 +1148,7 @@ module ActiveRecord
1165
1148
  "timestamp" => PG::TextDecoder::TimestampUtc,
1166
1149
  "timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
1167
1150
  }
1151
+ coders_by_name["date"] = PG::TextDecoder::Date if decode_dates
1168
1152
 
1169
1153
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
1170
1154
  query = <<~SQL % known_coder_types.join(", ")
@@ -1172,9 +1156,8 @@ module ActiveRecord
1172
1156
  FROM pg_type as t
1173
1157
  WHERE t.typname IN (%s)
1174
1158
  SQL
1175
- coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1176
- result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
1177
- end
1159
+ result = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
1160
+ coders = result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
1178
1161
 
1179
1162
  map = PG::TypeMapByOid.new
1180
1163
  coders.each { |coder| map.add_coder(coder) }