activerecord 4.2.0 → 6.1.7.1

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