activerecord 5.0.7.2 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (363) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +829 -2015
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +11 -9
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record.rb +37 -29
  8. data/lib/active_record/aggregations.rb +249 -247
  9. data/lib/active_record/association_relation.rb +30 -18
  10. data/lib/active_record/associations.rb +1714 -1596
  11. data/lib/active_record/associations/alias_tracker.rb +36 -42
  12. data/lib/active_record/associations/association.rb +143 -68
  13. data/lib/active_record/associations/association_scope.rb +98 -94
  14. data/lib/active_record/associations/belongs_to_association.rb +76 -46
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -28
  17. data/lib/active_record/associations/builder/belongs_to.rb +52 -60
  18. data/lib/active_record/associations/builder/collection_association.rb +12 -22
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
  20. data/lib/active_record/associations/builder/has_many.rb +10 -2
  21. data/lib/active_record/associations/builder/has_one.rb +35 -2
  22. data/lib/active_record/associations/builder/singular_association.rb +5 -1
  23. data/lib/active_record/associations/collection_association.rb +104 -259
  24. data/lib/active_record/associations/collection_proxy.rb +169 -125
  25. data/lib/active_record/associations/foreign_association.rb +22 -0
  26. data/lib/active_record/associations/has_many_association.rb +46 -31
  27. data/lib/active_record/associations/has_many_through_association.rb +66 -46
  28. data/lib/active_record/associations/has_one_association.rb +71 -52
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +169 -180
  31. data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +97 -104
  35. data/lib/active_record/associations/preloader/association.rb +109 -97
  36. data/lib/active_record/associations/preloader/through_association.rb +77 -76
  37. data/lib/active_record/associations/singular_association.rb +12 -45
  38. data/lib/active_record/associations/through_association.rb +27 -15
  39. data/lib/active_record/attribute_assignment.rb +55 -60
  40. data/lib/active_record/attribute_methods.rb +111 -141
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +172 -112
  43. data/lib/active_record/attribute_methods/primary_key.rb +88 -91
  44. data/lib/active_record/attribute_methods/query.rb +6 -8
  45. data/lib/active_record/attribute_methods/read.rb +18 -50
  46. data/lib/active_record/attribute_methods/serialization.rb +38 -10
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
  48. data/lib/active_record/attribute_methods/write.rb +25 -32
  49. data/lib/active_record/attributes.rb +69 -31
  50. data/lib/active_record/autosave_association.rb +102 -66
  51. data/lib/active_record/base.rb +16 -25
  52. data/lib/active_record/callbacks.rb +202 -43
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -12
  55. data/lib/active_record/connection_adapters.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
  69. data/lib/active_record/connection_adapters/column.rb +55 -13
  70. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  71. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
  82. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  83. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
  85. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
  86. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
  93. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
  95. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
  98. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
  106. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
  107. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  109. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  110. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
  121. data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
  123. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  124. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
  131. data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
  132. data/lib/active_record/connection_handling.rb +287 -45
  133. data/lib/active_record/core.rb +385 -181
  134. data/lib/active_record/counter_cache.rb +60 -28
  135. data/lib/active_record/database_configurations.rb +272 -0
  136. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  137. data/lib/active_record/database_configurations/database_config.rb +80 -0
  138. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  139. data/lib/active_record/database_configurations/url_config.rb +53 -0
  140. data/lib/active_record/delegated_type.rb +209 -0
  141. data/lib/active_record/destroy_association_async_job.rb +36 -0
  142. data/lib/active_record/dynamic_matchers.rb +87 -87
  143. data/lib/active_record/enum.rb +122 -47
  144. data/lib/active_record/errors.rb +153 -22
  145. data/lib/active_record/explain.rb +13 -8
  146. data/lib/active_record/explain_registry.rb +3 -1
  147. data/lib/active_record/explain_subscriber.rb +9 -4
  148. data/lib/active_record/fixture_set/file.rb +20 -22
  149. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  150. data/lib/active_record/fixture_set/render_context.rb +17 -0
  151. data/lib/active_record/fixture_set/table_row.rb +152 -0
  152. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  153. data/lib/active_record/fixtures.rb +246 -507
  154. data/lib/active_record/gem_version.rb +6 -4
  155. data/lib/active_record/inheritance.rb +168 -95
  156. data/lib/active_record/insert_all.rb +208 -0
  157. data/lib/active_record/integration.rb +114 -25
  158. data/lib/active_record/internal_metadata.rb +30 -24
  159. data/lib/active_record/legacy_yaml_adapter.rb +11 -5
  160. data/lib/active_record/locking/optimistic.rb +81 -85
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +68 -31
  163. data/lib/active_record/middleware/database_selector.rb +77 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  166. data/lib/active_record/migration.rb +439 -342
  167. data/lib/active_record/migration/command_recorder.rb +152 -98
  168. data/lib/active_record/migration/compatibility.rb +229 -60
  169. data/lib/active_record/migration/join_table.rb +8 -7
  170. data/lib/active_record/model_schema.rb +230 -122
  171. data/lib/active_record/nested_attributes.rb +213 -203
  172. data/lib/active_record/no_touching.rb +11 -2
  173. data/lib/active_record/null_relation.rb +12 -34
  174. data/lib/active_record/persistence.rb +471 -97
  175. data/lib/active_record/query_cache.rb +23 -12
  176. data/lib/active_record/querying.rb +43 -25
  177. data/lib/active_record/railtie.rb +155 -43
  178. data/lib/active_record/railties/console_sandbox.rb +2 -0
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +507 -195
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +245 -269
  183. data/lib/active_record/relation.rb +475 -324
  184. data/lib/active_record/relation/batches.rb +125 -72
  185. data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
  186. data/lib/active_record/relation/calculations.rb +267 -171
  187. data/lib/active_record/relation/delegation.rb +73 -69
  188. data/lib/active_record/relation/finder_methods.rb +238 -248
  189. data/lib/active_record/relation/from_clause.rb +7 -9
  190. data/lib/active_record/relation/merger.rb +95 -77
  191. data/lib/active_record/relation/predicate_builder.rb +109 -110
  192. data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
  193. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  194. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  195. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
  196. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  197. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  198. data/lib/active_record/relation/query_attribute.rb +33 -2
  199. data/lib/active_record/relation/query_methods.rb +654 -374
  200. data/lib/active_record/relation/record_fetch_warning.rb +8 -6
  201. data/lib/active_record/relation/spawn_methods.rb +15 -14
  202. data/lib/active_record/relation/where_clause.rb +171 -109
  203. data/lib/active_record/result.rb +88 -51
  204. data/lib/active_record/runtime_registry.rb +5 -3
  205. data/lib/active_record/sanitization.rb +73 -100
  206. data/lib/active_record/schema.rb +7 -14
  207. data/lib/active_record/schema_dumper.rb +101 -69
  208. data/lib/active_record/schema_migration.rb +16 -12
  209. data/lib/active_record/scoping.rb +20 -20
  210. data/lib/active_record/scoping/default.rb +92 -95
  211. data/lib/active_record/scoping/named.rb +39 -30
  212. data/lib/active_record/secure_token.rb +19 -9
  213. data/lib/active_record/serialization.rb +7 -3
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +80 -29
  216. data/lib/active_record/store.rb +122 -42
  217. data/lib/active_record/suppressor.rb +6 -3
  218. data/lib/active_record/table_metadata.rb +51 -39
  219. data/lib/active_record/tasks/database_tasks.rb +332 -115
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +246 -0
  225. data/lib/active_record/timestamp.rb +70 -38
  226. data/lib/active_record/touch_later.rb +26 -24
  227. data/lib/active_record/transactions.rb +121 -184
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type.rb +29 -17
  230. data/lib/active_record/type/adapter_specific_registry.rb +44 -48
  231. data/lib/active_record/type/date.rb +2 -0
  232. data/lib/active_record/type/date_time.rb +2 -0
  233. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  234. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  235. data/lib/active_record/type/internal/timezone.rb +2 -0
  236. data/lib/active_record/type/json.rb +30 -0
  237. data/lib/active_record/type/serialized.rb +20 -9
  238. data/lib/active_record/type/text.rb +11 -0
  239. data/lib/active_record/type/time.rb +12 -1
  240. data/lib/active_record/type/type_map.rb +14 -17
  241. data/lib/active_record/type/unsigned_integer.rb +16 -0
  242. data/lib/active_record/type_caster.rb +4 -2
  243. data/lib/active_record/type_caster/connection.rb +17 -13
  244. data/lib/active_record/type_caster/map.rb +10 -6
  245. data/lib/active_record/validations.rb +8 -5
  246. data/lib/active_record/validations/absence.rb +2 -0
  247. data/lib/active_record/validations/associated.rb +4 -3
  248. data/lib/active_record/validations/length.rb +2 -0
  249. data/lib/active_record/validations/numericality.rb +35 -0
  250. data/lib/active_record/validations/presence.rb +4 -2
  251. data/lib/active_record/validations/uniqueness.rb +52 -45
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/arel.rb +54 -0
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes.rb +70 -0
  269. data/lib/arel/nodes/and.rb +32 -0
  270. data/lib/arel/nodes/ascending.rb +23 -0
  271. data/lib/arel/nodes/binary.rb +126 -0
  272. data/lib/arel/nodes/bind_param.rb +44 -0
  273. data/lib/arel/nodes/case.rb +55 -0
  274. data/lib/arel/nodes/casted.rb +62 -0
  275. data/lib/arel/nodes/comment.rb +29 -0
  276. data/lib/arel/nodes/count.rb +12 -0
  277. data/lib/arel/nodes/delete_statement.rb +45 -0
  278. data/lib/arel/nodes/descending.rb +23 -0
  279. data/lib/arel/nodes/equality.rb +15 -0
  280. data/lib/arel/nodes/extract.rb +24 -0
  281. data/lib/arel/nodes/false.rb +16 -0
  282. data/lib/arel/nodes/full_outer_join.rb +8 -0
  283. data/lib/arel/nodes/function.rb +44 -0
  284. data/lib/arel/nodes/grouping.rb +11 -0
  285. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  286. data/lib/arel/nodes/in.rb +15 -0
  287. data/lib/arel/nodes/infix_operation.rb +92 -0
  288. data/lib/arel/nodes/inner_join.rb +8 -0
  289. data/lib/arel/nodes/insert_statement.rb +37 -0
  290. data/lib/arel/nodes/join_source.rb +20 -0
  291. data/lib/arel/nodes/matches.rb +18 -0
  292. data/lib/arel/nodes/named_function.rb +23 -0
  293. data/lib/arel/nodes/node.rb +51 -0
  294. data/lib/arel/nodes/node_expression.rb +13 -0
  295. data/lib/arel/nodes/ordering.rb +27 -0
  296. data/lib/arel/nodes/outer_join.rb +8 -0
  297. data/lib/arel/nodes/over.rb +15 -0
  298. data/lib/arel/nodes/regexp.rb +16 -0
  299. data/lib/arel/nodes/right_outer_join.rb +8 -0
  300. data/lib/arel/nodes/select_core.rb +67 -0
  301. data/lib/arel/nodes/select_statement.rb +41 -0
  302. data/lib/arel/nodes/sql_literal.rb +19 -0
  303. data/lib/arel/nodes/string_join.rb +11 -0
  304. data/lib/arel/nodes/table_alias.rb +31 -0
  305. data/lib/arel/nodes/terminal.rb +16 -0
  306. data/lib/arel/nodes/true.rb +16 -0
  307. data/lib/arel/nodes/unary.rb +44 -0
  308. data/lib/arel/nodes/unary_operation.rb +20 -0
  309. data/lib/arel/nodes/unqualified_column.rb +22 -0
  310. data/lib/arel/nodes/update_statement.rb +41 -0
  311. data/lib/arel/nodes/values_list.rb +9 -0
  312. data/lib/arel/nodes/window.rb +126 -0
  313. data/lib/arel/nodes/with.rb +11 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors.rb +13 -0
  321. data/lib/arel/visitors/dot.rb +308 -0
  322. data/lib/arel/visitors/mysql.rb +93 -0
  323. data/lib/arel/visitors/postgresql.rb +120 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +899 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/rails/generators/active_record.rb +7 -5
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration.rb +22 -3
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
  333. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
  334. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
  335. data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. metadata +141 -57
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute.rb +0 -213
  348. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  349. data/lib/active_record/attribute_decorators.rb +0 -67
  350. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/attribute_set/builder.rb +0 -132
  353. data/lib/active_record/collection_cache_key.rb +0 -50
  354. data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
  355. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
  356. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  359. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
  360. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  361. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  362. data/lib/active_record/relation/where_clause_factory.rb +0 -38
  363. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,35 +1,44 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
