activerecord 6.0.0 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -1,17 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
4
- gem "pg", ">= 0.18", "< 2.0"
3
+ gem "pg", "~> 1.1"
5
4
  require "pg"
6
5
 
7
- # Use async_exec instead of exec_params on pg versions before 1.1
8
- class ::PG::Connection # :nodoc:
9
- unless self.public_method_defined?(:async_exec_params)
10
- remove_method :exec_params
11
- alias exec_params async_exec
12
- end
13
- end
14
-
6
+ require "active_support/core_ext/object/try"
15
7
  require "active_record/connection_adapters/abstract_adapter"
16
8
  require "active_record/connection_adapters/statement_pool"
17
9
  require "active_record/connection_adapters/postgresql/column"
@@ -28,43 +20,19 @@ require "active_record/connection_adapters/postgresql/type_metadata"
28
20
  require "active_record/connection_adapters/postgresql/utils"
29
21
 
30
22
  module ActiveRecord
31
- module ConnectionHandling # :nodoc:
32
- # Establishes a connection to the database that's used by all Active Record objects
33
- def postgresql_connection(config)
34
- conn_params = config.symbolize_keys
35
-
36
- conn_params.delete_if { |_, v| v.nil? }
37
-
38
- # Map ActiveRecords param names to PGs.
39
- conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
40
- conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
41
-
42
- # Forward only valid config params to PG::Connection.connect.
43
- valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
44
- conn_params.slice!(*valid_conn_param_keys)
45
-
46
- conn = PG.connect(conn_params)
47
- ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
48
- rescue ::PG::Error => error
49
- if error.message.include?(conn_params[:dbname])
50
- raise ActiveRecord::NoDatabaseError
51
- else
52
- raise
53
- end
54
- end
55
- end
56
-
57
23
  module ConnectionAdapters
58
- # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
24
+ # = Active Record PostgreSQL Adapter
25
+ #
26
+ # The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
59
27
  #
60
- # Options:
28
+ # ==== Options
61
29
  #
62
30
  # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
63
31
  # the default is to connect to localhost.
64
32
  # * <tt>:port</tt> - Defaults to 5432.
65
33
  # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
66
34
  # * <tt>:password</tt> - Password to be used if the server demands password authentication.
67
- # * <tt>:database</tt> - Defaults to be the same as the user name.
35
+ # * <tt>:database</tt> - Defaults to be the same as the username.
68
36
  # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
69
37
  # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
70
38
  # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
@@ -85,6 +53,43 @@ module ActiveRecord
85
53
  class PostgreSQLAdapter < AbstractAdapter
86
54
  ADAPTER_NAME = "PostgreSQL"
87
55
 
56
+ class << self
57
+ def new_client(conn_params)
58
+ PG.connect(**conn_params)
59
+ rescue ::PG::Error => error
60
+ if conn_params && conn_params[:dbname] == "postgres"
61
+ raise ActiveRecord::ConnectionNotEstablished, error.message
62
+ elsif conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
63
+ raise ActiveRecord::NoDatabaseError.db_error(conn_params[:dbname])
64
+ elsif conn_params && conn_params[:user] && error.message.include?(conn_params[:user])
65
+ raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:user])
66
+ elsif conn_params && conn_params[:host] && error.message.include?(conn_params[:host])
67
+ raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:host])
68
+ else
69
+ raise ActiveRecord::ConnectionNotEstablished, error.message
70
+ end
71
+ end
72
+
73
+ def dbconsole(config, options = {})
74
+ pg_config = config.configuration_hash
75
+
76
+ ENV["PGUSER"] = pg_config[:username] if pg_config[:username]
77
+ ENV["PGHOST"] = pg_config[:host] if pg_config[:host]
78
+ ENV["PGPORT"] = pg_config[:port].to_s if pg_config[:port]
79
+ ENV["PGPASSWORD"] = pg_config[:password].to_s if pg_config[:password] && options[:include_password]
80
+ ENV["PGSSLMODE"] = pg_config[:sslmode].to_s if pg_config[:sslmode]
81
+ ENV["PGSSLCERT"] = pg_config[:sslcert].to_s if pg_config[:sslcert]
82
+ ENV["PGSSLKEY"] = pg_config[:sslkey].to_s if pg_config[:sslkey]
83
+ ENV["PGSSLROOTCERT"] = pg_config[:sslrootcert].to_s if pg_config[:sslrootcert]
84
+ if pg_config[:variables]
85
+ ENV["PGOPTIONS"] = pg_config[:variables].filter_map do |name, value|
86
+ "-c #{name}=#{value.to_s.gsub(/[ \\]/, '\\\\\0')}" unless value == ":default" || value == :default
87
+ end.join(" ")
88
+ end
89
+ find_cmd_and_exec("psql", config.database)
90
+ end
91
+ end
92
+
88
93
  ##
89
94
  # :singleton-method:
90
95
  # PostgreSQL allows the creation of "unlogged" tables, which do not record
@@ -92,20 +97,51 @@ module ActiveRecord
92
97
  # but significantly increases the risk of data loss if the database
93
98
  # crashes. As a result, this should not be used in production
94
99
  # environments. If you would like all created tables to be unlogged in
95
- # the test environment you can add the following line to your test.rb
96
- # file:
100
+ # the test environment you can add the following to your test.rb file:
97
101
  #
98
- # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
102
+ # ActiveSupport.on_load(:active_record_postgresqladapter) do
103
+ # self.create_unlogged_tables = true
104
+ # end
99
105
  class_attribute :create_unlogged_tables, default: false
100
106
 
107
+ ##
108
+ # :singleton-method:
109
+ # PostgreSQL supports multiple types for DateTimes. By default, if you use +datetime+
110
+ # in migrations, \Rails will translate this to a PostgreSQL "timestamp without time zone".
111
+ # Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
112
+ # store DateTimes as "timestamp with time zone":
113
+ #
114
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
115
+ #
116
+ # Or if you are adding a custom type:
117
+ #
118
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
119
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
120
+ #
121
+ # If you're using +:ruby+ as your +config.active_record.schema_format+ and you change this
122
+ # setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
123
+ class_attribute :datetime_type, default: :timestamp
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
+
101
134
  NATIVE_DATABASE_TYPES = {
102
135
  primary_key: "bigserial primary key",
103
136
  string: { name: "character varying" },
104
137
  text: { name: "text" },
105
138
  integer: { name: "integer", limit: 4 },
139
+ bigint: { name: "bigint" },
106
140
  float: { name: "float" },
107
141
  decimal: { name: "decimal" },
108
- datetime: { name: "timestamp" },
142
+ datetime: {}, # set dynamically based on datetime_type
143
+ timestamp: { name: "timestamp" },
144
+ timestamptz: { name: "timestamptz" },
109
145
  time: { name: "time" },
110
146
  date: { name: "date" },
111
147
  daterange: { name: "daterange" },
@@ -139,9 +175,10 @@ module ActiveRecord
139
175
  money: { name: "money" },
140
176
  interval: { name: "interval" },
141
177
  oid: { name: "oid" },
178
+ enum: {} # special type https://www.postgresql.org/docs/current/datatype-enum.html
142
179
  }
143
180
 
144
- OID = PostgreSQL::OID #:nodoc:
181
+ OID = PostgreSQL::OID # :nodoc:
145
182
 
146
183
  include PostgreSQL::Quoting
147
184
  include PostgreSQL::ReferentialIntegrity
@@ -156,10 +193,18 @@ module ActiveRecord
156
193
  true
157
194
  end
158
195
 
196
+ def supports_partitioned_indexes?
197
+ database_version >= 11_00_00 # >= 11.0
198
+ end
199
+
159
200
  def supports_partial_index?
160
201
  true
161
202
  end
162
203
 
204
+ def supports_index_include?
205
+ database_version >= 11_00_00 # >= 11.0
206
+ end
207
+
163
208
  def supports_expression_index?
164
209
  true
165
210
  end
@@ -172,10 +217,26 @@ module ActiveRecord
172
217
  true
173
218
  end
174
219
 
220
+ def supports_check_constraints?
221
+ true
222
+ end
223
+
224
+ def supports_exclusion_constraints?
225
+ true
226
+ end
227
+
228
+ def supports_unique_constraints?
229
+ true
230
+ end
231
+
175
232
  def supports_validate_constraints?
176
233
  true
177
234
  end
178
235
 
236
+ def supports_deferrable_constraints?
237
+ true
238
+ end
239
+
179
240
  def supports_views?
180
241
  true
181
242
  end
@@ -196,17 +257,33 @@ module ActiveRecord
196
257
  true
197
258
  end
198
259
 
260
+ def supports_restart_db_transaction?
261
+ database_version >= 12_00_00 # >= 12.0
262
+ end
263
+
199
264
  def supports_insert_returning?
200
265
  true
201
266
  end
202
267
 
203
268
  def supports_insert_on_conflict?
204
- database_version >= 90500
269
+ database_version >= 9_05_00 # >= 9.5
205
270
  end
206
271
  alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
207
272
  alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
208
273
  alias supports_insert_conflict_target? supports_insert_on_conflict?
209
274
 
275
+ def supports_virtual_columns?
276
+ database_version >= 12_00_00 # >= 12.0
277
+ end
278
+
279
+ def supports_identity_columns? # :nodoc:
280
+ database_version >= 10_00_00 # >= 10.0
281
+ end
282
+
283
+ def supports_nulls_not_distinct?
284
+ database_version >= 15_00_00 # >= 15.0
285
+ end
286
+
210
287
  def index_algorithms
211
288
  { concurrently: "CONCURRENTLY" }
212
289
  end
@@ -219,82 +296,84 @@ module ActiveRecord
219
296
  end
220
297
 
221
298
  def next_key
222
- "a#{@counter + 1}"
223
- end
224
-
225
- def []=(sql, key)
226
- super.tap { @counter += 1 }
299
+ "a#{@counter += 1}"
227
300
  end
228
301
 
229
302
  private
230
303
  def dealloc(key)
231
- @connection.query "DEALLOCATE #{key}" if connection_active?
232
- rescue PG::Error
233
- end
234
-
235
- def connection_active?
236
- @connection.status == PG::CONNECTION_OK
304
+ # This is ugly, but safe: the statement pool is only
305
+ # accessed while holding the connection's lock. (And we
306
+ # don't need the complication of with_raw_connection because
307
+ # a reconnect would invalidate the entire statement pool.)
308
+ if conn = @connection.instance_variable_get(:@raw_connection)
309
+ conn.query "DEALLOCATE #{key}" if conn.status == PG::CONNECTION_OK
310
+ end
237
311
  rescue PG::Error
238
- false
239
312
  end
240
313
  end
241
314
 
242
315
  # Initializes and connects a PostgreSQL adapter.
243
- def initialize(connection, logger, connection_parameters, config)
244
- super(connection, logger, config)
316
+ def initialize(...)
317
+ super
245
318
 
246
- @connection_parameters = connection_parameters
319
+ conn_params = @config.compact
247
320
 
248
- # @local_tz is initialized as nil to avoid warnings when connect tries to use it
249
- @local_tz = nil
250
- @max_identifier_length = nil
321
+ # Map ActiveRecords param names to PGs.
322
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
323
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
324
+
325
+ # Forward only valid config params to PG::Connection.connect.
326
+ valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
327
+ conn_params.slice!(*valid_conn_param_keys)
328
+
329
+ @connection_parameters = conn_params
251
330
 
252
- configure_connection
253
- add_pg_encoders
254
- add_pg_decoders
331
+ @max_identifier_length = nil
332
+ @type_map = nil
333
+ @raw_connection = nil
334
+ @notice_receiver_sql_warnings = []
255
335
 
256
- @type_map = Type::HashLookupTypeMap.new
257
- initialize_type_map
258
- @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
259
336
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
260
337
  end
261
338
 