4
+ # :stopdoc:
2
5
  module ConnectionAdapters
3
- class PostgreSQLTypeMetadata < DelegateClass(SqlTypeMetadata)
4
- attr_reader :oid, :fmod, :array
6
+ module PostgreSQL
7
+ class TypeMetadata < DelegateClass(SqlTypeMetadata)
8
+ undef to_yaml if method_defined?(:to_yaml)
5
9
 
6
- def initialize(type_metadata, oid: nil, fmod: nil)
7
- super(type_metadata)
8
- @type_metadata = type_metadata
9
- @oid = oid
10
- @fmod = fmod
11
- @array = /\[\]$/ === type_metadata.sql_type
12
- end
10
+ include Deduplicable
13
11
 
14
- def sql_type
15
- super.gsub(/\[\]$/, "".freeze)
16
- end
12
+ attr_reader :oid, :fmod
17
13
 
18
- def ==(other)
19
- other.is_a?(PostgreSQLTypeMetadata) &&
20
- attributes_for_hash == other.attributes_for_hash
21
- end
22
- alias eql? ==
14
+ def initialize(type_metadata, oid: nil, fmod: nil)
15
+ super(type_metadata)
16
+ @oid = oid
17
+ @fmod = fmod
18
+ end
23
19
 
24
- def hash
25
- attributes_for_hash.hash
26
- end
20
+ def ==(other)
21
+ other.is_a?(TypeMetadata) &&
22
+ __getobj__ == other.__getobj__ &&
23
+ oid == other.oid &&
24
+ fmod == other.fmod
25
+ end
26
+ alias eql? ==
27
27
 