262
- def self.database_exists?(config)
263
- !!ActiveRecord::Base.postgresql_connection(config)
264
- rescue ActiveRecord::NoDatabaseError
265
- false
339
+ def connected?
340
+ !(@raw_connection.nil? || @raw_connection.finished?)
266
341
  end
267
342
 
268
343
  # Is this connection alive and ready for queries?
269
344
  def active?
270
345
  @lock.synchronize do
271
- @connection.query "SELECT 1"
346
+ return false unless @raw_connection
347
+ @raw_connection.query ";"
348
+ verified!
272
349
  end
273
350
  true
274
351
  rescue PG::Error
275
352
  false
276
353
  end
277
354
 
278
- # Close then reopen the connection.
279
- def reconnect!
355
+ def reload_type_map # :nodoc:
280
356
  @lock.synchronize do
281
- super
282
- @connection.reset
283
- configure_connection
284
- rescue PG::ConnectionBad
285
- connect
357
+ if @type_map
358
+ type_map.clear
359
+ else
360
+ @type_map = Type::HashLookupTypeMap.new
361
+ end
362
+
363
+ initialize_type_map
286
364
  end
287
365
  end
288
366
 
289
367
  def reset!
290
368
  @lock.synchronize do
291
- clear_cache!
292
- reset_transaction
293
- unless @connection.transaction_status == ::PG::PQTRANS_IDLE
294
- @connection.query "ROLLBACK"
369
+ return connect! unless @raw_connection
370
+
371
+ unless @raw_connection.transaction_status == ::PG::PQTRANS_IDLE
372
+ @raw_connection.query "ROLLBACK"
295
373
  end
296
- @connection.query "DISCARD ALL"
297
- configure_connection
374
+ @raw_connection.query "DISCARD ALL"
375
+
376
+ super
298
377
  end
299
378
  end
300
379
 
@@ -303,22 +382,31 @@ module ActiveRecord
303
382
  def disconnect!
304
383
  @lock.synchronize do
305
384
  super
306
- @connection.close rescue nil
385
+ @raw_connection&.close rescue nil
386
+ @raw_connection = nil
307
387
  end
308
388
  end
309
389
 
310
390
  def discard! # :nodoc:
311
391
  super
312
- @connection.socket_io.reopen(IO::NULL) rescue nil
313
- @connection = nil
392
+ @raw_connection&.socket_io&.reopen(IO::NULL) rescue nil
393
+ @raw_connection = nil
314
394
  end
315
395
 
316
- def native_database_types #:nodoc:
317
- NATIVE_DATABASE_TYPES
396
+ def native_database_types # :nodoc:
397
+ self.class.native_database_types
398
+ end
399
+
400
+ def self.native_database_types # :nodoc:
401
+ @native_database_types ||= begin
402
+ types = NATIVE_DATABASE_TYPES.dup
403
+ types[:datetime] = types[datetime_type]
404
+ types
405
+ end
318
406
  end
319
407
 
320
408
  def set_standard_conforming_strings
321
- execute("SET standard_conforming_strings = on", "SCHEMA")
409
+ internal_execute("SET standard_conforming_strings = on")
322
410
  end
323
411
 
324
412
  def supports_ddl_transactions?
@@ -337,11 +425,6 @@ module ActiveRecord
337
425
  true
338
426
  end
339
427
 
340
- def supports_ranges?
341
- true
342
- end
343
- deprecate :supports_ranges?
344
-
345
428
  def supports_materialized_views?
346
429
  true
347
430
  end
@@ -351,7 +434,7 @@ module ActiveRecord
351
434
  end
352
435
 
353
436
  def supports_pgcrypto_uuid?
354
- database_version >= 90400
437
+ database_version >= 9_04_00 # >= 9.4
355
438
  end
356
439
 
357
440
  def supports_optimizer_hints?
@@ -361,6 +444,10 @@ module ActiveRecord
361
444
  @has_pg_hint_plan
362
445
  end
363
446
 
447
+ def supports_common_table_expressions?
448
+ true
449
+ end
450
+
364
451
  def supports_lazy_transactions?
365
452
  true
366
453
  end
@@ -379,14 +466,21 @@ module ActiveRecord
379
466
  query_value("SELECT pg_advisory_unlock(#{lock_id})")
380
467
  end
381
468
 
382
- def enable_extension(name)
383
- exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
384
- reload_type_map
385
- }
469
+ def enable_extension(name, **)
470
+ schema, name = name.to_s.split(".").values_at(-2, -1)
471
+ sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
472
+ sql << " SCHEMA #{schema}" if schema
473
+
474
+ internal_exec_query(sql).tap { reload_type_map }
386
475
  end
387
476
 
388
- def disable_extension(name)
389
- exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
477
+ # Removes an extension from the database.
478
+ #
479
+ # [<tt>:force</tt>]
480
+ # Set to +:cascade+ to drop dependent objects as well.
481
+ # Defaults to false.
482
+ def disable_extension(name, force: false)
483
+ internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
390
484
  reload_type_map
391
485
  }
392
486
  end
@@ -400,7 +494,105 @@ module ActiveRecord
400
494
  end
401
495
 
402
496
  def extensions
403
- exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
497
+ internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
498
+ end
499
+
500
+ # Returns a list of defined enum types, and their values.
501
+ def enum_types
502
+ query = <<~SQL
503
+ SELECT
504
+ type.typname AS name,
505
+ type.OID AS oid,
506
+ n.nspname AS schema,
507
+ array_agg(enum.enumlabel ORDER BY enum.enumsortorder) AS value
508
+ FROM pg_enum AS enum
509
+ JOIN pg_type AS type ON (type.oid = enum.enumtypid)
510
+ JOIN pg_namespace n ON type.typnamespace = n.oid
511
+ WHERE n.nspname = ANY (current_schemas(false))
512
+ GROUP BY type.OID, n.nspname, type.typname;
513
+ SQL
514
+
515
+ internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
516
+ name, schema = row[0], row[2]
517
+ schema = nil if schema == current_schema
518
+ full_name = [schema, name].compact.join(".")
519
+ memo[full_name] = row.last
520
+ end.to_a
521
+ end
522
+
523
+ # Given a name and an array of values, creates an enum type.
524
+ def create_enum(name, values, **options)
525
+ sql_values = values.map { |s| quote(s) }.join(", ")
526
+ scope = quoted_scope(name)
527
+ query = <<~SQL
528
+ DO $$
529
+ BEGIN
530
+ IF NOT EXISTS (
531
+ SELECT 1
532
+ FROM pg_type t
533
+ JOIN pg_namespace n ON t.typnamespace = n.oid
534
+ WHERE t.typname = #{scope[:name]}
535
+ AND n.nspname = #{scope[:schema]}
536
+ ) THEN
537
+ CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
538
+ END IF;
539
+ END
540
+ $$;
541
+ SQL
542
+ internal_exec_query(query).tap { reload_type_map }
543
+ end
544
+
545
+ # Drops an enum type.
546
+ #
547
+ # If the <tt>if_exists: true</tt> option is provided, the enum is dropped
548
+ # only if it exists. Otherwise, if the enum doesn't exist, an error is
549
+ # raised.
550
+ #
551
+ # The +values+ parameter will be ignored if present. It can be helpful
552
+ # to provide this in a migration's +change+ method so it can be reverted.
553
+ # In that case, +values+ will be used by #create_enum.
554
+ def drop_enum(name, values = nil, **options)
555
+ query = <<~SQL
556
+ DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
557
+ SQL
558
+ internal_exec_query(query).tap { reload_type_map }
559
+ end
560
+
561
+ # Rename an existing enum type to something else.
562
+ def rename_enum(name, options = {})
563
+ to = options.fetch(:to) { raise ArgumentError, ":to is required" }
564
+
565
+ exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
566
+ end
567
+
568
+ # Add enum value to an existing enum type.
569
+ def add_enum_value(type_name, value, options = {})
570
+ before, after = options.values_at(:before, :after)
571
+ sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
572
+
573
+ if before && after
574
+ raise ArgumentError, "Cannot have both :before and :after at the same time"
575
+ elsif before
576
+ sql << " BEFORE '#{before}'"
577
+ elsif after
578
+ sql << " AFTER '#{after}'"
579
+ end
580
+
581
+ execute(sql).tap { reload_type_map }
582
+ end
583
+
584
+ # Rename enum value on an existing enum type.
585
+ def rename_enum_value(type_name, options = {})
586
+ unless database_version >= 10_00_00 # >= 10.0
587
+ raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
588
+ end
589
+
590
+ from = options.fetch(:from) { raise ArgumentError, ":from is required" }
591
+ to = options.fetch(:to) { raise ArgumentError, ":to is required" }
592
+
593
+ execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
594
+ reload_type_map
595
+ }
404
596
  end
405
597
 
406
598
  # Returns the configured supported identifier length supported by PostgreSQL
@@ -411,26 +603,22 @@ module ActiveRecord
411
603
  # Set the authorized user for this session
412
604
  def session_auth=(user)
413
605
  clear_cache!
414
- execute("SET SESSION AUTHORIZATION #{user}")
606
+ internal_execute("SET SESSION AUTHORIZATION #{user}", nil, materialize_transactions: true)
415
607
  end
416
608
 
417
609
  def use_insert_returning?
418
610
  @use_insert_returning
419
611
  end
420
612
 
421
- def column_name_for_operation(operation, node) # :nodoc:
422
- OPERATION_ALIASES.fetch(operation) { operation.downcase }
423
- end
424
-
425
- OPERATION_ALIASES = { # :nodoc:
426
- "maximum" => "max",
427
- "minimum" => "min",
428
- "average" => "avg",
429
- }
430
-
431
613
  # Returns the version of the connected PostgreSQL server.
432
614
  def get_database_version # :nodoc:
433
- @connection.server_version
615
+ with_raw_connection do |conn|
616
+ version = conn.server_version
617
+ if version == 0
618
+ raise ActiveRecord::ConnectionFailed, "Could not determine PostgreSQL version"
619
+ end
620
+ version
621
+ end
434
622
  end
435
623
  alias :postgresql_version :database_version
436
624
 
@@ -445,7 +633,12 @@ module ActiveRecord
445
633
  sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
446
634
  elsif insert.update_duplicates?
447
635
  sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
448
- sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
636
+ if insert.raw_update_sql?
637
+ sql << insert.raw_update_sql
638
+ else
639
+ sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
640
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
641
+ end
449
642
  end
450
643
 
451
644
  sql << " RETURNING #{insert.returning}" if insert.returning
@@ -453,71 +646,19 @@ module ActiveRecord
453
646
  end
454
647
 
455
648
  def check_version # :nodoc:
456
- if database_version < 90300
649
+ if database_version < 9_03_00 # < 9.3
457
650
  raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
458
651
  end
459
652
  end
460
653
 