28
- protected
28
+ def hash
29
+ TypeMetadata.hash ^
30
+ __getobj__.hash ^
31
+ oid.hash ^
32
+ fmod.hash
33
+ end
29
34
 
30
- def attributes_for_hash
31
- [self.class, @type_metadata, oid, fmod]
35
+ private
36
+ def deduplicated
37
+ __setobj__(__getobj__.deduplicate)
38
+ super
39
+ end
32
40
  end
33
41
  end
42
+ PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
34
43
  end
35
44
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
@@ -35,6 +37,11 @@ module ActiveRecord
35
37
  end
36
38
 
37
39
  protected
40
+ def parts
41
+ @parts ||= [@schema, @identifier].compact
42
+ end
43
+
44
+ private
38
45
  def unquote(part)
39
46
  if part && part.start_with?('"')
40
47
  part[1..-2]
@@ -42,10 +49,6 @@ module ActiveRecord
42
49
  part
43
50
  end
44
51
  end
45
-
46
- def parts
47
- @parts ||= [@schema, @identifier].compact
48
- end
49
52
  end
50
53
 
51
54
  module Utils # :nodoc:
@@ -53,7 +56,7 @@ module ActiveRecord
53
56
 
54
57
  # Returns an instance of <tt>ActiveRecord::ConnectionAdapters::PostgreSQL::Name</tt>
55
58
  # extracted from +string+.
56
- # +schema+ is nil if not specified in +string+.
59
+ # +schema+ is +nil+ if not specified in +string+.
57
60
  # +schema+ and +identifier+ exclude surrounding quotes (regardless of whether provided in +string+)
58
61
  # +string+ supports the range of schema/table references understood by PostgreSQL, for example:
59
62
  #
@@ -64,7 +67,7 @@ module ActiveRecord
64
67
  # * <tt>"schema_name".table_name</tt>
65
68
  # * <tt>"schema.name"."table name"</tt>
66
69
  def extract_schema_qualified_name(string)