461
- private
462
-
463
- # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
464
- VALUE_LIMIT_VIOLATION = "22001"
465
- NUMERIC_VALUE_OUT_OF_RANGE = "22003"
466
- NOT_NULL_VIOLATION = "23502"
467
- FOREIGN_KEY_VIOLATION = "23503"
468
- UNIQUE_VIOLATION = "23505"
469
- SERIALIZATION_FAILURE = "40001"
470
- DEADLOCK_DETECTED = "40P01"
471
- LOCK_NOT_AVAILABLE = "55P03"
472
- QUERY_CANCELED = "57014"
473
-
474
- def translate_exception(exception, message:, sql:, binds:)
475
- return exception unless exception.respond_to?(:result)
476
-
477
- case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
478
- when UNIQUE_VIOLATION
479
- RecordNotUnique.new(message, sql: sql, binds: binds)
480
- when FOREIGN_KEY_VIOLATION
481
- InvalidForeignKey.new(message, sql: sql, binds: binds)
482
- when VALUE_LIMIT_VIOLATION
483
- ValueTooLong.new(message, sql: sql, binds: binds)
484
- when NUMERIC_VALUE_OUT_OF_RANGE
485
- RangeError.new(message, sql: sql, binds: binds)
486
- when NOT_NULL_VIOLATION
487
- NotNullViolation.new(message, sql: sql, binds: binds)
488
- when SERIALIZATION_FAILURE
489
- SerializationFailure.new(message, sql: sql, binds: binds)
490
- when DEADLOCK_DETECTED
491
- Deadlocked.new(message, sql: sql, binds: binds)
492
- when LOCK_NOT_AVAILABLE
493
- LockWaitTimeout.new(message, sql: sql, binds: binds)
494
- when QUERY_CANCELED
495
- QueryCanceled.new(message, sql: sql, binds: binds)
496
- else
497
- super
498
- end
499
- end
500
-
501
- def get_oid_type(oid, fmod, column_name, sql_type = "")
502
- if !type_map.key?(oid)
503
- load_additional_types([oid])
504
- end
505
-
506
- type_map.fetch(oid, fmod, sql_type) {
507
- warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
508
- Type.default_value.tap do |cast_type|
509
- type_map.register_type(oid, cast_type)
510
- end
511
- }
512
- end
513
-
514
- def initialize_type_map(m = type_map)
654
+ class << self
655
+ def initialize_type_map(m) # :nodoc:
515
656
  m.register_type "int2", Type::Integer.new(limit: 2)
516
657
  m.register_type "int4", Type::Integer.new(limit: 4)
517
658
  m.register_type "int8", Type::Integer.new(limit: 8)
518
659
  m.register_type "oid", OID::Oid.new
519
- m.register_type "float4", Type::Float.new
520
- m.alias_type "float8", "float4"
660
+ m.register_type "float4", Type::Float.new(limit: 24)
661
+ m.register_type "float8", Type::Float.new
521
662
  m.register_type "text", Type::Text.new
522
663
  register_class_with_limit m, "varchar", Type::String
523
664
  m.alias_type "char", "varchar"
@@ -526,7 +667,6 @@ module ActiveRecord
526
667
  m.register_type "bool", Type::Boolean.new
527
668
  register_class_with_limit m, "bit", OID::Bit
528
669
  register_class_with_limit m, "varbit", OID::BitVarying
529
- m.alias_type "timestamptz", "timestamp"
530
670
  m.register_type "date", OID::Date.new
531
671
 
532
672
  m.register_type "money", OID::Money.new
@@ -540,7 +680,7 @@ module ActiveRecord
540
680
  m.register_type "uuid", OID::Uuid.new
541
681
  m.register_type "xml", OID::Xml.new
542
682
  m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
543
- m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
683
+ m.register_type "macaddr", OID::Macaddr.new
544
684
  m.register_type "citext", OID::SpecializedString.new(:citext)
545
685
  m.register_type "ltree", OID::SpecializedString.new(:ltree)
546
686
  m.register_type "line", OID::SpecializedString.new(:line)
@@ -550,14 +690,6 @@ module ActiveRecord
550
690
  m.register_type "polygon", OID::SpecializedString.new(:polygon)
551
691
  m.register_type "circle", OID::SpecializedString.new(:circle)
552
692
 
553
- m.register_type "interval" do |_, _, sql_type|
554
- precision = extract_precision(sql_type)
555
- OID::SpecializedString.new(:interval, precision: precision)
556
- end
557
-
558
- register_class_with_precision m, "time", Type::Time
559
- register_class_with_precision m, "timestamp", OID::DateTime
560
-
561
693
  m.register_type "numeric" do |_, fmod, sql_type|
562
694
  precision = extract_precision(sql_type)
563
695
  scale = extract_scale(sql_type)
@@ -578,6 +710,23 @@ module ActiveRecord
578
710
  end
579
711
  end
580
712
 
713
+ m.register_type "interval" do |*args, sql_type|
714
+ precision = extract_precision(sql_type)
715
+ OID::Interval.new(precision: precision)
716
+ end
717
+ end
718
+ end
719
+
720
+ private
721
+ attr_reader :type_map
722
+
723
+ def initialize_type_map(m = type_map)
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
+
581
730
  load_additional_types
582
731
  end
583
732
 
@@ -585,7 +734,7 @@ module ActiveRecord
585
734
  def extract_value_from_default(default)
586
735
  case default
587
736
  # Quoted types
588
- when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
737
+ when /\A[(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
589
738
  # The default 'now'::date is CURRENT_DATE
590
739
  if $1 == "now" && $2 == "date"
591
740
  nil
@@ -616,70 +765,159 @@ module ActiveRecord
616
765
  !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
617
766
  end
618
767
 
768
+ # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
769
+ VALUE_LIMIT_VIOLATION = "22001"
770
+ NUMERIC_VALUE_OUT_OF_RANGE = "22003"
771
+ NOT_NULL_VIOLATION = "23502"
772
+ FOREIGN_KEY_VIOLATION = "23503"
773
+ UNIQUE_VIOLATION = "23505"
774
+ SERIALIZATION_FAILURE = "40001"
775
+ DEADLOCK_DETECTED = "40P01"
776
+ DUPLICATE_DATABASE = "42P04"
777
+ LOCK_NOT_AVAILABLE = "55P03"
778
+ QUERY_CANCELED = "57014"
779
+
780
+ def translate_exception(exception, message:, sql:, binds:)
781
+ return exception unless exception.respond_to?(:result)
782
+
783
+ case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
784
+ when nil
785
+ if exception.message.match?(/connection is closed/i) || exception.message.match?(/no connection to the server/i)
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
798
+ else
799
+ super
800
+ end
801
+ when UNIQUE_VIOLATION
802
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
803
+ when FOREIGN_KEY_VIOLATION
804
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
805
+ when VALUE_LIMIT_VIOLATION
806
+ ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
807
+ when NUMERIC_VALUE_OUT_OF_RANGE
808
+ RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
809
+ when NOT_NULL_VIOLATION
810
+ NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
811
+ when SERIALIZATION_FAILURE
812
+ SerializationFailure.new(message, sql: sql, binds: binds, connection_pool: @pool)
813
+ when DEADLOCK_DETECTED
814
+ Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
815
+ when DUPLICATE_DATABASE
816
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
817
+ when LOCK_NOT_AVAILABLE
818
+ LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
819
+ when QUERY_CANCELED
820
+ QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
821
+ else
822
+ super
823
+ end
824
+ end
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
+
833
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
834
+ if !type_map.key?(oid)
835
+ load_additional_types([oid])
836
+ end
837
+
838
+ type_map.fetch(oid, fmod, sql_type) {
839
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
840
+ Type.default_value.tap do |cast_type|
841
+ type_map.register_type(oid, cast_type)
842
+ end
843
+ }
844
+ end
845
+
619
846
  def load_additional_types(oids = nil)
620
847
  initializer = OID::TypeMapInitializer.new(type_map)
848
+ load_types_queries(initializer, oids) do |query|
849
+ execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
850
+ initializer.run(records)
851
+ end
852
+ end
853
+ end
621
854
 
855
+ def load_types_queries(initializer, oids)
622
856
  query = <<~SQL
623
857
  SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
624
858
  FROM pg_type as t
625
859
  LEFT JOIN pg_range as r ON oid = rngtypid
626
860
  SQL
627
-
628
861
  if oids
629
- query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
862
+ yield query + "WHERE t.oid IN (%s)" % oids.join(", ")
630
863
  else
631
- query += initializer.query_conditions_for_initial_load
632
- end
633
-
634
- execute_and_clear(query, "SCHEMA", []) do |records|
635
- initializer.run(records)
864
+ yield query + initializer.query_conditions_for_known_type_names
865
+ yield query + initializer.query_conditions_for_known_type_types
866
+ yield query + initializer.query_conditions_for_array_types
636
867
  end
637
868
  end
638
869
 
639
- FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
870
+ FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
640
871
 
641
- def execute_and_clear(sql, name, binds, prepare: false)
642
- if preventing_writes? && write_query?(sql)
643
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
644
- end
872
+ def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
873
+ sql = transform_query(sql)
874
+ check_if_write_query(sql)
645
875
 
646
- if without_prepared_statement?(binds)
647
- result = exec_no_cache(sql, name, [])
648
- elsif !prepare
649
- result = exec_no_cache(sql, name, binds)
876
+ if !prepare || without_prepared_statement?(binds)
877
+ result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
650
878
  else
651
- result = exec_cache(sql, name, binds)
879
+ result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
880
+ end
881
+ begin
882
+ ret = yield result
883
+ ensure
884
+ result.clear
652
885
  end
653
- ret = yield result
654
- result.clear
655
886
  ret
656
887
  end
657
888
 
658
- def exec_no_cache(sql, name, binds)
659
- materialize_transactions
889
+ def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
890
+ mark_transaction_written_if_write(sql)
660
891
 
661
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
892
+ # make sure we carry over any changes to ActiveRecord.default_timezone that have been
662
893
  # made since we established the connection
663
894
  update_typemap_for_default_timezone
664
895
 
665
896
  type_casted_binds = type_casted_binds(binds)
666
- log(sql, name, binds, type_casted_binds) do
667
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
668
- @connection.exec_params(sql, type_casted_binds)
897
+ log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
898
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
899
+ result = conn.exec_params(sql, type_casted_binds)
900
+ verified!
901
+ notification_payload[:row_count] = result.count
902
+ result
669
903
  end
670
904
  end
671
905
  end
672
906
 
673
- def exec_cache(sql, name, binds)
674
- materialize_transactions
907
+ def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
908
+ mark_transaction_written_if_write(sql)
909
+
675
910
  update_typemap_for_default_timezone
676
911
 
677
- stmt_key = prepare_statement(sql, binds)
678
- type_casted_binds = type_casted_binds(binds)
912
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
913
+ stmt_key = prepare_statement(sql, binds, conn)
914
+ type_casted_binds = type_casted_binds(binds)
679
915
 
680
- log(sql, name, binds, type_casted_binds, stmt_key) do
681
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
682
- @connection.exec_prepared(stmt_key, type_casted_binds)
916
+ log(sql, name, binds, type_casted_binds, stmt_key, async: async) do |notification_payload|
917
+ result = conn.exec_prepared(stmt_key, type_casted_binds)
918
+ verified!
919
+ notification_payload[:row_count] = result.count
920
+ result
683
921
  end
684
922
  end
685
923
  rescue ActiveRecord::StatementInvalid => e
@@ -688,7 +926,7 @@ module ActiveRecord
688
926
  # Nothing we can do if we are in a transaction because all commands
689
927
  # will raise InFailedSQLTransaction
690
928
  if in_transaction?
691
- raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
929
+ raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message, connection_pool: @pool)
692
930
  else
693
931
  @lock.synchronize do
694
932
  # outside of transactions we can simply flush this query and retry
@@ -707,11 +945,10 @@ module ActiveRecord
707
945
  #
708
946
  # Check here for more details:
709
947
  # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
710
- CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
711
948
  def is_cached_plan_failure?(e)
712
949
  pgerror = e.cause
713
- code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
714
- code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
950
+ pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
951
+ pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
715
952
  rescue
716
953
  false
717
954
  end
@@ -728,67 +965,100 @@ module ActiveRecord
728
965
 
729
966
  # Prepare the statement if it hasn't been prepared, return
730
967
  # the statement key.
731
- def prepare_statement(sql, binds)
732
- @lock.synchronize do
733
- sql_key = sql_key(sql)
734
- unless @statements.key? sql_key
735
- nextkey = @statements.next_key
736
- begin
737
- @connection.prepare nextkey, sql
738
- rescue => e
739
- raise translate_exception_class(e, sql, binds)
740
- end
741
- # Clear the queue
742
- @connection.get_last_result
743
- @statements[sql_key] = nextkey
968
+ def prepare_statement(sql, binds, conn)
969
+ sql_key = sql_key(sql)
970
+ unless @statements.key? sql_key
971
+ nextkey = @statements.next_key
972
+ begin
973
+ conn.prepare nextkey, sql
974
+ rescue => e
975
+ raise translate_exception_class(e, sql, binds)
744
976
  end
745
- @statements[sql_key]
977
+ # Clear the queue
978
+ conn.get_last_result
979
+ @statements[sql_key] = nextkey
746
980
  end
981
+ @statements[sql_key]
747
982
  end
748
983
 
749
984
  # Connects to a PostgreSQL server and sets up the adapter depending on the
750
985
  # connected server's characteristics.
751
986
  def connect
752
- @connection = PG.connect(@connection_parameters)
753
- configure_connection
754
- add_pg_encoders
755
- add_pg_decoders
987
+ @raw_connection = self.class.new_client(@connection_parameters)
988
+ rescue ConnectionNotEstablished => ex
989
+ raise ex.set_pool(@pool)
990
+ end
991
+
992
+ def reconnect
993
+ begin
994
+ @raw_connection&.reset
995
+ rescue PG::ConnectionBad
996
+ @raw_connection = nil
997
+ end
998
+
999
+ connect unless @raw_connection
756
1000
  end
757
1001
 
758
1002
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
759
1003
  # This is called by #connect and should not be called manually.
760
1004
  def configure_connection
1005
+ super
1006
+
761
1007
  if @config[:encoding]
762
- @connection.set_client_encoding(@config[:encoding])
1008
+ @raw_connection.set_client_encoding(@config[:encoding])
763
1009
  end
764
1010
  self.client_min_messages = @config[:min_messages] || "warning"
765
1011
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
766
1012
 
1013
+ unless ActiveRecord.db_warnings_action.nil?
1014
+ @raw_connection.set_notice_receiver do |result|
1015
+ message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY)
1016
+ code = result.error_field(PG::Result::PG_DIAG_SQLSTATE)
1017
+ level = result.error_field(PG::Result::PG_DIAG_SEVERITY)
1018
+ @notice_receiver_sql_warnings << SQLWarning.new(message, code, level, nil, @pool)
1019
+ end
1020
+ end
1021
+
767
1022
  # Use standard-conforming strings so we don't have to do the E'...' dance.