67
- schema, table = string.scan(/[^".\s]+|"[^"]*"/)
70
+ schema, table = string.scan(/[^".]+|"[^"]*"/)
68
71
  if table.nil?
69
72
  table = schema
70
73
  schema = nil
@@ -1,28 +1,29 @@
1
- # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
2
- gem "pg", ">= 0.18", "< 2.0"
3
- require 'pg'
1
+ # frozen_string_literal: true
4
2
 
3
+ gem "pg", "~> 1.1"
4
+ require "pg"
5
+
6
+ require "active_support/core_ext/object/try"
5
7
  require "active_record/connection_adapters/abstract_adapter"
8
+ require "active_record/connection_adapters/statement_pool"
6
9
  require "active_record/connection_adapters/postgresql/column"
7
10
  require "active_record/connection_adapters/postgresql/database_statements"
8
11
  require "active_record/connection_adapters/postgresql/explain_pretty_printer"
9
12
  require "active_record/connection_adapters/postgresql/oid"
10
13
  require "active_record/connection_adapters/postgresql/quoting"
11
14
  require "active_record/connection_adapters/postgresql/referential_integrity"
15
+ require "active_record/connection_adapters/postgresql/schema_creation"
12
16
  require "active_record/connection_adapters/postgresql/schema_definitions"
13
17
  require "active_record/connection_adapters/postgresql/schema_dumper"
14
18
  require "active_record/connection_adapters/postgresql/schema_statements"
15
19
  require "active_record/connection_adapters/postgresql/type_metadata"
16
20
  require "active_record/connection_adapters/postgresql/utils"
17
- require "active_record/connection_adapters/statement_pool"
18
21
 
19
22
  module ActiveRecord
20
23
  module ConnectionHandling # :nodoc:
21
24
  # Establishes a connection to the database that's used by all Active Record objects
22
25
  def postgresql_connection(config)
23
- conn_params = config.symbolize_keys
24
-
25
- conn_params.delete_if { |_, v| v.nil? }
26
+ conn_params = config.symbolize_keys.compact
26
27
 
27
28
  # Map ActiveRecords param names to PGs.
28
29
  conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
@@ -32,14 +33,17 @@ module ActiveRecord
32
33
  valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
33
34
  conn_params.slice!(*valid_conn_param_keys)
34
35
 
35
- # The postgres drivers don't allow the creation of an unconnected PG::Connection object,
36
- # so just pass a nil connection object for the time being.
37
- ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
36
+ ConnectionAdapters::PostgreSQLAdapter.new(
37
+ ConnectionAdapters::PostgreSQLAdapter.new_client(conn_params),
38
+ logger,
39
+ conn_params,
40
+ config,
41
+ )
38
42
  end
39
43
  end
40
44
 
41
45
  module ConnectionAdapters
42
- # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
46
+ # The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
43
47
  #
44
48
  # Options:
45
49
  #
@@ -61,19 +65,44 @@ module ActiveRecord
61
65
  # defaults to true.
62
66
  #
63
67
  # Any further options are used as connection parameters to libpq. See
64
- # http://www.postgresql.org/docs/current/static/libpq-connect.html for the
68
+ # https://www.postgresql.org/docs/current/static/libpq-connect.html for the
65
69
  # list of parameters.
66
70
  #
67
71
  # In addition, default connection parameters of libpq can be set per environment variables.
68
- # See http://www.postgresql.org/docs/current/static/libpq-envars.html .
72
+ # See https://www.postgresql.org/docs/current/static/libpq-envars.html .
69
73
  class PostgreSQLAdapter < AbstractAdapter
70
- ADAPTER_NAME = 'PostgreSQL'.freeze
74
+ ADAPTER_NAME = "PostgreSQL"
75
+
76
+ class << self
77
+ def new_client(conn_params)
78
+ PG.connect(conn_params)
79
+ rescue ::PG::Error => error
80
+ if conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
81
+ raise ActiveRecord::NoDatabaseError
82
+ else
83
+ raise ActiveRecord::ConnectionNotEstablished, error.message
84
+ end
85
+ end
86
+ end
87
+
88
+ ##
89
+ # :singleton-method:
90
+ # PostgreSQL allows the creation of "unlogged" tables, which do not record
91
+ # data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
92
+ # but significantly increases the risk of data loss if the database
93
+ # crashes. As a result, this should not be used in production
94
+ # 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:
97
+ #
98
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
99
+ class_attribute :create_unlogged_tables, default: false
71
100
 
72
101
  NATIVE_DATABASE_TYPES = {
73
- primary_key: "serial primary key",
102
+ primary_key: "bigserial primary key",
74
103
  string: { name: "character varying" },
75
104
  text: { name: "text" },
76
- integer: { name: "integer" },
105
+ integer: { name: "integer", limit: 4 },
77
106
  float: { name: "float" },
78
107
  decimal: { name: "decimal" },
79
108
  datetime: { name: "timestamp" },
@@ -108,6 +137,8 @@ module ActiveRecord
108
137
  bit: { name: "bit" },
109
138
  bit_varying: { name: "bit varying" },
110
139
  money: { name: "money" },
140
+ interval: { name: "interval" },
141
+ oid: { name: "oid" },
111
142
  }
112
143
 
113
144
  OID = PostgreSQL::OID #:nodoc:
@@ -116,19 +147,8 @@ module ActiveRecord
116
147
  include PostgreSQL::ReferentialIntegrity
117
148
  include PostgreSQL::SchemaStatements
118
149
  include PostgreSQL::DatabaseStatements
119
- include PostgreSQL::ColumnDumper
120
150
 
121
- def schema_creation # :nodoc:
122
- PostgreSQL::SchemaCreation.new self
123
- end
124
-
125
- def arel_visitor # :nodoc:
126
- Arel::Visitors::PostgreSQL.new(self)
127
- end
128
-
129
- # Returns true, since this connection adapter supports prepared statement
130
- # caching.
131
- def supports_statement_cache?
151
+ def supports_bulk_alter?
132
152
  true
133
153
  end
134
154
 
@@ -136,6 +156,10 @@ module ActiveRecord
136
156
  true
137
157
  end
138
158
 
159
+ def supports_partitioned_indexes?
160
+ database_version >= 110_000
161
+ end
162
+
139
163
  def supports_partial_index?
140
164
  true
141
165
  end
@@ -152,6 +176,14 @@ module ActiveRecord
152
176
  true
153
177
  end
154
178
 
179
+ def supports_check_constraints?
180
+ true
181
+ end
182
+
183
+ def supports_validate_constraints?
184
+ true
185
+ end
186
+
155
187
  def supports_views?
156
188
  true
157
189
  end
@@ -161,7 +193,7 @@ module ActiveRecord
161
193
  end
162
194
 
163
195
  def supports_json?
164
- postgresql_version >= 90200
196
+ true
165
197
  end
166
198
 
167
199
  def supports_comments?
@@ -172,11 +204,22 @@ module ActiveRecord
172
204
  true
173
205
  end
174
206
 
207
+ def supports_insert_returning?
208
+ true
209
+ end
210
+
211
+ def supports_insert_on_conflict?
212
+ database_version >= 90500
213
+ end
214
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
215
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
216
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
217
+
175
218
  def index_algorithms
176
- { concurrently: 'CONCURRENTLY' }
219
+ { concurrently: "CONCURRENTLY" }
177
220
  end
178
221
 
179
- class StatementPool < ConnectionAdapters::StatementPool
222
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
180
223
  def initialize(connection, max)
181
224
  super(max)
182
225
  @connection = connection
@@ -192,9 +235,9 @@ module ActiveRecord
192
235
  end
193
236
 
194
237
  private
195
-
196
238
  def dealloc(key)
197
239
  @connection.query "DEALLOCATE #{key}" if connection_active?
240
+ rescue PG::Error
198
241
  end
199
242
 
200
243
  def connection_active?
@@ -214,35 +257,27 @@ module ActiveRecord
214
257
  @local_tz = nil
215
258
  @max_identifier_length = nil
216
259
 
217
- connect
260
+ configure_connection
218
261
  add_pg_encoders
219
- @statements = StatementPool.new @connection,
220
- self.class.type_cast_config_to_integer(config[:statement_limit])
221
-
222
- if postgresql_version < 90100
223
- raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
224
- end
225
-
226
262
  add_pg_decoders
227
263
 
228
264
  @type_map = Type::HashLookupTypeMap.new
229
- initialize_type_map(type_map)
230
- @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
265
+ initialize_type_map
266
+ @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
231
267
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
232
268
  end
233
269
 
234
- # Clears the prepared statements cache.
235
- def clear_cache!
236
- @statements.clear
237
- end
238
-
239
- def truncate(table_name, name = nil)
240
- exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
270
+ def self.database_exists?(config)
271
+ !!ActiveRecord::Base.postgresql_connection(config)
272
+ rescue ActiveRecord::NoDatabaseError
273
+ false
241
274
  end
242
275
 
243
276
  # Is this connection alive and ready for queries?
244
277
  def active?
245
- @connection.query 'SELECT 1'
278
+ @lock.synchronize do
279
+ @connection.query "SELECT 1"
280
+ end
246
281
  true
247
282
  rescue PG::Error
248
283
  false
@@ -250,44 +285,48 @@ module ActiveRecord
250
285
 
251
286
  # Close then reopen the connection.
252
287
  def reconnect!
253
- super
254
- @connection.reset
255
- configure_connection
288
+ @lock.synchronize do
289
+ super
290
+ @connection.reset
291
+ configure_connection
292
+ rescue PG::ConnectionBad
293
+ connect
294
+ end
256
295
  end
257
296
 
258
297
  def reset!
259
- clear_cache!
260
- reset_transaction
261
- unless @connection.transaction_status == ::PG::PQTRANS_IDLE
262
- @connection.query 'ROLLBACK'
298
+ @lock.synchronize do
299
+ clear_cache!
300
+ reset_transaction
301
+ unless @connection.transaction_status == ::PG::PQTRANS_IDLE
302
+ @connection.query "ROLLBACK"
303
+ end
304
+ @connection.query "DISCARD ALL"
305
+ configure_connection
263
306
  end
264
- @connection.query 'DISCARD ALL'
265
- configure_connection
266
307
  end
267
308
 
268
309
  # Disconnects from the database if already connected. Otherwise, this
269
310
  # method does nothing.
270
311
  def disconnect!
312
+ @lock.synchronize do
313
+ super
314
+ @connection.close rescue nil
315
+ end
316
+ end
317
+
318
+ def discard! # :nodoc:
271
319
  super
272
- @connection.close rescue nil
320
+ @connection.socket_io.reopen(IO::NULL) rescue nil
321
+ @connection = nil
273
322
  end
274
323
 
275
324
  def native_database_types #:nodoc:
276
325
  NATIVE_DATABASE_TYPES
277
326
  end
278
327
 
279
- # Returns true, since this connection adapter supports migrations.
280
- def supports_migrations?
281
- true
282
- end
283
-
284
- # Does PostgreSQL support finding primary key on non-Active Record tables?
285
- def supports_primary_key? #:nodoc:
286
- true
287
- end
288
-
289
328
  def set_standard_conforming_strings
290
- execute('SET standard_conforming_strings = on', 'SCHEMA')
329
+ execute("SET standard_conforming_strings = on", "SCHEMA")
291
330
  end
292
331
 
293
332
  def supports_ddl_transactions?
@@ -306,27 +345,45 @@ module ActiveRecord
306
345
  true
307
346
  end
308
347
 
309
- # Range datatypes weren't introduced until PostgreSQL 9.2
310
- def supports_ranges?
311
- postgresql_version >= 90200
348
+ def supports_materialized_views?
349
+ true
312
350
  end
313
351
 
314
- def supports_materialized_views?
315
- postgresql_version >= 90300
352
+ def supports_foreign_tables?
353
+ true
354
+ end
355
+
356
+ def supports_pgcrypto_uuid?
357
+ database_version >= 90400
358
+ end
359
+
360
+ def supports_optimizer_hints?
361
+ unless defined?(@has_pg_hint_plan)
362
+ @has_pg_hint_plan = extension_available?("pg_hint_plan")
363
+ end
364
+ @has_pg_hint_plan
365
+ end
366
+
367
+ def supports_common_table_expressions?
368
+ true
369
+ end
370
+
371
+ def supports_lazy_transactions?
372
+ true
316
373
  end
317
374
 
318
375
  def get_advisory_lock(lock_id) # :nodoc:
319
376
  unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
320
- raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
377
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
321
378
  end
322
- select_value("SELECT pg_try_advisory_lock(#{lock_id});")
379
+ query_value("SELECT pg_try_advisory_lock(#{lock_id})")
323
380
  end
324
381
 
325
382
  def release_advisory_lock(lock_id) # :nodoc:
326
383
  unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
327
- raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
384
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
328
385
  end
329
- select_value("SELECT pg_advisory_unlock(#{lock_id})")
386
+ query_value("SELECT pg_advisory_unlock(#{lock_id})")
330
387
  end
331
388
 
332
389
  def enable_extension(name)
@@ -341,150 +398,168 @@ module ActiveRecord
341
398
  }
342
399
  end
343
400
 
401
+ def extension_available?(name)
402
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
403
+ end
404
+
344
405
  def extension_enabled?(name)
345
- if supports_extensions?
346
- res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
347
- 'SCHEMA'
348
- res.cast_values.first
349
- end
406
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
350
407
  end
351
408
 
352
409
  def extensions
353
- if supports_extensions?
354
- exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
355
- else
356
- super
357
- end
410
+ exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
358
411
  end
359
412
 
360
413
  # Returns the configured supported identifier length supported by PostgreSQL
361
414
  def max_identifier_length
362
- @max_identifier_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
415
+ @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
363
416
  end
364
- alias table_alias_length max_identifier_length
365
- alias index_name_length max_identifier_length
366
417
 
367
418
  # Set the authorized user for this session
368
419
  def session_auth=(user)
369
420
  clear_cache!
370
- exec_query "SET SESSION AUTHORIZATION #{user}"
421
+ execute("SET SESSION AUTHORIZATION #{user}")
371
422
  end
372
423
 
373
424
  def use_insert_returning?
374
425
  @use_insert_returning
375
426
  end
376
427
 
377
- def valid_type?(type)
378
- !native_database_types[type].nil?
379
- end
380
-
381
- def update_table_definition(table_name, base) #:nodoc:
382
- PostgreSQL::Table.new(table_name, base)
428
+ # Returns the version of the connected PostgreSQL server.
429
+ def get_database_version # :nodoc:
430
+ @connection.server_version
383
431
  end
432
+ alias :postgresql_version :database_version
384
433
 
385
- def lookup_cast_type(sql_type) # :nodoc:
386
- oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
387
- super(oid)
434
+ def default_index_type?(index) # :nodoc:
435
+ index.using == :btree || super
388
436
  end
389
437
 
390
- def column_name_for_operation(operation, node) # :nodoc:
391
- OPERATION_ALIASES.fetch(operation) { operation.downcase }
392
- end
438
+ def build_insert_sql(insert) # :nodoc:
439
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
393
440
 
394
- OPERATION_ALIASES = { # :nodoc:
395
- "maximum" => "max",
396
- "minimum" => "min",
397
- "average" => "avg",
398
- }
441
+ if insert.skip_duplicates?
442
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
443
+ elsif insert.update_duplicates?
444
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
445
+ sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
446
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
447
+ end
399
448
 
400
- # Returns the version of the connected PostgreSQL server.
401
- def postgresql_version
402
- @connection.server_version
449
+ sql << " RETURNING #{insert.returning}" if insert.returning
450
+ sql
403
451
  end
404
452
 
405
- protected
453
+ def check_version # :nodoc:
454
+ if database_version < 90300
455
+ raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
456
+ end
457
+ end
406
458
 
407
- # See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
459
+ private
460
+ # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
408
461
  VALUE_LIMIT_VIOLATION = "22001"
462
+ NUMERIC_VALUE_OUT_OF_RANGE = "22003"
463
+ NOT_NULL_VIOLATION = "23502"
409
464
  FOREIGN_KEY_VIOLATION = "23503"
410
465
  UNIQUE_VIOLATION = "23505"
466
+ SERIALIZATION_FAILURE = "40001"
467
+ DEADLOCK_DETECTED = "40P01"
468
+ DUPLICATE_DATABASE = "42P04"
469
+ LOCK_NOT_AVAILABLE = "55P03"
470
+ QUERY_CANCELED = "57014"
411
471
 
412
- def translate_exception(exception, message)
472
+ def translate_exception(exception, message:, sql:, binds:)
413
473
  return exception unless exception.respond_to?(:result)
414
474
 
415
475
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
476
+ when nil
477
+ if exception.message.match?(/connection is closed/i)
478
+ ConnectionNotEstablished.new(exception)
479
+ else
480
+ super
481
+ end
416
482
  when UNIQUE_VIOLATION
417
- RecordNotUnique.new(message)
483
+ RecordNotUnique.new(message, sql: sql, binds: binds)
418
484
  when FOREIGN_KEY_VIOLATION
419
- InvalidForeignKey.new(message)
485
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
420
486
  when VALUE_LIMIT_VIOLATION
421
- ValueTooLong.new(message)
487
+ ValueTooLong.new(message, sql: sql, binds: binds)
488
+ when NUMERIC_VALUE_OUT_OF_RANGE
489
+ RangeError.new(message, sql: sql, binds: binds)
490
+ when NOT_NULL_VIOLATION
491
+ NotNullViolation.new(message, sql: sql, binds: binds)
492
+ when SERIALIZATION_FAILURE
493
+ SerializationFailure.new(message, sql: sql, binds: binds)
494
+ when DEADLOCK_DETECTED
495
+ Deadlocked.new(message, sql: sql, binds: binds)
496
+ when DUPLICATE_DATABASE
497
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
498
+ when LOCK_NOT_AVAILABLE
499
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
500
+ when QUERY_CANCELED
501
+ QueryCanceled.new(message, sql: sql, binds: binds)
422
502
  else
423
503
  super
424
504
  end
425
505
  end
426
506
 
427
- private
428
-
429
- def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
507
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
430
508
  if !type_map.key?(oid)
431
- load_additional_types(type_map, [oid])
509
+ load_additional_types([oid])
432
510
  end
433
511
 
434
512
  type_map.fetch(oid, fmod, sql_type) {
435
513
  warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
436
- Type::Value.new.tap do |cast_type|
514
+ Type.default_value.tap do |cast_type|
437
515
  type_map.register_type(oid, cast_type)
438
516
  end
439
517
  }
440
518
  end
441
519
 
442
- def initialize_type_map(m) # :nodoc:
443
- register_class_with_limit m, 'int2', Type::Integer
444
- register_class_with_limit m, 'int4', Type::Integer
445
- register_class_with_limit m, 'int8', Type::Integer
446
- m.alias_type 'oid', 'int2'
447
- m.register_type 'float4', Type::Float.new
448
- m.alias_type 'float8', 'float4'
449
- m.register_type 'text', Type::Text.new
450
- register_class_with_limit m, 'varchar', Type::String
451
- m.alias_type 'char', 'varchar'
452
- m.alias_type 'name', 'varchar'
453
- m.alias_type 'bpchar', 'varchar'
454
- m.register_type 'bool', Type::Boolean.new
455
- register_class_with_limit m, 'bit', OID::Bit
456
- register_class_with_limit m, 'varbit', OID::BitVarying
457
- m.alias_type 'timestamptz', 'timestamp'
458
- m.register_type 'date', Type::Date.new
459
-
460
- m.register_type 'money', OID::Money.new
461
- m.register_type 'bytea', OID::Bytea.new
462
- m.register_type 'point', OID::Point.new
463
- m.register_type 'hstore', OID::Hstore.new
464
- m.register_type 'json', OID::Json.new
465
- m.register_type 'jsonb', OID::Jsonb.new
466
- m.register_type 'cidr', OID::Cidr.new
467
- m.register_type 'inet', OID::Inet.new
468
- m.register_type 'uuid', OID::Uuid.new
469
- m.register_type 'xml', OID::Xml.new
470
- m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
471
- m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
472
- m.register_type 'citext', OID::SpecializedString.new(:citext)
473
- m.register_type 'ltree', OID::SpecializedString.new(:ltree)
474
- m.register_type 'line', OID::SpecializedString.new(:line)
475
- m.register_type 'lseg', OID::SpecializedString.new(:lseg)
476
- m.register_type 'box', OID::SpecializedString.new(:box)
477
- m.register_type 'path', OID::SpecializedString.new(:path)
478
- m.register_type 'polygon', OID::SpecializedString.new(:polygon)
479
- m.register_type 'circle', OID::SpecializedString.new(:circle)
480
-
481
- # FIXME: why are we keeping these types as strings?
482
- m.alias_type 'interval', 'varchar'
483
-
484
- register_class_with_precision m, 'time', Type::Time
485
- register_class_with_precision m, 'timestamp', OID::DateTime
486
-
487
- m.register_type 'numeric' do |_, fmod, sql_type|
520
+ def initialize_type_map(m = type_map)
521
+ m.register_type "int2", Type::Integer.new(limit: 2)
522
+ m.register_type "int4", Type::Integer.new(limit: 4)
523
+ m.register_type "int8", Type::Integer.new(limit: 8)
524
+ m.register_type "oid", OID::Oid.new
525
+ m.register_type "float4", Type::Float.new
526
+ m.alias_type "float8", "float4"
527
+ m.register_type "text", Type::Text.new
528
+ register_class_with_limit m, "varchar", Type::String
529
+ m.alias_type "char", "varchar"
530
+ m.alias_type "name", "varchar"
531
+ m.alias_type "bpchar", "varchar"
532
+ m.register_type "bool", Type::Boolean.new
533
+ register_class_with_limit m, "bit", OID::Bit
534
+ register_class_with_limit m, "varbit", OID::BitVarying
535
+ m.alias_type "timestamptz", "timestamp"
536
+ m.register_type "date", OID::Date.new
537
+
538
+ m.register_type "money", OID::Money.new
539
+ m.register_type "bytea", OID::Bytea.new
540
+ m.register_type "point", OID::Point.new
541
+ m.register_type "hstore", OID::Hstore.new
542
+ m.register_type "json", Type::Json.new
543
+ m.register_type "jsonb", OID::Jsonb.new
544
+ m.register_type "cidr", OID::Cidr.new
545
+ m.register_type "inet", OID::Inet.new
546
+ m.register_type "uuid", OID::Uuid.new
547
+ m.register_type "xml", OID::Xml.new
548
+ m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
549
+ m.register_type "macaddr", OID::Macaddr.new
550
+ m.register_type "citext", OID::SpecializedString.new(:citext)
551
+ m.register_type "ltree", OID::SpecializedString.new(:ltree)
552
+ m.register_type "line", OID::SpecializedString.new(:line)
553
+ m.register_type "lseg", OID::SpecializedString.new(:lseg)
554
+ m.register_type "box", OID::SpecializedString.new(:box)
555
+ m.register_type "path", OID::SpecializedString.new(:path)
556
+ m.register_type "polygon", OID::SpecializedString.new(:polygon)
557
+ m.register_type "circle", OID::SpecializedString.new(:circle)
558
+
559
+ register_class_with_precision m, "time", Type::Time
560
+ register_class_with_precision m, "timestamp", OID::DateTime
561
+
562
+ m.register_type "numeric" do |_, fmod, sql_type|
488
563
  precision = extract_precision(sql_type)
489
564
  scale = extract_scale(sql_type)
490
565
 
@@ -504,78 +579,65 @@ module ActiveRecord
504
579
  end
505
580
  end
506
581
 
507
- load_additional_types(m)
508
- end
509
-
510
- def extract_limit(sql_type) # :nodoc:
511
- case sql_type
512
- when /^bigint/i, /^int8/i
513
- 8
514
- when /^smallint/i
515
- 2
516
- else
517
- super
582
+ m.register_type "interval" do |*args, sql_type|
583
+ precision = extract_precision(sql_type)
584
+ OID::Interval.new(precision: precision)
518
585
  end
586
+
587
+ load_additional_types
519
588
  end
520
589
 
521
590
  # Extracts the value from a PostgreSQL column default definition.
522
- def extract_value_from_default(default) # :nodoc:
591
+ def extract_value_from_default(default)
523
592
  case default
524
593
  # Quoted types
525
- when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
526
- # The default 'now'::date is CURRENT_DATE
527
- if $1 == "now".freeze && $2 == "date".freeze
528
- nil
529
- else
530
- $1.gsub("''".freeze, "'".freeze)
531
- end
594
+ when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
595
+ # The default 'now'::date is CURRENT_DATE
596
+ if $1 == "now" && $2 == "date"
597
+ nil
598
+ else
599
+ $1.gsub("''", "'")
600
+ end
532
601
  # Boolean types
533
- when 'true'.freeze, 'false'.freeze
534
- default
602
+ when "true", "false"
603
+ default
535
604
  # Numeric types
536
- when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
537
- $1
605
+ when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
606
+ $1
538
607
  # Object identifier types
539
- when /\A-?\d+\z/
540
- $1
541
- else
542
- # Anything else is blank, some user type, or some function
543
- # and we can't know the value of that, so return nil.
544
- nil
608
+ when /\A-?\d+\z/
609
+ $1
610
+ else
611
+ # Anything else is blank, some user type, or some function
612
+ # and we can't know the value of that, so return nil.
613
+ nil
545
614
  end
546
615
  end
547
616
 
548
- def extract_default_function(default_value, default) # :nodoc:
617
+ def extract_default_function(default_value, default)
549
618
  default if has_default_function?(default_value, default)
550
619
  end
551
620
 
552
- def has_default_function?(default_value, default) # :nodoc:
553
- !default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default)
621
+ def has_default_function?(default_value, default)
622
+ !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
554
623
  end
555
624
 
556
- def load_additional_types(type_map, oids = nil) # :nodoc:
625
+ def load_additional_types(oids = nil)
557
626
  initializer = OID::TypeMapInitializer.new(type_map)
558
627
 
559
- if supports_ranges?
560
- query = <<-SQL
561
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
562
- FROM pg_type as t
563
- LEFT JOIN pg_range as r ON oid = rngtypid
564
- SQL
565
- else
566
- query = <<-SQL
567
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
568
- FROM pg_type as t
569
- SQL
570
- end
628
+ query = <<~SQL
629
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
630
+ FROM pg_type as t
631
+ LEFT JOIN pg_range as r ON oid = rngtypid
632
+ SQL
571
633
 
572
634
  if oids
573
- query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
635
+ query += "WHERE t.oid IN (%s)" % oids.join(", ")
574
636
  else
575
- query += initializer.query_conditions_for_initial_load(type_map)
637
+ query += initializer.query_conditions_for_initial_load
576
638
  end
577
639
 
578
- execute_and_clear(query, 'SCHEMA', []) do |records|
640
+ execute_and_clear(query, "SCHEMA", []) do |records|
579
641
  initializer.run(records)
580
642
  end
581
643
  end
@@ -583,6 +645,10 @@ module ActiveRecord
583
645
  FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
584
646
 
585
647
  def execute_and_clear(sql, name, binds, prepare: false)
648
+ if preventing_writes? && write_query?(sql)
649
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
650
+ end
651
+
586
652
  if without_prepared_statement?(binds)
587
653
  result = exec_no_cache(sql, name, [])
588
654
  elsif !prepare
@@ -590,22 +656,42 @@ module ActiveRecord
590
656
  else
591
657
  result = exec_cache(sql, name, binds)
592
658
  end
593
- ret = yield result
594
- result.clear
659
+ begin
660
+ ret = yield result
661
+ ensure
662
+ result.clear
663
+ end
595
664
  ret
596
665
  end
597
666
 
598
667
  def exec_no_cache(sql, name, binds)
668
+ materialize_transactions
669
+ mark_transaction_written_if_write(sql)
670
+
671
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
672
+ # made since we established the connection
673
+ update_typemap_for_default_timezone
674
+
599
675
  type_casted_binds = type_casted_binds(binds)
600
- log(sql, name, binds, type_casted_binds) { @connection.async_exec(sql, type_casted_binds) }
676
+ log(sql, name, binds, type_casted_binds) do
677
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
678
+ @connection.exec_params(sql, type_casted_binds)
679
+ end
680
+ end
601
681
  end
602
682
 
603
683
  def exec_cache(sql, name, binds)
604
- stmt_key = prepare_statement(sql)
684
+ materialize_transactions
685
+ mark_transaction_written_if_write(sql)
686
+ update_typemap_for_default_timezone
687
+
688
+ stmt_key = prepare_statement(sql, binds)
605
689
  type_casted_binds = type_casted_binds(binds)
606
690
 
607
691
  log(sql, name, binds, type_casted_binds, stmt_key) do
608
- @connection.exec_prepared(stmt_key, type_casted_binds)
692
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
693
+ @connection.exec_prepared(stmt_key, type_casted_binds)
694
+ end
609
695
  end
610
696
  rescue ActiveRecord::StatementInvalid => e
611
697
  raise unless is_cached_plan_failure?(e)
@@ -615,8 +701,10 @@ module ActiveRecord
615
701
  if in_transaction?
616
702
  raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
617
703
  else
618
- # outside of transactions we can simply flush this query and retry
619
- @statements.delete sql_key(sql)
704
+ @lock.synchronize do
705
+ # outside of transactions we can simply flush this query and retry
706
+ @statements.delete sql_key(sql)
707
+ end
620
708
  retry
621
709
  end
622
710
  end
@@ -629,12 +717,11 @@ module ActiveRecord
629
717
  # ActiveRecord::PreparedStatementCacheExpired
630
718
  #
631
719
  # Check here for more details:
632
- # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
633
- CACHED_PLAN_HEURISTIC = 'cached plan must not change result type'.freeze
720
+ # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
634
721
  def is_cached_plan_failure?(e)
635
722
  pgerror = e.cause
636
- code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
637
- code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
723
+ pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
724
+ pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
638
725
  rescue
639
726
  false
640
727
  end
@@ -651,33 +738,31 @@ module ActiveRecord
651
738
 
652
739
  # Prepare the statement if it hasn't been prepared, return
653
740
  # the statement key.
654
- def prepare_statement(sql)
655
- sql_key = sql_key(sql)
656
- unless @statements.key? sql_key
657
- nextkey = @statements.next_key
658
- begin
659
- @connection.prepare nextkey, sql
660
- rescue => e
661
- raise translate_exception_class(e, sql)
741
+ def prepare_statement(sql, binds)
742
+ @lock.synchronize do
743
+ sql_key = sql_key(sql)
744
+ unless @statements.key? sql_key
745
+ nextkey = @statements.next_key
746
+ begin
747
+ @connection.prepare nextkey, sql
748
+ rescue => e
749
+ raise translate_exception_class(e, sql, binds)
750
+ end
751
+ # Clear the queue
752
+ @connection.get_last_result
753
+ @statements[sql_key] = nextkey
662
754
  end
663
- # Clear the queue
664
- @connection.get_last_result
665
- @statements[sql_key] = nextkey
755
+ @statements[sql_key]
666
756
  end
667
- @statements[sql_key]
668
757
  end
669
758
 
670
759
  # Connects to a PostgreSQL server and sets up the adapter depending on the
671
760
  # connected server's characteristics.
672
761
  def connect
673
- @connection = PG.connect(@connection_parameters)
762
+ @connection = self.class.new_client(@connection_parameters)
674
763
  configure_connection
675
- rescue ::PG::Error => error
676
- if error.message.include?("does not exist")
677
- raise ActiveRecord::NoDatabaseError
678
- else
679
- raise
680
- end
764
+ add_pg_encoders
765
+ add_pg_decoders
681
766
  end
682
767
 
683
768
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -686,39 +771,39 @@ module ActiveRecord
686
771
  if @config[:encoding]
687
772
  @connection.set_client_encoding(@config[:encoding])
688
773
  end
689
- self.client_min_messages = @config[:min_messages] || 'warning'
774
+ self.client_min_messages = @config[:min_messages] || "warning"
690
775
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
691
776
 
692
777
  # Use standard-conforming strings so we don't have to do the E'...' dance.
693
778
  set_standard_conforming_strings
694
779
 
780
+ variables = @config.fetch(:variables, {}).stringify_keys
781
+
695
782
  # If using Active Record's time zone support configure the connection to return
696
783
  # TIMESTAMP WITH ZONE types in UTC.
697
- # (SET TIME ZONE does not use an equals sign like other SET variables)
698
- if ActiveRecord::Base.default_timezone == :utc
699
- execute("SET time zone 'UTC'", 'SCHEMA')
700
- elsif @local_tz
701
- execute("SET time zone '#{@local_tz}'", 'SCHEMA')
784
+ unless variables["timezone"]
785
+ if ActiveRecord::Base.default_timezone == :utc
786
+ variables["timezone"] = "UTC"
787
+ elsif @local_tz
788
+ variables["timezone"] = @local_tz
789
+ end
702
790
  end
703
791
 
792
+ # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
793
+ execute("SET intervalstyle = iso_8601", "SCHEMA")
794
+
704
795
  # SET statements from :variables config hash
705
- # http://www.postgresql.org/docs/current/static/sql-set.html
706
- variables = @config[:variables] || {}
796
+ # https://www.postgresql.org/docs/current/static/sql-set.html
707
797
  variables.map do |k, v|
708
- if v == ':default' || v == :default
798
+ if v == ":default" || v == :default
709
799
  # Sets the value to the global or compile default
710
- execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
800
+ execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
711
801
  elsif !v.nil?
712
- execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
802
+ execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
713
803
  end
714
804
  end
715
805
  end
716
806
 
717
- # Returns the current ID of a table's sequence.
718
- def last_insert_id_result(sequence_name) # :nodoc:
719
- exec_query("SELECT currval('#{sequence_name}')", 'SQL')
720
- end
721
-
722
807
  # Returns the list of a table's column names, data types, and default values.
723
808
  #
724
809
  # The underlying query is roughly:
@@ -737,34 +822,38 @@ module ActiveRecord
737
822
  # Query implementation notes:
738
823
  # - format_type includes the column size constraint, e.g. varchar(50)
739
824
  # - ::regclass is a function that gives the id for a table name
740
- def column_definitions(table_name) # :nodoc:
741
- query(<<-end_sql, 'SCHEMA')
825
+ def column_definitions(table_name)
826
+ query(<<~SQL, "SCHEMA")
742
827
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
743
828
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
744
- (SELECT c.collname FROM pg_collation c, pg_type t
745
- WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation),
746
- col_description(a.attrelid, a.attnum) AS comment
747
- FROM pg_attribute a LEFT JOIN pg_attrdef d
748
- ON a.attrelid = d.adrelid AND a.attnum = d.adnum
749
- WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
829
+ c.collname, col_description(a.attrelid, a.attnum) AS comment
830
+ FROM pg_attribute a
831
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
832
+ LEFT JOIN pg_type t ON a.atttypid = t.oid
833
+ LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
834
+ WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
750
835
  AND a.attnum > 0 AND NOT a.attisdropped
751
836
  ORDER BY a.attnum
752
- end_sql
837
+ SQL
753
838
  end
754
839
 
755
- def extract_table_ref_from_insert_sql(sql) # :nodoc:
840
+ def extract_table_ref_from_insert_sql(sql)
756
841
  sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
757
842
  $1.strip if $1
758
843
  end
759
844
 
760
- def create_table_definition(*args) # :nodoc:
761
- PostgreSQL::TableDefinition.new(*args)
845
+ def arel_visitor
846
+ Arel::Visitors::PostgreSQL.new(self)
847
+ end
848
+
849
+ def build_statement_pool
850
+ StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
762
851
  end
763
852
 
764
853
  def can_perform_case_insensitive_comparison_for?(column)
765
854
  @case_insensitive_cache ||= {}
766
855
  @case_insensitive_cache[column.sql_type] ||= begin
767
- sql = <<-end_sql
856
+ sql = <<~SQL
768
857
  SELECT exists(
769
858
  SELECT * FROM pg_proc
770
859
  WHERE proname = 'lower'
@@ -776,7 +865,7 @@ module ActiveRecord
776
865
  WHERE proname = 'lower'
777
866
  AND castsource = #{quote column.sql_type}::regtype
778
867
  )
779
- end_sql
868
+ SQL
780
869
  execute_and_clear(sql, "SCHEMA", []) do |result|
781
870
  result.getvalue(0, 0)
782
871
  end
@@ -788,40 +877,75 @@ module ActiveRecord
788
877
  map[Integer] = PG::TextEncoder::Integer.new
789
878
  map[TrueClass] = PG::TextEncoder::Boolean.new
790
879
  map[FalseClass] = PG::TextEncoder::Boolean.new
791
- map[Float] = PG::TextEncoder::Float.new
792
880
  @connection.type_map_for_queries = map
793
881
  end
794
882
 
883
+ def update_typemap_for_default_timezone
884
+ if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
885
+ decoder_class = ActiveRecord::Base.default_timezone == :utc ?
886
+ PG::TextDecoder::TimestampUtc :
887
+ PG::TextDecoder::TimestampWithoutTimeZone
888
+
889
+ @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
890
+ @connection.type_map_for_results.add_coder(@timestamp_decoder)
891
+ @default_timezone = ActiveRecord::Base.default_timezone
892
+ end
893
+ end
894
+
795
895
  def add_pg_decoders
896
+ @default_timezone = nil
897
+ @timestamp_decoder = nil
898
+
796
899
  coders_by_name = {
797
- 'int2' => PG::TextDecoder::Integer,
798
- 'int4' => PG::TextDecoder::Integer,
799
- 'int8' => PG::TextDecoder::Integer,
800
- 'oid' => PG::TextDecoder::Integer,
801
- 'float4' => PG::TextDecoder::Float,
802
- 'float8' => PG::TextDecoder::Float,
803
- 'bool' => PG::TextDecoder::Boolean,
900
+ "int2" => PG::TextDecoder::Integer,
901
+ "int4" => PG::TextDecoder::Integer,
902
+ "int8" => PG::TextDecoder::Integer,
903
+ "oid" => PG::TextDecoder::Integer,
904
+ "float4" => PG::TextDecoder::Float,
905
+ "float8" => PG::TextDecoder::Float,
906
+ "numeric" => PG::TextDecoder::Numeric,
907
+ "bool" => PG::TextDecoder::Boolean,
908
+ "timestamp" => PG::TextDecoder::TimestampUtc,
909
+ "timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
804
910
  }
911
+
805
912
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
806
- query = <<-SQL % known_coder_types.join(", ")
913
+ query = <<~SQL % known_coder_types.join(", ")
807
914
  SELECT t.oid, t.typname
808
915
  FROM pg_type as t
809
916
  WHERE t.typname IN (%s)
810
917
  SQL
811
918
  coders = execute_and_clear(query, "SCHEMA", []) do |result|
812
919
  result
813
- .map { |row| construct_coder(row, coders_by_name[row['typname']]) }
920
+ .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
814
921
  .compact
815
922
  end
816
923
 
817
924
  map = PG::TypeMapByOid.new
818
925
  coders.each { |coder| map.add_coder(coder) }
819
926
  @connection.type_map_for_results = map
927
+
928
+ @type_map_for_results = PG::TypeMapByOid.new
929
+ @type_map_for_results.default_type_map = map
930
+ @type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
931
+ @type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
932
+
933
+ # extract timestamp decoder for use in update_typemap_for_default_timezone
934
+ @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
935
+ update_typemap_for_default_timezone
820
936
  end
821
937
 
822
938
  def construct_coder(row, coder_class)
823
939
  return unless coder_class
824
- coder_class.new(oid: row['oid'].to_i, name: row['typname'])
940
+ coder_class.new(oid: row["oid"].to_i, name: row["typname"])
941
+ end
942
+
943
+ class MoneyDecoder < PG::SimpleDecoder # :nodoc:
944
+ TYPE = OID::Money.new
945
+
946
+ def decode(value, tuple = nil, field = nil)
947
+ TYPE.deserialize(value)
948
+ end
825
949
  end
826
950
 
827
951
  ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
@@ -830,16 +954,17 @@ module ActiveRecord
830
954
  ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
831
955
  ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
832
956
  ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
957
+ ActiveRecord::Type.register(:date, OID::Date, adapter: :postgresql)
833
958
  ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
834
959
  ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
835
960
  ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
836
961
  ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
837
962
  ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
838
- ActiveRecord::Type.register(:json, OID::Json, adapter: :postgresql)
963
+ ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
839
964
  ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
840
965
  ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
841
- ActiveRecord::Type.register(:point, OID::Rails51Point, adapter: :postgresql)
842
- ActiveRecord::Type.register(:legacy_point, OID::Point, adapter: :postgresql)
966
+ ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
967
+ ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
843
968
  ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
844
969
  ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
845
970
  ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)