768
1023
  set_standard_conforming_strings
769
1024
 
770
1025
  variables = @config.fetch(:variables, {}).stringify_keys
771
1026
 
772
- # If using Active Record's time zone support configure the connection to return
773
- # TIMESTAMP WITH ZONE types in UTC.
774
- unless variables["timezone"]
775
- if ActiveRecord::Base.default_timezone == :utc
776
- variables["timezone"] = "UTC"
777
- elsif @local_tz
778
- variables["timezone"] = @local_tz
779
- end
780
- end
1027
+ # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
1028
+ internal_execute("SET intervalstyle = iso_8601")
781
1029
 
782
1030
  # SET statements from :variables config hash
783
1031
  # https://www.postgresql.org/docs/current/static/sql-set.html
784
1032
  variables.map do |k, v|
785
1033
  if v == ":default" || v == :default
786
1034
  # Sets the value to the global or compile default
787
- execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
1035
+ internal_execute("SET SESSION #{k} TO DEFAULT")
788
1036
  elsif !v.nil?
789
- execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
1037
+ internal_execute("SET SESSION #{k} TO #{quote(v)}")
790
1038
  end
791
1039
  end
1040
+
1041
+ add_pg_encoders
1042
+ add_pg_decoders
1043
+
1044
+ reload_type_map
1045
+ end
1046
+
1047
+ def reconfigure_connection_timezone
1048
+ variables = @config.fetch(:variables, {}).stringify_keys
1049
+
1050
+ # If it's been directly configured as a connection variable, we don't
1051
+ # need to do anything here; it will be set up by configure_connection
1052
+ # and then never changed.
1053
+ return if variables["timezone"]
1054
+
1055
+ # If using Active Record's time zone support configure the connection
1056
+ # to return TIMESTAMP WITH ZONE types in UTC.
1057
+ if default_timezone == :utc
1058
+ internal_execute("SET SESSION timezone TO 'UTC'")
1059
+ else
1060
+ internal_execute("SET SESSION timezone TO DEFAULT")
1061
+ end
792
1062
  end
793
1063
 
794
1064
  # Returns the list of a table's column names, data types, and default values.
@@ -813,7 +1083,9 @@ module ActiveRecord
813
1083
  query(<<~SQL, "SCHEMA")
814
1084
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
815
1085
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
816
- c.collname, col_description(a.attrelid, a.attnum) AS comment
1086
+ c.collname, col_description(a.attrelid, a.attnum) AS comment,
1087
+ #{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
1088
+ #{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
817
1089
  FROM pg_attribute a
818
1090
  LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
819
1091
  LEFT JOIN pg_type t ON a.atttypid = t.oid
@@ -824,37 +1096,37 @@ module ActiveRecord
824
1096
  SQL
825
1097
  end
826
1098
 
827
- def extract_table_ref_from_insert_sql(sql)
828
- sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
829
- $1.strip if $1
830
- end
831
-
832
1099
  def arel_visitor
833
1100
  Arel::Visitors::PostgreSQL.new(self)
834
1101
  end
835
1102
 
836
1103
  def build_statement_pool
837
- StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
1104
+ StatementPool.new(self, self.class.type_cast_config_to_integer(@config[:statement_limit]))
838
1105
  end
839
1106
 
840
1107
  def can_perform_case_insensitive_comparison_for?(column)
841
- @case_insensitive_cache ||= {}
842
- @case_insensitive_cache[column.sql_type] ||= begin
843
- sql = <<~SQL
844
- SELECT exists(
845
- SELECT * FROM pg_proc
846
- WHERE proname = 'lower'
847
- AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
848
- ) OR exists(
849
- SELECT * FROM pg_proc
850
- INNER JOIN pg_cast
851
- ON ARRAY[casttarget]::oidvector = proargtypes
852
- WHERE proname = 'lower'
853
- AND castsource = #{quote column.sql_type}::regtype
854
- )
855
- SQL
856
- execute_and_clear(sql, "SCHEMA", []) do |result|
857
- result.getvalue(0, 0)
1108
+ # NOTE: citext is an exception. It is possible to perform a
1109
+ # case-insensitive comparison using `LOWER()`, but it is
1110
+ # unnecessary, as `citext` is case-insensitive by definition.
1111
+ @case_insensitive_cache ||= { "citext" => false }
1112
+ @case_insensitive_cache.fetch(column.sql_type) do
1113
+ @case_insensitive_cache[column.sql_type] = begin
1114
+ sql = <<~SQL
1115
+ SELECT exists(
1116
+ SELECT * FROM pg_proc
1117
+ WHERE proname = 'lower'
1118
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
1119
+ ) OR exists(
1120
+ SELECT * FROM pg_proc
1121
+ INNER JOIN pg_cast
1122
+ ON ARRAY[casttarget]::oidvector = proargtypes
1123
+ WHERE proname = 'lower'
1124
+ AND castsource = #{quote column.sql_type}::regtype
1125
+ )
1126
+ SQL
1127
+ execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1128
+ result.getvalue(0, 0)
1129
+ end
858
1130
  end
859
1131
  end
860
1132
  end
@@ -864,23 +1136,30 @@ module ActiveRecord
864
1136
  map[Integer] = PG::TextEncoder::Integer.new
865
1137
  map[TrueClass] = PG::TextEncoder::Boolean.new
866
1138
  map[FalseClass] = PG::TextEncoder::Boolean.new
867
- @connection.type_map_for_queries = map
1139
+ @raw_connection.type_map_for_queries = map
868
1140
  end
869
1141
 
870
1142
  def update_typemap_for_default_timezone
871
- if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
872
- decoder_class = ActiveRecord::Base.default_timezone == :utc ?
1143
+ if @raw_connection && @mapped_default_timezone != default_timezone && @timestamp_decoder
1144
+ decoder_class = default_timezone == :utc ?
873
1145
  PG::TextDecoder::TimestampUtc :
874
1146
  PG::TextDecoder::TimestampWithoutTimeZone
875
1147
 
876
- @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
877
- @connection.type_map_for_results.add_coder(@timestamp_decoder)
878
- @default_timezone = ActiveRecord::Base.default_timezone
1148
+ @timestamp_decoder = decoder_class.new(**@timestamp_decoder.to_h)
1149
+ @raw_connection.type_map_for_results.add_coder(@timestamp_decoder)
1150
+
1151
+ @mapped_default_timezone = default_timezone
1152
+
1153
+ # if default timezone has changed, we need to reconfigure the connection
1154
+ # (specifically, the session time zone)
1155
+ reconfigure_connection_timezone
1156
+
1157
+ true
879
1158
  end
880
1159
  end
881
1160
 
882
1161
  def add_pg_decoders
883
- @default_timezone = nil
1162
+ @mapped_default_timezone = nil
884
1163
  @timestamp_decoder = nil
885
1164
 
886
1165
  coders_by_name = {
@@ -890,14 +1169,12 @@ module ActiveRecord
890
1169
  "oid" => PG::TextDecoder::Integer,
891
1170
  "float4" => PG::TextDecoder::Float,
892
1171
  "float8" => PG::TextDecoder::Float,
1172
+ "numeric" => PG::TextDecoder::Numeric,
893
1173
  "bool" => PG::TextDecoder::Boolean,
1174
+ "timestamp" => PG::TextDecoder::TimestampUtc,
1175
+ "timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
894
1176
  }
895
-
896
- if defined?(PG::TextDecoder::TimestampUtc)
897
- # Use native PG encoders available since pg-1.1
898
- coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
899
- coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
900
- end
1177
+ coders_by_name["date"] = PG::TextDecoder::Date if decode_dates
901
1178
 
902
1179
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
903
1180
  query = <<~SQL % known_coder_types.join(", ")
@@ -905,15 +1182,18 @@ module ActiveRecord
905
1182
  FROM pg_type as t
906
1183
  WHERE t.typname IN (%s)
907
1184
  SQL
908
- coders = execute_and_clear(query, "SCHEMA", []) do |result|
909
- result
910
- .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
911
- .compact
1185
+ coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1186
+ result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
912
1187
  end
913
1188
 
914
1189
  map = PG::TypeMapByOid.new
915
1190
  coders.each { |coder| map.add_coder(coder) }
916
- @connection.type_map_for_results = map
1191
+ @raw_connection.type_map_for_results = map
1192
+
1193
+ @type_map_for_results = PG::TypeMapByOid.new
1194
+ @type_map_for_results.default_type_map = map
1195
+ @type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
1196
+ @type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
917
1197
 
918
1198
  # extract timestamp decoder for use in update_typemap_for_default_timezone
919
1199
  @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
@@ -925,6 +1205,14 @@ module ActiveRecord
925
1205
  coder_class.new(oid: row["oid"].to_i, name: row["typname"])
926
1206
  end
927
1207
 
1208
+ class MoneyDecoder < PG::SimpleDecoder # :nodoc:
1209
+ TYPE = OID::Money.new
1210
+
1211
+ def decode(value, tuple = nil, field = nil)
1212
+ TYPE.deserialize(value)
1213
+ end
1214
+ end
1215
+
928
1216
  ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
929
1217
  ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
930
1218
  ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
@@ -937,6 +1225,7 @@ module ActiveRecord
937
1225
  ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
938
1226
  ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
939
1227
  ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
1228
+ ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
940
1229
  ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
941
1230
  ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
942
1231
  ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
@@ -945,5 +1234,6 @@ module ActiveRecord
945
1234
  ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
946
1235
  ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
947
1236
  end
1237
+ ActiveSupport.run_load_hooks(:active_record_postgresqladapter, PostgreSQLAdapter)
948
1238
  end
949
1239
  end