activerecord 3.2.6 → 6.0.0

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 (371) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +611 -6417
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +44 -47
  5. data/examples/performance.rb +79 -71
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +268 -238
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +173 -81
  11. data/lib/active_record/associations/association_scope.rb +124 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -38
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
  14. data/lib/active_record/associations/builder/association.rb +113 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +105 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +53 -56
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
  18. data/lib/active_record/associations/builder/has_many.rb +11 -63
  19. data/lib/active_record/associations/builder/has_one.rb +47 -45
  20. data/lib/active_record/associations/builder/singular_association.rb +30 -18
  21. data/lib/active_record/associations/collection_association.rb +217 -295
  22. data/lib/active_record/associations/collection_proxy.rb +1074 -77
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +78 -50
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -61
  26. data/lib/active_record/associations/has_one_association.rb +75 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +208 -164
  32. data/lib/active_record/associations/preloader/association.rb +93 -87
  33. data/lib/active_record/associations/preloader/through_association.rb +87 -38
  34. data/lib/active_record/associations/preloader.rb +134 -110
  35. data/lib/active_record/associations/singular_association.rb +19 -24
  36. data/lib/active_record/associations/through_association.rb +61 -27
  37. data/lib/active_record/associations.rb +1766 -1505
  38. data/lib/active_record/attribute_assignment.rb +57 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +187 -67
  42. data/lib/active_record/attribute_methods/primary_key.rb +100 -78
  43. data/lib/active_record/attribute_methods/query.rb +10 -8
  44. data/lib/active_record/attribute_methods/read.rb +29 -118
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -72
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
  47. data/lib/active_record/attribute_methods/write.rb +36 -44
  48. data/lib/active_record/attribute_methods.rb +306 -161
  49. data/lib/active_record/attributes.rb +279 -0
  50. data/lib/active_record/autosave_association.rb +324 -238
  51. data/lib/active_record/base.rb +114 -507
  52. data/lib/active_record/callbacks.rb +147 -83
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
  68. data/lib/active_record/connection_adapters/column.rb +58 -233
  69. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +75 -207
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
  116. data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +528 -26
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +267 -0
  128. data/lib/active_record/core.rb +599 -0
  129. data/lib/active_record/counter_cache.rb +177 -103
  130. data/lib/active_record/database_configurations/database_config.rb +37 -0
  131. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  132. data/lib/active_record/database_configurations/url_config.rb +79 -0
  133. data/lib/active_record/database_configurations.rb +233 -0
  134. data/lib/active_record/define_callbacks.rb +22 -0
  135. data/lib/active_record/dynamic_matchers.rb +107 -64
  136. data/lib/active_record/enum.rb +274 -0
  137. data/lib/active_record/errors.rb +254 -61
  138. data/lib/active_record/explain.rb +35 -70
  139. data/lib/active_record/explain_registry.rb +32 -0
  140. data/lib/active_record/explain_subscriber.rb +18 -8
  141. data/lib/active_record/fixture_set/file.rb +82 -0
  142. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  143. data/lib/active_record/fixture_set/render_context.rb +17 -0
  144. data/lib/active_record/fixture_set/table_row.rb +153 -0
  145. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  146. data/lib/active_record/fixtures.rb +291 -475
  147. data/lib/active_record/gem_version.rb +17 -0
  148. data/lib/active_record/inheritance.rb +219 -100
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +175 -17
  151. data/lib/active_record/internal_metadata.rb +53 -0
  152. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  153. data/lib/active_record/locale/en.yml +9 -1
  154. data/lib/active_record/locking/optimistic.rb +106 -92
  155. data/lib/active_record/locking/pessimistic.rb +23 -11
  156. data/lib/active_record/log_subscriber.rb +80 -30
  157. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  158. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  159. data/lib/active_record/middleware/database_selector.rb +75 -0
  160. data/lib/active_record/migration/command_recorder.rb +235 -56
  161. data/lib/active_record/migration/compatibility.rb +244 -0
  162. data/lib/active_record/migration/join_table.rb +17 -0
  163. data/lib/active_record/migration.rb +917 -301
  164. data/lib/active_record/model_schema.rb +351 -175
  165. data/lib/active_record/nested_attributes.rb +366 -235
  166. data/lib/active_record/no_touching.rb +65 -0
  167. data/lib/active_record/null_relation.rb +68 -0
  168. data/lib/active_record/persistence.rb +761 -166
  169. data/lib/active_record/query_cache.rb +22 -44
  170. data/lib/active_record/querying.rb +55 -31
  171. data/lib/active_record/railtie.rb +185 -47
  172. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  173. data/lib/active_record/railties/console_sandbox.rb +5 -4
  174. data/lib/active_record/railties/controller_runtime.rb +35 -33
  175. data/lib/active_record/railties/databases.rake +366 -463
  176. data/lib/active_record/readonly_attributes.rb +4 -6
  177. data/lib/active_record/reflection.rb +736 -228
  178. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  179. data/lib/active_record/relation/batches.rb +252 -52
  180. data/lib/active_record/relation/calculations.rb +340 -270
  181. data/lib/active_record/relation/delegation.rb +117 -36
  182. data/lib/active_record/relation/finder_methods.rb +439 -286
  183. data/lib/active_record/relation/from_clause.rb +26 -0
  184. data/lib/active_record/relation/merger.rb +184 -0
  185. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  186. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  187. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  188. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  189. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  190. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  191. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder.rb +131 -39
  193. data/lib/active_record/relation/query_attribute.rb +50 -0
  194. data/lib/active_record/relation/query_methods.rb +1163 -221
  195. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  196. data/lib/active_record/relation/spawn_methods.rb +49 -120
  197. data/lib/active_record/relation/where_clause.rb +190 -0
  198. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  199. data/lib/active_record/relation.rb +671 -349
  200. data/lib/active_record/result.rb +149 -15
  201. data/lib/active_record/runtime_registry.rb +24 -0
  202. data/lib/active_record/sanitization.rb +153 -133
  203. data/lib/active_record/schema.rb +22 -19
  204. data/lib/active_record/schema_dumper.rb +178 -112
  205. data/lib/active_record/schema_migration.rb +60 -0
  206. data/lib/active_record/scoping/default.rb +107 -98
  207. data/lib/active_record/scoping/named.rb +130 -115
  208. data/lib/active_record/scoping.rb +77 -123
  209. data/lib/active_record/secure_token.rb +40 -0
  210. data/lib/active_record/serialization.rb +10 -6
  211. data/lib/active_record/statement_cache.rb +148 -0
  212. data/lib/active_record/store.rb +256 -16
  213. data/lib/active_record/suppressor.rb +61 -0
  214. data/lib/active_record/table_metadata.rb +75 -0
  215. data/lib/active_record/tasks/database_tasks.rb +506 -0
  216. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  217. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  218. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  219. data/lib/active_record/test_databases.rb +23 -0
  220. data/lib/active_record/test_fixtures.rb +224 -0
  221. data/lib/active_record/timestamp.rb +93 -39
  222. data/lib/active_record/touch_later.rb +66 -0
  223. data/lib/active_record/transactions.rb +260 -129
  224. data/lib/active_record/translation.rb +3 -1
  225. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  226. data/lib/active_record/type/date.rb +9 -0
  227. data/lib/active_record/type/date_time.rb +9 -0
  228. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  229. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  230. data/lib/active_record/type/internal/timezone.rb +17 -0
  231. data/lib/active_record/type/json.rb +30 -0
  232. data/lib/active_record/type/serialized.rb +71 -0
  233. data/lib/active_record/type/text.rb +11 -0
  234. data/lib/active_record/type/time.rb +21 -0
  235. data/lib/active_record/type/type_map.rb +62 -0
  236. data/lib/active_record/type/unsigned_integer.rb +17 -0
  237. data/lib/active_record/type.rb +78 -0
  238. data/lib/active_record/type_caster/connection.rb +34 -0
  239. data/lib/active_record/type_caster/map.rb +20 -0
  240. data/lib/active_record/type_caster.rb +9 -0
  241. data/lib/active_record/validations/absence.rb +25 -0
  242. data/lib/active_record/validations/associated.rb +35 -18
  243. data/lib/active_record/validations/length.rb +26 -0
  244. data/lib/active_record/validations/presence.rb +68 -0
  245. data/lib/active_record/validations/uniqueness.rb +123 -77
  246. data/lib/active_record/validations.rb +54 -43
  247. data/lib/active_record/version.rb +7 -7
  248. data/lib/active_record.rb +97 -49
  249. data/lib/arel/alias_predication.rb +9 -0
  250. data/lib/arel/attributes/attribute.rb +37 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/collectors/bind.rb +24 -0
  253. data/lib/arel/collectors/composite.rb +31 -0
  254. data/lib/arel/collectors/plain_string.rb +20 -0
  255. data/lib/arel/collectors/sql_string.rb +20 -0
  256. data/lib/arel/collectors/substitute_binds.rb +28 -0
  257. data/lib/arel/crud.rb +42 -0
  258. data/lib/arel/delete_manager.rb +18 -0
  259. data/lib/arel/errors.rb +9 -0
  260. data/lib/arel/expressions.rb +29 -0
  261. data/lib/arel/factory_methods.rb +49 -0
  262. data/lib/arel/insert_manager.rb +49 -0
  263. data/lib/arel/math.rb +45 -0
  264. data/lib/arel/nodes/and.rb +32 -0
  265. data/lib/arel/nodes/ascending.rb +23 -0
  266. data/lib/arel/nodes/binary.rb +52 -0
  267. data/lib/arel/nodes/bind_param.rb +36 -0
  268. data/lib/arel/nodes/case.rb +55 -0
  269. data/lib/arel/nodes/casted.rb +50 -0
  270. data/lib/arel/nodes/comment.rb +29 -0
  271. data/lib/arel/nodes/count.rb +12 -0
  272. data/lib/arel/nodes/delete_statement.rb +45 -0
  273. data/lib/arel/nodes/descending.rb +23 -0
  274. data/lib/arel/nodes/equality.rb +18 -0
  275. data/lib/arel/nodes/extract.rb +24 -0
  276. data/lib/arel/nodes/false.rb +16 -0
  277. data/lib/arel/nodes/full_outer_join.rb +8 -0
  278. data/lib/arel/nodes/function.rb +44 -0
  279. data/lib/arel/nodes/grouping.rb +8 -0
  280. data/lib/arel/nodes/in.rb +8 -0
  281. data/lib/arel/nodes/infix_operation.rb +80 -0
  282. data/lib/arel/nodes/inner_join.rb +8 -0
  283. data/lib/arel/nodes/insert_statement.rb +37 -0
  284. data/lib/arel/nodes/join_source.rb +20 -0
  285. data/lib/arel/nodes/matches.rb +18 -0
  286. data/lib/arel/nodes/named_function.rb +23 -0
  287. data/lib/arel/nodes/node.rb +50 -0
  288. data/lib/arel/nodes/node_expression.rb +13 -0
  289. data/lib/arel/nodes/outer_join.rb +8 -0
  290. data/lib/arel/nodes/over.rb +15 -0
  291. data/lib/arel/nodes/regexp.rb +16 -0
  292. data/lib/arel/nodes/right_outer_join.rb +8 -0
  293. data/lib/arel/nodes/select_core.rb +67 -0
  294. data/lib/arel/nodes/select_statement.rb +41 -0
  295. data/lib/arel/nodes/sql_literal.rb +16 -0
  296. data/lib/arel/nodes/string_join.rb +11 -0
  297. data/lib/arel/nodes/table_alias.rb +27 -0
  298. data/lib/arel/nodes/terminal.rb +16 -0
  299. data/lib/arel/nodes/true.rb +16 -0
  300. data/lib/arel/nodes/unary.rb +45 -0
  301. data/lib/arel/nodes/unary_operation.rb +20 -0
  302. data/lib/arel/nodes/unqualified_column.rb +22 -0
  303. data/lib/arel/nodes/update_statement.rb +41 -0
  304. data/lib/arel/nodes/values_list.rb +9 -0
  305. data/lib/arel/nodes/window.rb +126 -0
  306. data/lib/arel/nodes/with.rb +11 -0
  307. data/lib/arel/nodes.rb +68 -0
  308. data/lib/arel/order_predications.rb +13 -0
  309. data/lib/arel/predications.rb +257 -0
  310. data/lib/arel/select_manager.rb +271 -0
  311. data/lib/arel/table.rb +110 -0
  312. data/lib/arel/tree_manager.rb +72 -0
  313. data/lib/arel/update_manager.rb +34 -0
  314. data/lib/arel/visitors/depth_first.rb +204 -0
  315. data/lib/arel/visitors/dot.rb +297 -0
  316. data/lib/arel/visitors/ibm_db.rb +34 -0
  317. data/lib/arel/visitors/informix.rb +62 -0
  318. data/lib/arel/visitors/mssql.rb +157 -0
  319. data/lib/arel/visitors/mysql.rb +83 -0
  320. data/lib/arel/visitors/oracle.rb +159 -0
  321. data/lib/arel/visitors/oracle12.rb +66 -0
  322. data/lib/arel/visitors/postgresql.rb +110 -0
  323. data/lib/arel/visitors/sqlite.rb +39 -0
  324. data/lib/arel/visitors/to_sql.rb +889 -0
  325. data/lib/arel/visitors/visitor.rb +46 -0
  326. data/lib/arel/visitors/where_sql.rb +23 -0
  327. data/lib/arel/visitors.rb +20 -0
  328. data/lib/arel/window_predications.rb +9 -0
  329. data/lib/arel.rb +51 -0
  330. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  331. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +59 -9
  333. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  334. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  335. data/lib/rails/generators/active_record/migration.rb +41 -8
  336. data/lib/rails/generators/active_record/model/model_generator.rb +24 -22
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  339. data/lib/rails/generators/active_record.rb +10 -16
  340. metadata +285 -149
  341. data/examples/associations.png +0 -0
  342. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  343. data/lib/active_record/associations/join_helper.rb +0 -55
  344. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  345. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  346. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  347. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  348. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  349. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  350. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  351. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  352. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  353. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
  354. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
  355. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
  356. data/lib/active_record/dynamic_finder_match.rb +0 -68
  357. data/lib/active_record/dynamic_scope_match.rb +0 -23
  358. data/lib/active_record/fixtures/file.rb +0 -65
  359. data/lib/active_record/identity_map.rb +0 -162
  360. data/lib/active_record/observer.rb +0 -121
  361. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  362. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  363. data/lib/active_record/session_store.rb +0 -358
  364. data/lib/active_record/test_case.rb +0 -73
  365. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  366. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  367. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  368. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  369. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  370. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  371. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,250 +1,154 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_support/core_ext/object/blank'
3
- require 'active_record/connection_adapters/statement_pool'
4
- require 'arel/visitors/bind_visitor'
1
+ # frozen_string_literal: true
5
2
 
6
- # Make sure we're using pg high enough for PGResult#values
7
- gem 'pg', '~> 0.11'
8
- require 'pg'
3
+ # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
4
+ gem "pg", ">= 0.18", "< 2.0"
5
+ require "pg"
9
6
 
10
- module ActiveRecord
11
- class Base
12
- # Establishes a connection to the database that's used by all Active Record objects
13
- def self.postgresql_connection(config) # :nodoc:
14
- config = config.symbolize_keys
15
- host = config[:host]
16
- port = config[:port] || 5432
17
- username = config[:username].to_s if config[:username]
18
- password = config[:password].to_s if config[:password]
19
-
20
- if config.key?(:database)
21
- database = config[:database]
22
- else
23
- raise ArgumentError, "No database specified. Missing argument: database."
24
- end
25
-
26
- # The postgres drivers don't allow the creation of an unconnected PGconn object,
27
- # so just pass a nil connection object for the time being.
28
- ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, [host, port, nil, nil, database, username, password], config)
29
- end
7
+ # Use async_exec instead of exec_params on pg versions before 1.1
8
+ class ::PG::Connection # :nodoc:
9
+ unless self.public_method_defined?(:async_exec_params)
10
+ remove_method :exec_params
11
+ alias exec_params async_exec
30
12
  end
13
+ end
31
14
 
32
- module ConnectionAdapters
33
- # PostgreSQL-specific extensions to column definitions in a table.
34
- class PostgreSQLColumn < Column #:nodoc:
35
- # Instantiates a new PostgreSQL column definition in a table.
36
- def initialize(name, default, sql_type = nil, null = true)
37
- super(name, self.class.extract_value_from_default(default), sql_type, null)
38
- end
39
-
40
- # :stopdoc:
41
- class << self
42
- attr_accessor :money_precision
43
- def string_to_time(string)
44
- return string unless String === string
15
+ require "active_record/connection_adapters/abstract_adapter"
16
+ require "active_record/connection_adapters/statement_pool"
17
+ require "active_record/connection_adapters/postgresql/column"
18
+ require "active_record/connection_adapters/postgresql/database_statements"
19
+ require "active_record/connection_adapters/postgresql/explain_pretty_printer"
20
+ require "active_record/connection_adapters/postgresql/oid"
21
+ require "active_record/connection_adapters/postgresql/quoting"
22
+ require "active_record/connection_adapters/postgresql/referential_integrity"
23
+ require "active_record/connection_adapters/postgresql/schema_creation"
24
+ require "active_record/connection_adapters/postgresql/schema_definitions"
25
+ require "active_record/connection_adapters/postgresql/schema_dumper"
26
+ require "active_record/connection_adapters/postgresql/schema_statements"
27
+ require "active_record/connection_adapters/postgresql/type_metadata"
28
+ require "active_record/connection_adapters/postgresql/utils"
45
29
 
46
- case string
47
- when 'infinity' then 1.0 / 0.0
48
- when '-infinity' then -1.0 / 0.0
49
- else
50
- super
51
- end
52
- end
53
- end
54
- # :startdoc:
55
-
56
- private
57
- def extract_limit(sql_type)
58
- case sql_type
59
- when /^bigint/i; 8
60
- when /^smallint/i; 2
61
- else super
62
- end
63
- end
30
+ module ActiveRecord
31
+ module ConnectionHandling # :nodoc:
32
+ # Establishes a connection to the database that's used by all Active Record objects
33
+ def postgresql_connection(config)
34
+ conn_params = config.symbolize_keys
64
35
 
65
- # Extracts the scale from PostgreSQL-specific data types.
66
- def extract_scale(sql_type)
67
- # Money type has a fixed scale of 2.
68
- sql_type =~ /^money/ ? 2 : super
69
- end
36
+ conn_params.delete_if { |_, v| v.nil? }
70
37
 
71
- # Extracts the precision from PostgreSQL-specific data types.
72
- def extract_precision(sql_type)
73
- if sql_type == 'money'
74
- self.class.money_precision
75
- else
76
- super
77
- end
78
- end
38
+ # Map ActiveRecords param names to PGs.
39
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
40
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
79
41
 
80
- # Maps PostgreSQL-specific data types to logical Rails types.
81
- def simplified_type(field_type)
82
- case field_type
83
- # Numeric and monetary types
84
- when /^(?:real|double precision)$/
85
- :float
86
- # Monetary types
87
- when 'money'
88
- :decimal
89
- # Character types
90
- when /^(?:character varying|bpchar)(?:\(\d+\))?$/
91
- :string
92
- # Binary data types
93
- when 'bytea'
94
- :binary
95
- # Date/time types
96
- when /^timestamp with(?:out)? time zone$/
97
- :datetime
98
- when 'interval'
99
- :string
100
- # Geometric types
101
- when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
102
- :string
103
- # Network address types
104
- when /^(?:cidr|inet|macaddr)$/
105
- :string
106
- # Bit strings
107
- when /^bit(?: varying)?(?:\(\d+\))?$/
108
- :string
109
- # XML type
110
- when 'xml'
111
- :xml
112
- # tsvector type
113
- when 'tsvector'
114
- :tsvector
115
- # Arrays
116
- when /^\D+\[\]$/
117
- :string
118
- # Object identifier types
119
- when 'oid'
120
- :integer
121
- # UUID type
122
- when 'uuid'
123
- :string
124
- # Small and big integer types
125
- when /^(?:small|big)int$/
126
- :integer
127
- # Pass through all types that are not specific to PostgreSQL.
128
- else
129
- super
130
- end
131
- end
42
+ # Forward only valid config params to PG::Connection.connect.
43
+ valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
44
+ conn_params.slice!(*valid_conn_param_keys)
132
45
 
133
- # Extracts the value from a PostgreSQL column default definition.
134
- def self.extract_value_from_default(default)
135
- case default
136
- # This is a performance optimization for Ruby 1.9.2 in development.
137
- # If the value is nil, we return nil straight away without checking
138
- # the regular expressions. If we check each regular expression,
139
- # Regexp#=== will call NilClass#to_str, which will trigger
140
- # method_missing (defined by whiny nil in ActiveSupport) which
141
- # makes this method very very slow.
142
- when NilClass
143
- nil
144
- # Numeric types
145
- when /\A\(?(-?\d+(\.\d*)?\)?)\z/
146
- $1
147
- # Character types
148
- when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
149
- $1
150
- # Character types (8.1 formatting)
151
- when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
152
- $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
153
- # Binary data types
154
- when /\A'(.*)'::bytea\z/m
155
- $1
156
- # Date/time types
157
- when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
158
- $1
159
- when /\A'(.*)'::interval\z/
160
- $1
161
- # Boolean type
162
- when 'true'
163
- true
164
- when 'false'
165
- false
166
- # Geometric types
167
- when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
168
- $1
169
- # Network address types
170
- when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
171
- $1
172
- # Bit string types
173
- when /\AB'(.*)'::"?bit(?: varying)?"?\z/
174
- $1
175
- # XML type
176
- when /\A'(.*)'::xml\z/m
177
- $1
178
- # Arrays
179
- when /\A'(.*)'::"?\D+"?\[\]\z/
180
- $1
181
- # Object identifier types
182
- when /\A-?\d+\z/
183
- $1
184
- else
185
- # Anything else is blank, some user type, or some function
186
- # and we can't know the value of that, so return nil.
187
- nil
188
- end
189
- end
46
+ conn = PG.connect(conn_params)
47
+ ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
48
+ rescue ::PG::Error => error
49
+ if error.message.include?(conn_params[:dbname])
50
+ raise ActiveRecord::NoDatabaseError
51
+ else
52
+ raise
53
+ end
190
54
  end
55
+ end
191
56
 
192
- # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure
193
- # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers.
57
+ module ConnectionAdapters
58
+ # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
194
59
  #
195
60
  # Options:
196
61
  #
197
- # * <tt>:host</tt> - Defaults to "localhost".
62
+ # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
63
+ # the default is to connect to localhost.
198
64
  # * <tt>:port</tt> - Defaults to 5432.
199
- # * <tt>:username</tt> - Defaults to nothing.
200
- # * <tt>:password</tt> - Defaults to nothing.
201
- # * <tt>:database</tt> - The name of the database. No default, must be provided.
65
+ # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
66
+ # * <tt>:password</tt> - Password to be used if the server demands password authentication.
67
+ # * <tt>:database</tt> - Defaults to be the same as the user name.
202
68
  # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
203
69
  # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
204
70
  # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
205
71
  # <encoding></tt> call on the connection.
206
72
  # * <tt>:min_messages</tt> - An optional client min messages that is used in a
207
73
  # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
74
+ # * <tt>:variables</tt> - An optional hash of additional parameters that
75
+ # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
76
+ # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
77
+ # defaults to true.
78
+ #
79
+ # Any further options are used as connection parameters to libpq. See
80
+ # https://www.postgresql.org/docs/current/static/libpq-connect.html for the
81
+ # list of parameters.
82
+ #
83
+ # In addition, default connection parameters of libpq can be set per environment variables.
84
+ # See https://www.postgresql.org/docs/current/static/libpq-envars.html .
208
85
  class PostgreSQLAdapter < AbstractAdapter
209
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
210
- def xml(*args)
211
- options = args.extract_options!
212
- column(args[0], 'xml', options)
213
- end
214
-
215
- def tsvector(*args)
216
- options = args.extract_options!
217
- column(args[0], 'tsvector', options)
218
- end
219
- end
220
-
221
- ADAPTER_NAME = 'PostgreSQL'
86
+ ADAPTER_NAME = "PostgreSQL"
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
222
100
 
223
101
  NATIVE_DATABASE_TYPES = {
224
- :primary_key => "serial primary key",
225
- :string => { :name => "character varying", :limit => 255 },
226
- :text => { :name => "text" },
227
- :integer => { :name => "integer" },
228
- :float => { :name => "float" },
229
- :decimal => { :name => "decimal" },
230
- :datetime => { :name => "timestamp" },
231
- :timestamp => { :name => "timestamp" },
232
- :time => { :name => "time" },
233
- :date => { :name => "date" },
234
- :binary => { :name => "bytea" },
235
- :boolean => { :name => "boolean" },
236
- :xml => { :name => "xml" },
237
- :tsvector => { :name => "tsvector" }
102
+ primary_key: "bigserial primary key",
103
+ string: { name: "character varying" },
104
+ text: { name: "text" },
105
+ integer: { name: "integer", limit: 4 },
106
+ float: { name: "float" },
107
+ decimal: { name: "decimal" },
108
+ datetime: { name: "timestamp" },
109
+ time: { name: "time" },
110
+ date: { name: "date" },
111
+ daterange: { name: "daterange" },
112
+ numrange: { name: "numrange" },
113
+ tsrange: { name: "tsrange" },
114
+ tstzrange: { name: "tstzrange" },
115
+ int4range: { name: "int4range" },
116
+ int8range: { name: "int8range" },
117
+ binary: { name: "bytea" },
118
+ boolean: { name: "boolean" },
119
+ xml: { name: "xml" },
120
+ tsvector: { name: "tsvector" },
121
+ hstore: { name: "hstore" },
122
+ inet: { name: "inet" },
123
+ cidr: { name: "cidr" },
124
+ macaddr: { name: "macaddr" },
125
+ uuid: { name: "uuid" },
126
+ json: { name: "json" },
127
+ jsonb: { name: "jsonb" },
128
+ ltree: { name: "ltree" },
129
+ citext: { name: "citext" },
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" },
137
+ bit: { name: "bit" },
138
+ bit_varying: { name: "bit varying" },
139
+ money: { name: "money" },
140
+ interval: { name: "interval" },
141
+ oid: { name: "oid" },
238
142
  }
239
143
 
240
- # Returns 'PostgreSQL' as adapter name for identification purposes.
241
- def adapter_name
242
- ADAPTER_NAME
243
- end
144
+ OID = PostgreSQL::OID #:nodoc:
244
145
 
245
- # Returns +true+, since this connection adapter supports prepared statement
246
- # caching.
247
- def supports_statement_cache?
146
+ include PostgreSQL::Quoting
147
+ include PostgreSQL::ReferentialIntegrity
148
+ include PostgreSQL::SchemaStatements
149
+ include PostgreSQL::DatabaseStatements
150
+
151
+ def supports_bulk_alter?
248
152
  true
249
153
  end
250
154
 
@@ -252,929 +156,568 @@ module ActiveRecord
252
156
  true
253
157
  end
254
158
 
255
- class StatementPool < ConnectionAdapters::StatementPool
256
- def initialize(connection, max)
257
- super
258
- @counter = 0
259
- @cache = Hash.new { |h,pid| h[pid] = {} }
260
- end
261
-
262
- def each(&block); cache.each(&block); end
263
- def key?(key); cache.key?(key); end
264
- def [](key); cache[key]; end
265
- def length; cache.length; end
266
-
267
- def next_key
268
- "a#{@counter + 1}"
269
- end
270
-
271
- def []=(sql, key)
272
- while @max <= cache.size
273
- dealloc(cache.shift.last)
274
- end
275
- @counter += 1
276
- cache[sql] = key
277
- end
278
-
279
- def clear
280
- cache.each_value do |stmt_key|
281
- dealloc stmt_key
282
- end
283
- cache.clear
284
- end
285
-
286
- def delete(sql_key)
287
- dealloc cache[sql_key]
288
- cache.delete sql_key
289
- end
290
-
291
- private
292
- def cache
293
- @cache[$$]
294
- end
295
-
296
- def dealloc(key)
297
- @connection.query "DEALLOCATE #{key}" if connection_active?
298
- end
299
-
300
- def connection_active?
301
- @connection.status == PGconn::CONNECTION_OK
302
- rescue PGError
303
- false
304
- end
305
- end
306
-
307
- class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
308
- include Arel::Visitors::BindVisitor
309
- end
310
-
311
- # Initializes and connects a PostgreSQL adapter.
312
- def initialize(connection, logger, connection_parameters, config)
313
- super(connection, logger)
314
-
315
- if config.fetch(:prepared_statements) { true }
316
- @visitor = Arel::Visitors::PostgreSQL.new self
317
- else
318
- @visitor = BindSubstitution.new self
319
- end
320
-
321
- connection_parameters.delete :prepared_statements
322
-
323
- @connection_parameters, @config = connection_parameters, config
324
-
325
- # @local_tz is initialized as nil to avoid warnings when connect tries to use it
326
- @local_tz = nil
327
- @table_alias_length = nil
328
-
329
- connect
330
- @statements = StatementPool.new @connection,
331
- config.fetch(:statement_limit) { 1000 }
332
-
333
- if postgresql_version < 80200
334
- raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
335
- end
336
-
337
- @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
338
- end
339
-
340
- # Clears the prepared statements cache.
341
- def clear_cache!
342
- @statements.clear
343
- end
344
-
345
- # Is this connection alive and ready for queries?
346
- def active?
347
- @connection.query 'SELECT 1'
159
+ def supports_partial_index?
348
160
  true
349
- rescue PGError
350
- false
351
161
  end
352
162
 
353
- # Close then reopen the connection.
354
- def reconnect!
355
- clear_cache!
356
- @connection.reset
357
- configure_connection
358
- end
359
-
360
- def reset!
361
- clear_cache!
362
- super
163
+ def supports_expression_index?
164
+ true
363
165
  end
364
166
 
365
- # Disconnects from the database if already connected. Otherwise, this
366
- # method does nothing.
367
- def disconnect!
368
- clear_cache!
369
- @connection.close rescue nil
167
+ def supports_transaction_isolation?
168
+ true
370
169
  end
371
170
 
372
- def native_database_types #:nodoc:
373
- NATIVE_DATABASE_TYPES
171
+ def supports_foreign_keys?
172
+ true
374
173
  end
375
174
 
376
- # Returns true, since this connection adapter supports migrations.
377
- def supports_migrations?
175
+ def supports_validate_constraints?
378
176
  true
379
177
  end
380
178
 
381
- # Does PostgreSQL support finding primary key on non-Active Record tables?
382
- def supports_primary_key? #:nodoc:
179
+ def supports_views?
383
180
  true
384
181
  end
385
182
 
386
- # Enable standard-conforming strings if available.
387
- def set_standard_conforming_strings
388
- old, self.client_min_messages = client_min_messages, 'panic'
389
- execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
390
- ensure
391
- self.client_min_messages = old
183
+ def supports_datetime_with_precision?
184
+ true
392
185
  end
393
186
 
394
- def supports_insert_with_returning?
187
+ def supports_json?
395
188
  true
396
189
  end
397
190
 
398
- def supports_ddl_transactions?
191
+ def supports_comments?
399
192
  true
400
193
  end
401
194
 
402
- # Returns true, since this connection adapter supports savepoints.
403
195
  def supports_savepoints?
404
196
  true
405
197
  end
406
198
 
407
- # Returns true.
408
- def supports_explain?
199
+ def supports_insert_returning?
409
200
  true
410
201
  end
411
202
 
412
- # Returns the configured supported identifier length supported by PostgreSQL
413
- def table_alias_length
414
- @table_alias_length ||= query('SHOW max_identifier_length')[0][0].to_i
415
- end
416
-
417
- # QUOTING ==================================================
418
-
419
- # Escapes binary strings for bytea input to the database.
420
- def escape_bytea(value)
421
- @connection.escape_bytea(value) if value
422
- end
423
-
424
- # Unescapes bytea output from a database to the binary string it represents.
425
- # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
426
- # on escaped binary output from database drive.
427
- def unescape_bytea(value)
428
- @connection.unescape_bytea(value) if value
429
- end
430
-
431
- # Quotes PostgreSQL-specific data types for SQL input.
432
- def quote(value, column = nil) #:nodoc:
433
- return super unless column
434
-
435
- case value
436
- when Float
437
- return super unless value.infinite? && column.type == :datetime
438
- "'#{value.to_s.downcase}'"
439
- when Numeric
440
- return super unless column.sql_type == 'money'
441
- # Not truly string input, so doesn't require (or allow) escape string syntax.
442
- "'#{value}'"
443
- when String
444
- case column.sql_type
445
- when 'bytea' then "'#{escape_bytea(value)}'"
446
- when 'xml' then "xml '#{quote_string(value)}'"
447
- when /^bit/
448
- case value
449
- when /^[01]*$/ then "B'#{value}'" # Bit-string notation
450
- when /^[0-9A-F]*$/i then "X'#{value}'" # Hexadecimal notation
451
- end
452
- else
453
- super
454
- end
455
- else
456
- super
457
- end
458
- end
459
-
460
- def type_cast(value, column)
461
- return super unless column
462
-
463
- case value
464
- when String
465
- return super unless 'bytea' == column.sql_type
466
- { :value => value, :format => 1 }
467
- else
468
- super
469
- end
203
+ def supports_insert_on_conflict?
204
+ database_version >= 90500
470
205
  end
206
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
207
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
208
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
471
209
 
472
- # Quotes strings for use in SQL input.
473
- def quote_string(s) #:nodoc:
474
- @connection.escape(s)
210
+ def index_algorithms
211
+ { concurrently: "CONCURRENTLY" }
475
212
  end
476
213
 
477
- # Checks the following cases:
478
- #
479
- # - table_name
480
- # - "table.name"
481
- # - schema_name.table_name
482
- # - schema_name."table.name"
483
- # - "schema.name".table_name
484
- # - "schema.name"."table.name"
485
- def quote_table_name(name)
486
- schema, name_part = extract_pg_identifier_from_name(name.to_s)
487
-
488
- unless name_part
489
- quote_column_name(schema)
490
- else
491
- table_name, name_part = extract_pg_identifier_from_name(name_part)
492
- "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
214
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
215
+ def initialize(connection, max)
216
+ super(max)
217
+ @connection = connection
218
+ @counter = 0
493
219
  end
494
- end
495
220
 
496
- # Quotes column names for use in SQL queries.
497
- def quote_column_name(name) #:nodoc:
498
- PGconn.quote_ident(name.to_s)
499
- end
500
-
501
- # Quote date/time values for use in SQL input. Includes microseconds
502
- # if the value is a Time responding to usec.
503
- def quoted_date(value) #:nodoc:
504
- if value.acts_like?(:time) && value.respond_to?(:usec)
505
- "#{super}.#{sprintf("%06d", value.usec)}"
506
- else
507
- super
221
+ def next_key
222
+ "a#{@counter + 1}"
508
223
  end
509
- end
510
-
511
- # Set the authorized user for this session
512
- def session_auth=(user)
513
- clear_cache!
514
- exec_query "SET SESSION AUTHORIZATION #{user}"
515
- end
516
224
 
517
- # REFERENTIAL INTEGRITY ====================================
518
-
519
- def supports_disable_referential_integrity? #:nodoc:
520
- true
521
- end
522
-
523
- def disable_referential_integrity #:nodoc:
524
- if supports_disable_referential_integrity? then
525
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
526
- end
527
- yield
528
- ensure
529
- if supports_disable_referential_integrity? then
530
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
225
+ def []=(sql, key)
226
+ super.tap { @counter += 1 }
531
227
  end
532
- end
533
228
 
534
- # DATABASE STATEMENTS ======================================
229
+ private
230
+ def dealloc(key)
231
+ @connection.query "DEALLOCATE #{key}" if connection_active?
232
+ rescue PG::Error
233
+ end
535
234
 
536
- def explain(arel, binds = [])
537
- sql = "EXPLAIN #{to_sql(arel, binds)}"
538
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
235
+ def connection_active?
236
+ @connection.status == PG::CONNECTION_OK
237
+ rescue PG::Error
238
+ false
239
+ end
539
240
  end
540
241
 
541
- class ExplainPrettyPrinter # :nodoc:
542
- # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
543
- # PostgreSQL shell:
544
- #
545
- # QUERY PLAN
546
- # ------------------------------------------------------------------------------
547
- # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
548
- # Join Filter: (posts.user_id = users.id)
549
- # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
550
- # Index Cond: (id = 1)
551
- # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
552
- # Filter: (posts.user_id = 1)
553
- # (6 rows)
554
- #
555
- def pp(result)
556
- header = result.columns.first
557
- lines = result.rows.map(&:first)
558
-
559
- # We add 2 because there's one char of padding at both sides, note
560
- # the extra hyphens in the example above.
561
- width = [header, *lines].map(&:length).max + 2
562
-
563
- pp = []
242
+ # Initializes and connects a PostgreSQL adapter.
243
+ def initialize(connection, logger, connection_parameters, config)
244
+ super(connection, logger, config)
564
245
 
565
- pp << header.center(width).rstrip
566
- pp << '-' * width
246
+ @connection_parameters = connection_parameters
567
247
 
568
- pp += lines.map {|line| " #{line}"}
248
+ # @local_tz is initialized as nil to avoid warnings when connect tries to use it
249
+ @local_tz = nil
250
+ @max_identifier_length = nil
569
251
 
570
- nrows = result.rows.length
571
- rows_label = nrows == 1 ? 'row' : 'rows'
572
- pp << "(#{nrows} #{rows_label})"
252
+ configure_connection
253
+ add_pg_encoders
254
+ add_pg_decoders
573
255
 
574
- pp.join("\n") + "\n"
575
- end
256
+ @type_map = Type::HashLookupTypeMap.new
257
+ initialize_type_map
258
+ @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
259
+ @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
576
260
  end
577
261
 
578
- # Executes a SELECT query and returns an array of rows. Each row is an
579
- # array of field values.
580
- def select_rows(sql, name = nil)
581
- select_raw(sql, name).last
262
+ def self.database_exists?(config)
263
+ !!ActiveRecord::Base.postgresql_connection(config)
264
+ rescue ActiveRecord::NoDatabaseError
265
+ false
582
266
  end
583
267
 
584
- # Executes an INSERT query and returns the new record's ID
585
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
586
- unless pk
587
- # Extract the table from the insert sql. Yuck.
588
- table_ref = extract_table_ref_from_insert_sql(sql)
589
- pk = primary_key(table_ref) if table_ref
268
+ # Is this connection alive and ready for queries?
269
+ def active?
270
+ @lock.synchronize do
271
+ @connection.query "SELECT 1"
590
272
  end
273
+ true
274
+ rescue PG::Error
275
+ false
276
+ end
591
277
 
592
- if pk
593
- select_value("#{sql} RETURNING #{quote_column_name(pk)}")
594
- else
278
+ # Close then reopen the connection.
279
+ def reconnect!
280
+ @lock.synchronize do
595
281
  super
282
+ @connection.reset
283
+ configure_connection
284
+ rescue PG::ConnectionBad
285
+ connect
596
286
  end
597
287
  end
598
- alias :create :insert
599
-
600
- # create a 2D array representing the result set
601
- def result_as_array(res) #:nodoc:
602
- # check if we have any binary column and if they need escaping
603
- ftypes = Array.new(res.nfields) do |i|
604
- [i, res.ftype(i)]
605
- end
606
-
607
- rows = res.values
608
- return rows unless ftypes.any? { |_, x|
609
- x == BYTEA_COLUMN_TYPE_OID || x == MONEY_COLUMN_TYPE_OID
610
- }
611
-
612
- typehash = ftypes.group_by { |_, type| type }
613
- binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
614
- monies = typehash[MONEY_COLUMN_TYPE_OID] || []
615
288
 
616
- rows.each do |row|
617
- # unescape string passed BYTEA field (OID == 17)
618
- binaries.each do |index, _|
619
- row[index] = unescape_bytea(row[index])
620
- end
621
-
622
- # If this is a money type column and there are any currency symbols,
623
- # then strip them off. Indeed it would be prettier to do this in
624
- # PostgreSQLColumn.string_to_decimal but would break form input
625
- # fields that call value_before_type_cast.
626
- monies.each do |index, _|
627
- data = row[index]
628
- # Because money output is formatted according to the locale, there are two
629
- # cases to consider (note the decimal separators):
630
- # (1) $12,345,678.12
631
- # (2) $12.345.678,12
632
- case data
633
- when /^-?\D+[\d,]+\.\d{2}$/ # (1)
634
- data.gsub!(/[^-\d.]/, '')
635
- when /^-?\D+[\d.]+,\d{2}$/ # (2)
636
- data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
637
- end
289
+ def reset!
290
+ @lock.synchronize do
291
+ clear_cache!
292
+ reset_transaction
293
+ unless @connection.transaction_status == ::PG::PQTRANS_IDLE
294
+ @connection.query "ROLLBACK"
638
295
  end
296
+ @connection.query "DISCARD ALL"
297
+ configure_connection
639
298
  end
640
299
  end
641
300
 
642
-
643
- # Queries the database and returns the results in an Array-like object
644
- def query(sql, name = nil) #:nodoc:
645
- log(sql, name) do
646
- result_as_array @connection.async_exec(sql)
647
- end
648
- end
649
-
650
- # Executes an SQL statement, returning a PGresult object on success
651
- # or raising a PGError exception otherwise.
652
- def execute(sql, name = nil)
653
- log(sql, name) do
654
- @connection.async_exec(sql)
655
- end
656
- end
657
-
658
- def substitute_at(column, index)
659
- Arel::Nodes::BindParam.new "$#{index + 1}"
660
- end
661
-
662
- def exec_query(sql, name = 'SQL', binds = [])
663
- log(sql, name, binds) do
664
- result = binds.empty? ? exec_no_cache(sql, binds) :
665
- exec_cache(sql, binds)
666
-
667
- ret = ActiveRecord::Result.new(result.fields, result_as_array(result))
668
- result.clear
669
- return ret
301
+ # Disconnects from the database if already connected. Otherwise, this
302
+ # method does nothing.
303
+ def disconnect!
304
+ @lock.synchronize do
305
+ super
306
+ @connection.close rescue nil
670
307
  end
671
308
  end
672
309
 
673
- def exec_delete(sql, name = 'SQL', binds = [])
674
- log(sql, name, binds) do
675
- result = binds.empty? ? exec_no_cache(sql, binds) :
676
- exec_cache(sql, binds)
677
- affected = result.cmd_tuples
678
- result.clear
679
- affected
680
- end
310
+ def discard! # :nodoc:
311
+ super
312
+ @connection.socket_io.reopen(IO::NULL) rescue nil
313
+ @connection = nil
681
314
  end
682
- alias :exec_update :exec_delete
683
315
 
684
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
685
- unless pk
686
- # Extract the table from the insert sql. Yuck.
687
- table_ref = extract_table_ref_from_insert_sql(sql)
688
- pk = primary_key(table_ref) if table_ref
689
- end
690
-
691
- sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
692
-
693
- [sql, binds]
316
+ def native_database_types #:nodoc:
317
+ NATIVE_DATABASE_TYPES
694
318
  end
695
319
 
696
- # Executes an UPDATE query and returns the number of affected tuples.
697
- def update_sql(sql, name = nil)
698
- super.cmd_tuples
320
+ def set_standard_conforming_strings
321
+ execute("SET standard_conforming_strings = on", "SCHEMA")
699
322
  end
700
323
 
701
- # Begins a transaction.
702
- def begin_db_transaction
703
- execute "BEGIN"
324
+ def supports_ddl_transactions?
325
+ true
704
326
  end
705
327
 
706
- # Commits a transaction.
707
- def commit_db_transaction
708
- execute "COMMIT"
328
+ def supports_advisory_locks?
329
+ true
709
330
  end
710
331
 
711
- # Aborts a transaction.
712
- def rollback_db_transaction
713
- execute "ROLLBACK"
332
+ def supports_explain?
333
+ true
714
334
  end
715
335
 
716
- def outside_transaction?
717
- @connection.transaction_status == PGconn::PQTRANS_IDLE
336
+ def supports_extensions?
337
+ true
718
338
  end
719
339
 
720
- def create_savepoint
721
- execute("SAVEPOINT #{current_savepoint_name}")
340
+ def supports_ranges?
341
+ true
722
342
  end
343
+ deprecate :supports_ranges?
723
344
 
724
- def rollback_to_savepoint
725
- execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
345
+ def supports_materialized_views?
346
+ true
726
347
  end
727
348
 
728
- def release_savepoint
729
- execute("RELEASE SAVEPOINT #{current_savepoint_name}")
349
+ def supports_foreign_tables?
350
+ true
730
351
  end
731
352
 
732
- # SCHEMA STATEMENTS ========================================
733
-
734
- # Drops the database specified on the +name+ attribute
735
- # and creates it again using the provided +options+.
736
- def recreate_database(name, options = {}) #:nodoc:
737
- drop_database(name)
738
- create_database(name, options)
353
+ def supports_pgcrypto_uuid?
354
+ database_version >= 90400
739
355
  end
740
356
 
741
- # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
742
- # <tt>:encoding</tt>, <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
743
- # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
744
- #
745
- # Example:
746
- # create_database config[:database], config
747
- # create_database 'foo_development', :encoding => 'unicode'
748
- def create_database(name, options = {})
749
- options = options.reverse_merge(:encoding => "utf8")
750
-
751
- option_string = options.symbolize_keys.sum do |key, value|
752
- case key
753
- when :owner
754
- " OWNER = \"#{value}\""
755
- when :template
756
- " TEMPLATE = \"#{value}\""
757
- when :encoding
758
- " ENCODING = '#{value}'"
759
- when :tablespace
760
- " TABLESPACE = \"#{value}\""
761
- when :connection_limit
762
- " CONNECTION LIMIT = #{value}"
763
- else
764
- ""
765
- end
357
+ def supports_optimizer_hints?
358
+ unless defined?(@has_pg_hint_plan)
359
+ @has_pg_hint_plan = extension_available?("pg_hint_plan")
766
360
  end
767
-
768
- execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
361
+ @has_pg_hint_plan
769
362
  end
770
363
 
771
- # Drops a PostgreSQL database.
772
- #
773
- # Example:
774
- # drop_database 'matt_development'
775
- def drop_database(name) #:nodoc:
776
- execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
364
+ def supports_lazy_transactions?
365
+ true
777
366
  end
778
367
 
779
- # Returns the list of all tables in the schema search path or a specified schema.
780
- def tables(name = nil)
781
- query(<<-SQL, 'SCHEMA').map { |row| row[0] }
782
- SELECT tablename
783
- FROM pg_tables
784
- WHERE schemaname = ANY (current_schemas(false))
785
- SQL
368
+ def get_advisory_lock(lock_id) # :nodoc:
369
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
370
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
371
+ end
372
+ query_value("SELECT pg_try_advisory_lock(#{lock_id})")
786
373
  end
787
374
 
788
- # Returns true if table exists.
789
- # If the schema is not specified as part of +name+ then it will only find tables within
790
- # the current schema search path (regardless of permissions to access tables in other schemas)
791
- def table_exists?(name)
792
- schema, table = Utils.extract_schema_and_table(name.to_s)
793
- return false unless table
794
-
795
- binds = [[nil, table]]
796
- binds << [nil, schema] if schema
797
-
798
- exec_query(<<-SQL, 'SCHEMA', binds).rows.first[0].to_i > 0
799
- SELECT COUNT(*)
800
- FROM pg_class c
801
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
802
- WHERE c.relkind in ('v','r')
803
- AND c.relname = $1
804
- AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'}
805
- SQL
375
+ def release_advisory_lock(lock_id) # :nodoc:
376
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
377
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
378
+ end
379
+ query_value("SELECT pg_advisory_unlock(#{lock_id})")
806
380
  end
807
381
 
808
- # Returns true if schema exists.
809
- def schema_exists?(name)
810
- exec_query(<<-SQL, 'SCHEMA', [[nil, name]]).rows.first[0].to_i > 0
811
- SELECT COUNT(*)
812
- FROM pg_namespace
813
- WHERE nspname = $1
814
- SQL
382
+ def enable_extension(name)
383
+ exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
384
+ reload_type_map
385
+ }
815
386
  end
816
387
 
817
- # Returns an array of indexes for the given table.
818
- def indexes(table_name, name = nil)
819
- result = query(<<-SQL, name)
820
- SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
821
- FROM pg_class t
822
- INNER JOIN pg_index d ON t.oid = d.indrelid
823
- INNER JOIN pg_class i ON d.indexrelid = i.oid
824
- WHERE i.relkind = 'i'
825
- AND d.indisprimary = 'f'
826
- AND t.relname = '#{table_name}'
827
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
828
- ORDER BY i.relname
829
- SQL
830
-
831
-
832
- result.map do |row|
833
- index_name = row[0]
834
- unique = row[1] == 't'
835
- indkey = row[2].split(" ")
836
- inddef = row[3]
837
- oid = row[4]
838
-
839
- columns = Hash[query(<<-SQL, "Columns for index #{row[0]} on #{table_name}")]
840
- SELECT a.attnum, a.attname
841
- FROM pg_attribute a
842
- WHERE a.attrelid = #{oid}
843
- AND a.attnum IN (#{indkey.join(",")})
844
- SQL
845
-
846
- column_names = columns.values_at(*indkey).compact
847
-
848
- # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
849
- desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
850
- orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
851
-
852
- column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
853
- end.compact
388
+ def disable_extension(name)
389
+ exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
390
+ reload_type_map
391
+ }
854
392
  end
855
393
 
856
- # Returns the list of all column definitions for a table.
857
- def columns(table_name, name = nil)
858
- # Limit, precision, and scale are all handled by the superclass.
859
- column_definitions(table_name).collect do |column_name, type, default, notnull|
860
- PostgreSQLColumn.new(column_name, default, type, notnull == 'f')
861
- end
394
+ def extension_available?(name)
395
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
862
396
  end
863
397
 
864
- # Returns the current database name.
865
- def current_database
866
- query('select current_database()')[0][0]
398
+ def extension_enabled?(name)
399
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
867
400
  end
868
401
 
869
- # Returns the current schema name.
870
- def current_schema
871
- query('SELECT current_schema', 'SCHEMA')[0][0]
402
+ def extensions
403
+ exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
872
404
  end
873
405
 
874
- # Returns the current database encoding format.
875
- def encoding
876
- query(<<-end_sql)[0][0]
877
- SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
878
- WHERE pg_database.datname LIKE '#{current_database}'
879
- end_sql
406
+ # Returns the configured supported identifier length supported by PostgreSQL
407
+ def max_identifier_length
408
+ @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
880
409
  end
881
410
 
882
- # Sets the schema search path to a string of comma-separated schema names.
883
- # Names beginning with $ have to be quoted (e.g. $user => '$user').
884
- # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
885
- #
886
- # This should be not be called manually but set in database.yml.
887
- def schema_search_path=(schema_csv)
888
- if schema_csv
889
- execute("SET search_path TO #{schema_csv}", 'SCHEMA')
890
- @schema_search_path = schema_csv
891
- end
411
+ # Set the authorized user for this session
412
+ def session_auth=(user)
413
+ clear_cache!
414
+ execute("SET SESSION AUTHORIZATION #{user}")
892
415
  end
893
416
 
894
- # Returns the active schema search path.
895
- def schema_search_path
896
- @schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0]
417
+ def use_insert_returning?
418
+ @use_insert_returning
897
419
  end
898
420
 
899
- # Returns the current client message level.
900
- def client_min_messages
901
- query('SHOW client_min_messages', 'SCHEMA')[0][0]
421
+ def column_name_for_operation(operation, node) # :nodoc:
422
+ OPERATION_ALIASES.fetch(operation) { operation.downcase }
902
423
  end
903
424
 
904
- # Set the client message level.
905
- def client_min_messages=(level)
906
- execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
907
- end
425
+ OPERATION_ALIASES = { # :nodoc:
426
+ "maximum" => "max",
427
+ "minimum" => "min",
428
+ "average" => "avg",
429
+ }
908
430
 
909
- # Returns the sequence name for a table's primary key or some other specified key.
910
- def default_sequence_name(table_name, pk = nil) #:nodoc:
911
- serial_sequence(table_name, pk || 'id').split('.').last
912
- rescue ActiveRecord::StatementInvalid
913
- "#{table_name}_#{pk || 'id'}_seq"
431
+ # Returns the version of the connected PostgreSQL server.
432
+ def get_database_version # :nodoc:
433
+ @connection.server_version
914
434
  end
435
+ alias :postgresql_version :database_version
915
436
 
916
- def serial_sequence(table, column)
917
- result = exec_query(<<-eosql, 'SCHEMA', [[nil, table], [nil, column]])
918
- SELECT pg_get_serial_sequence($1, $2)
919
- eosql
920
- result.rows.first.first
437
+ def default_index_type?(index) # :nodoc:
438
+ index.using == :btree || super
921
439
  end
922
440
 
923
- # Resets the sequence of a table's primary key to the maximum value.
924
- def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
925
- unless pk and sequence
926
- default_pk, default_sequence = pk_and_sequence_for(table)
927
-
928
- pk ||= default_pk
929
- sequence ||= default_sequence
930
- end
441
+ def build_insert_sql(insert) # :nodoc:
442
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
931
443
 
932
- if @logger && pk && !sequence
933
- @logger.warn "#{table} has primary key #{pk} with no default sequence"
444
+ if insert.skip_duplicates?
445
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
446
+ elsif insert.update_duplicates?
447
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
448
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
934
449
  end
935
450
 
936
- if pk && sequence
937
- quoted_sequence = quote_table_name(sequence)
938
-
939
- select_value <<-end_sql, 'Reset sequence'
940
- SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
941
- end_sql
942
- end
451
+ sql << " RETURNING #{insert.returning}" if insert.returning
452
+ sql
943
453
  end
944
454
 
945
- # Returns a table's primary key and belonging sequence.
946
- def pk_and_sequence_for(table) #:nodoc:
947
- # First try looking for a sequence with a dependency on the
948
- # given table's primary key.
949
- result = query(<<-end_sql, 'PK and serial sequence')[0]
950
- SELECT attr.attname, seq.relname
951
- FROM pg_class seq,
952
- pg_attribute attr,
953
- pg_depend dep,
954
- pg_namespace name,
955
- pg_constraint cons
956
- WHERE seq.oid = dep.objid
957
- AND seq.relkind = 'S'
958
- AND attr.attrelid = dep.refobjid
959
- AND attr.attnum = dep.refobjsubid
960
- AND attr.attrelid = cons.conrelid
961
- AND attr.attnum = cons.conkey[1]
962
- AND cons.contype = 'p'
963
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
964
- end_sql
965
-
966
- if result.nil? or result.empty?
967
- # If that fails, try parsing the primary key's default value.
968
- # Support the 7.x and 8.0 nextval('foo'::text) as well as
969
- # the 8.1+ nextval('foo'::regclass).
970
- result = query(<<-end_sql, 'PK and custom sequence')[0]
971
- SELECT attr.attname,
972
- CASE
973
- WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
974
- substr(split_part(def.adsrc, '''', 2),
975
- strpos(split_part(def.adsrc, '''', 2), '.')+1)
976
- ELSE split_part(def.adsrc, '''', 2)
977
- END
978
- FROM pg_class t
979
- JOIN pg_attribute attr ON (t.oid = attrelid)
980
- JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
981
- JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
982
- WHERE t.oid = '#{quote_table_name(table)}'::regclass
983
- AND cons.contype = 'p'
984
- AND def.adsrc ~* 'nextval'
985
- end_sql
455
+ def check_version # :nodoc:
456
+ if database_version < 90300
457
+ raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
986
458
  end
987
-
988
- [result.first, result.last]
989
- rescue
990
- nil
991
459
  end
992
460
 
993
- # Returns just a table's primary key
994
- def primary_key(table)
995
- row = exec_query(<<-end_sql, 'SCHEMA', [[nil, table]]).rows.first
996
- SELECT DISTINCT(attr.attname)
997
- FROM pg_attribute attr
998
- INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
999
- INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
1000
- WHERE cons.contype = 'p'
1001
- AND dep.refobjid = $1::regclass
1002
- end_sql
1003
-
1004
- row && row.first
1005
- end
461
+ private
1006
462
 
1007
- # Renames a table.
1008
- #
1009
- # Example:
1010
- # rename_table('octopuses', 'octopi')
1011
- def rename_table(name, new_name)
1012
- clear_cache!
1013
- execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
1014
- end
463
+ # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
464
+ VALUE_LIMIT_VIOLATION = "22001"
465
+ NUMERIC_VALUE_OUT_OF_RANGE = "22003"
466
+ NOT_NULL_VIOLATION = "23502"
467
+ FOREIGN_KEY_VIOLATION = "23503"
468
+ UNIQUE_VIOLATION = "23505"
469
+ SERIALIZATION_FAILURE = "40001"
470
+ DEADLOCK_DETECTED = "40P01"
471
+ LOCK_NOT_AVAILABLE = "55P03"
472
+ QUERY_CANCELED = "57014"
473
+
474
+ def translate_exception(exception, message:, sql:, binds:)
475
+ return exception unless exception.respond_to?(:result)
476
+
477
+ case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
478
+ when UNIQUE_VIOLATION
479
+ RecordNotUnique.new(message, sql: sql, binds: binds)
480
+ when FOREIGN_KEY_VIOLATION
481
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
482
+ when VALUE_LIMIT_VIOLATION
483
+ ValueTooLong.new(message, sql: sql, binds: binds)
484
+ when NUMERIC_VALUE_OUT_OF_RANGE
485
+ RangeError.new(message, sql: sql, binds: binds)
486
+ when NOT_NULL_VIOLATION
487
+ NotNullViolation.new(message, sql: sql, binds: binds)
488
+ when SERIALIZATION_FAILURE
489
+ SerializationFailure.new(message, sql: sql, binds: binds)
490
+ when DEADLOCK_DETECTED
491
+ Deadlocked.new(message, sql: sql, binds: binds)
492
+ when LOCK_NOT_AVAILABLE
493
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
494
+ when QUERY_CANCELED
495
+ QueryCanceled.new(message, sql: sql, binds: binds)
496
+ else
497
+ super
498
+ end
499
+ end
1015
500
 
1016
- # Adds a new column to the named table.
1017
- # See TableDefinition#column for details of the options you can use.
1018
- def add_column(table_name, column_name, type, options = {})
1019
- clear_cache!
1020
- add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
1021
- add_column_options!(add_column_sql, options)
501
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
502
+ if !type_map.key?(oid)
503
+ load_additional_types([oid])
504
+ end
1022
505
 
1023
- execute add_column_sql
1024
- end
506
+ type_map.fetch(oid, fmod, sql_type) {
507
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
508
+ Type.default_value.tap do |cast_type|
509
+ type_map.register_type(oid, cast_type)
510
+ end
511
+ }
512
+ end
513
+
514
+ def initialize_type_map(m = type_map)
515
+ m.register_type "int2", Type::Integer.new(limit: 2)
516
+ m.register_type "int4", Type::Integer.new(limit: 4)
517
+ m.register_type "int8", Type::Integer.new(limit: 8)
518
+ m.register_type "oid", OID::Oid.new
519
+ m.register_type "float4", Type::Float.new
520
+ m.alias_type "float8", "float4"
521
+ m.register_type "text", Type::Text.new
522
+ register_class_with_limit m, "varchar", Type::String
523
+ m.alias_type "char", "varchar"
524
+ m.alias_type "name", "varchar"
525
+ m.alias_type "bpchar", "varchar"
526
+ m.register_type "bool", Type::Boolean.new
527
+ register_class_with_limit m, "bit", OID::Bit
528
+ register_class_with_limit m, "varbit", OID::BitVarying
529
+ m.alias_type "timestamptz", "timestamp"
530
+ m.register_type "date", OID::Date.new
531
+
532
+ m.register_type "money", OID::Money.new
533
+ m.register_type "bytea", OID::Bytea.new
534
+ m.register_type "point", OID::Point.new
535
+ m.register_type "hstore", OID::Hstore.new
536
+ m.register_type "json", Type::Json.new
537
+ m.register_type "jsonb", OID::Jsonb.new
538
+ m.register_type "cidr", OID::Cidr.new
539
+ m.register_type "inet", OID::Inet.new
540
+ m.register_type "uuid", OID::Uuid.new
541
+ m.register_type "xml", OID::Xml.new
542
+ m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
543
+ m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
544
+ m.register_type "citext", OID::SpecializedString.new(:citext)
545
+ m.register_type "ltree", OID::SpecializedString.new(:ltree)
546
+ m.register_type "line", OID::SpecializedString.new(:line)
547
+ m.register_type "lseg", OID::SpecializedString.new(:lseg)
548
+ m.register_type "box", OID::SpecializedString.new(:box)
549
+ m.register_type "path", OID::SpecializedString.new(:path)
550
+ m.register_type "polygon", OID::SpecializedString.new(:polygon)
551
+ m.register_type "circle", OID::SpecializedString.new(:circle)
552
+
553
+ m.register_type "interval" do |_, _, sql_type|
554
+ precision = extract_precision(sql_type)
555
+ OID::SpecializedString.new(:interval, precision: precision)
556
+ end
1025
557
 
1026
- # Changes the column of a table.
1027
- def change_column(table_name, column_name, type, options = {})
1028
- clear_cache!
1029
- quoted_table_name = quote_table_name(table_name)
558
+ register_class_with_precision m, "time", Type::Time
559
+ register_class_with_precision m, "timestamp", OID::DateTime
560
+
561
+ m.register_type "numeric" do |_, fmod, sql_type|
562
+ precision = extract_precision(sql_type)
563
+ scale = extract_scale(sql_type)
564
+
565
+ # The type for the numeric depends on the width of the field,
566
+ # so we'll do something special here.
567
+ #
568
+ # When dealing with decimal columns:
569
+ #
570
+ # places after decimal = fmod - 4 & 0xffff
571
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
572
+ if fmod && (fmod - 4 & 0xffff).zero?
573
+ # FIXME: Remove this class, and the second argument to
574
+ # lookups on PG
575
+ Type::DecimalWithoutScale.new(precision: precision)
576
+ else
577
+ OID::Decimal.new(precision: precision, scale: scale)
578
+ end
579
+ end
1030
580
 
1031
- execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
581
+ load_additional_types
582
+ end
1032
583
 
1033
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
1034
- change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
1035
- end
584
+ # Extracts the value from a PostgreSQL column default definition.
585
+ def extract_value_from_default(default)
586
+ case default
587
+ # Quoted types
588
+ when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
589
+ # The default 'now'::date is CURRENT_DATE
590
+ if $1 == "now" && $2 == "date"
591
+ nil
592
+ else
593
+ $1.gsub("''", "'")
594
+ end
595
+ # Boolean types
596
+ when "true", "false"
597
+ default
598
+ # Numeric types
599
+ when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
600
+ $1
601
+ # Object identifier types
602
+ when /\A-?\d+\z/
603
+ $1
604
+ else
605
+ # Anything else is blank, some user type, or some function
606
+ # and we can't know the value of that, so return nil.
607
+ nil
608
+ end
609
+ end
1036
610
 
1037
- # Changes the default value of a table column.
1038
- def change_column_default(table_name, column_name, default)
1039
- clear_cache!
1040
- execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
1041
- end
611
+ def extract_default_function(default_value, default)
612
+ default if has_default_function?(default_value, default)
613
+ end
1042
614
 
1043
- def change_column_null(table_name, column_name, null, default = nil)
1044
- clear_cache!
1045
- unless null || default.nil?
1046
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
615
+ def has_default_function?(default_value, default)
616
+ !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
1047
617
  end
1048
- execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
1049
- end
1050
618
 
1051
- # Renames a column in a table.
1052
- def rename_column(table_name, column_name, new_column_name)
1053
- clear_cache!
1054
- execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1055
- end
619
+ def load_additional_types(oids = nil)
620
+ initializer = OID::TypeMapInitializer.new(type_map)
1056
621
 
1057
- def remove_index!(table_name, index_name) #:nodoc:
1058
- execute "DROP INDEX #{quote_table_name(index_name)}"
1059
- end
622
+ query = <<~SQL
623
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
624
+ FROM pg_type as t
625
+ LEFT JOIN pg_range as r ON oid = rngtypid
626
+ SQL
1060
627
 
1061
- def rename_index(table_name, old_name, new_name)
1062
- execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
1063
- end
628
+ if oids
629
+ query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
630
+ else
631
+ query += initializer.query_conditions_for_initial_load
632
+ end
1064
633
 
1065
- def index_name_length
1066
- 63
1067
- end
634
+ execute_and_clear(query, "SCHEMA", []) do |records|
635
+ initializer.run(records)
636
+ end
637
+ end
1068
638
 
1069
- # Maps logical Rails types to PostgreSQL-specific data types.
1070
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
1071
- case type.to_s
1072
- when 'binary'
1073
- # PostgreSQL doesn't support limits on binary (bytea) columns.
1074
- # The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
1075
- case limit
1076
- when nil, 0..0x3fffffff; super(type)
1077
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
639
+ FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
640
+
641
+ def execute_and_clear(sql, name, binds, prepare: false)
642
+ if preventing_writes? && write_query?(sql)
643
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
1078
644
  end
1079
- when 'integer'
1080
- return 'integer' unless limit
1081
-
1082
- case limit
1083
- when 1, 2; 'smallint'
1084
- when 3, 4; 'integer'
1085
- when 5..8; 'bigint'
1086
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
645
+
646
+ if without_prepared_statement?(binds)
647
+ result = exec_no_cache(sql, name, [])
648
+ elsif !prepare
649
+ result = exec_no_cache(sql, name, binds)
650
+ else
651
+ result = exec_cache(sql, name, binds)
1087
652
  end
1088
- else
1089
- super
653
+ ret = yield result
654
+ result.clear
655
+ ret
1090
656
  end
1091
- end
1092
657
 
1093
- # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
1094
- #
1095
- # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
1096
- # requires that the ORDER BY include the distinct column.
1097
- #
1098
- # distinct("posts.id", "posts.created_at desc")
1099
- def distinct(columns, orders) #:nodoc:
1100
- return "DISTINCT #{columns}" if orders.empty?
658
+ def exec_no_cache(sql, name, binds)
659
+ materialize_transactions
1101
660
 
1102
- # Construct a clean list of column names from the ORDER BY clause, removing
1103
- # any ASC/DESC modifiers
1104
- order_columns = orders.collect { |s| s.gsub(/\s+(ASC|DESC)\s*/i, '') }
1105
- order_columns.delete_if { |c| c.blank? }
1106
- order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
661
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
662
+ # made since we established the connection
663
+ update_typemap_for_default_timezone
1107
664
 
1108
- "DISTINCT #{columns}, #{order_columns * ', '}"
1109
- end
665
+ type_casted_binds = type_casted_binds(binds)
666
+ log(sql, name, binds, type_casted_binds) do
667
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
668
+ @connection.exec_params(sql, type_casted_binds)
669
+ end
670
+ end
671
+ end
1110
672
 
1111
- module Utils
1112
- extend self
673
+ def exec_cache(sql, name, binds)
674
+ materialize_transactions
675
+ update_typemap_for_default_timezone
1113
676
 
1114
- # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
1115
- # +schema_name+ is nil if not specified in +name+.
1116
- # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
1117
- # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
1118
- #
1119
- # * <tt>table_name</tt>
1120
- # * <tt>"table.name"</tt>
1121
- # * <tt>schema_name.table_name</tt>
1122
- # * <tt>schema_name."table.name"</tt>
1123
- # * <tt>"schema.name"."table name"</tt>
1124
- def extract_schema_and_table(name)
1125
- table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
1126
- [schema, table]
1127
- end
1128
- end
677
+ stmt_key = prepare_statement(sql, binds)
678
+ type_casted_binds = type_casted_binds(binds)
1129
679
 
1130
- protected
1131
- # Returns the version of the connected PostgreSQL server.
1132
- def postgresql_version
1133
- @connection.server_version
1134
- end
680
+ log(sql, name, binds, type_casted_binds, stmt_key) do
681
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
682
+ @connection.exec_prepared(stmt_key, type_casted_binds)
683
+ end
684
+ end
685
+ rescue ActiveRecord::StatementInvalid => e
686
+ raise unless is_cached_plan_failure?(e)
1135
687
 
1136
- def translate_exception(exception, message)
1137
- case exception.message
1138
- when /duplicate key value violates unique constraint/
1139
- RecordNotUnique.new(message, exception)
1140
- when /violates foreign key constraint/
1141
- InvalidForeignKey.new(message, exception)
688
+ # Nothing we can do if we are in a transaction because all commands
689
+ # will raise InFailedSQLTransaction
690
+ if in_transaction?
691
+ raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
1142
692
  else
1143
- super
693
+ @lock.synchronize do
694
+ # outside of transactions we can simply flush this query and retry
695
+ @statements.delete sql_key(sql)
696
+ end
697
+ retry
1144
698
  end
1145
699
  end
1146
700
 
1147
- private
1148
- FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
1149
-
1150
- def exec_no_cache(sql, binds)
1151
- @connection.async_exec(sql)
701
+ # Annoyingly, the code for prepared statements whose return value may
702
+ # have changed is FEATURE_NOT_SUPPORTED.
703
+ #
704
+ # This covers various different error types so we need to do additional
705
+ # work to classify the exception definitively as a
706
+ # ActiveRecord::PreparedStatementCacheExpired
707
+ #
708
+ # Check here for more details:
709
+ # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
710
+ CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
711
+ def is_cached_plan_failure?(e)
712
+ pgerror = e.cause
713
+ code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
714
+ code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
715
+ rescue
716
+ false
1152
717
  end
1153
718
 
1154
- def exec_cache(sql, binds)
1155
- begin
1156
- stmt_key = prepare_statement sql
1157
-
1158
- # Clear the queue
1159
- @connection.get_last_result
1160
- @connection.send_query_prepared(stmt_key, binds.map { |col, val|
1161
- type_cast(val, col)
1162
- })
1163
- @connection.block
1164
- @connection.get_last_result
1165
- rescue PGError => e
1166
- # Get the PG code for the failure. Annoyingly, the code for
1167
- # prepared statements whose return value may have changed is
1168
- # FEATURE_NOT_SUPPORTED. Check here for more details:
1169
- # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
1170
- code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
1171
- if FEATURE_NOT_SUPPORTED == code
1172
- @statements.delete sql_key(sql)
1173
- retry
1174
- else
1175
- raise e
1176
- end
1177
- end
719
+ def in_transaction?
720
+ open_transactions > 0
1178
721
  end
1179
722
 
1180
723
  # Returns the statement identifier for the client side cache
@@ -1185,32 +728,31 @@ module ActiveRecord
1185
728
 
1186
729
  # Prepare the statement if it hasn't been prepared, return
1187
730
  # the statement key.
1188
- def prepare_statement(sql)
1189
- sql_key = sql_key(sql)
1190
- unless @statements.key? sql_key
1191
- nextkey = @statements.next_key
1192
- @connection.prepare nextkey, sql
1193
- @statements[sql_key] = nextkey
731
+ def prepare_statement(sql, binds)
732
+ @lock.synchronize do
733
+ sql_key = sql_key(sql)
734
+ unless @statements.key? sql_key
735
+ nextkey = @statements.next_key
736
+ begin
737
+ @connection.prepare nextkey, sql
738
+ rescue => e
739
+ raise translate_exception_class(e, sql, binds)
740
+ end
741
+ # Clear the queue
742
+ @connection.get_last_result
743
+ @statements[sql_key] = nextkey
744
+ end
745
+ @statements[sql_key]
1194
746
  end
1195
- @statements[sql_key]
1196
747
  end
1197
748
 
1198
- # The internal PostgreSQL identifier of the money data type.
1199
- MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
1200
- # The internal PostgreSQL identifier of the BYTEA data type.
1201
- BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
1202
-
1203
749
  # Connects to a PostgreSQL server and sets up the adapter depending on the
1204
750
  # connected server's characteristics.
1205
751
  def connect
1206
- @connection = PGconn.connect(*@connection_parameters)
1207
-
1208
- # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
1209
- # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
1210
- # should know about this but can't detect it there, so deal with it here.
1211
- PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
1212
-
752
+ @connection = PG.connect(@connection_parameters)
1213
753
  configure_connection
754
+ add_pg_encoders
755
+ add_pg_decoders
1214
756
  end
1215
757
 
1216
758
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -1219,45 +761,40 @@ module ActiveRecord
1219
761
  if @config[:encoding]
1220
762
  @connection.set_client_encoding(@config[:encoding])
1221
763
  end
1222
- self.client_min_messages = @config[:min_messages] if @config[:min_messages]
764
+ self.client_min_messages = @config[:min_messages] || "warning"
1223
765
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
1224
766
 
1225
- # Use standard-conforming strings if available so we don't have to do the E'...' dance.
767
+ # Use standard-conforming strings so we don't have to do the E'...' dance.
1226
768
  set_standard_conforming_strings
1227
769
 
770
+ variables = @config.fetch(:variables, {}).stringify_keys
771
+
1228
772
  # If using Active Record's time zone support configure the connection to return
1229
773
  # TIMESTAMP WITH ZONE types in UTC.
1230
- if ActiveRecord::Base.default_timezone == :utc
1231
- execute("SET time zone 'UTC'", 'SCHEMA')
1232
- elsif @local_tz
1233
- execute("SET time zone '#{@local_tz}'", 'SCHEMA')
774
+ unless variables["timezone"]
775
+ if ActiveRecord::Base.default_timezone == :utc
776
+ variables["timezone"] = "UTC"
777
+ elsif @local_tz
778
+ variables["timezone"] = @local_tz
779
+ end
1234
780
  end
1235
- end
1236
-
1237
- # Returns the current ID of a table's sequence.
1238
- def last_insert_id(sequence_name) #:nodoc:
1239
- r = exec_query("SELECT currval($1)", 'SQL', [[nil, sequence_name]])
1240
- Integer(r.rows.first.first)
1241
- end
1242
781
 
1243
- # Executes a SELECT query and returns the results, performing any data type
1244
- # conversions that are required to be performed here instead of in PostgreSQLColumn.
1245
- def select(sql, name = nil, binds = [])
1246
- exec_query(sql, name, binds).to_a
1247
- end
1248
-
1249
- def select_raw(sql, name = nil)
1250
- res = execute(sql, name)
1251
- results = result_as_array(res)
1252
- fields = res.fields
1253
- res.clear
1254
- return fields, results
782
+ # SET statements from :variables config hash
783
+ # https://www.postgresql.org/docs/current/static/sql-set.html
784
+ variables.map do |k, v|
785
+ if v == ":default" || v == :default
786
+ # Sets the value to the global or compile default
787
+ execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
788
+ elsif !v.nil?
789
+ execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
790
+ end
791
+ end
1255
792
  end
1256
793
 
1257
794
  # Returns the list of a table's column names, data types, and default values.
1258
795
  #
1259
796
  # The underlying query is roughly:
1260
- # SELECT column.name, column.type, default.value
797
+ # SELECT column.name, column.type, default.value, column.comment
1261
798
  # FROM column LEFT JOIN default
1262
799
  # ON column.table_id = default.table_id
1263
800
  # AND column.num = default.column_num
@@ -1272,35 +809,141 @@ module ActiveRecord
1272
809
  # Query implementation notes:
1273
810
  # - format_type includes the column size constraint, e.g. varchar(50)
1274
811
  # - ::regclass is a function that gives the id for a table name
1275
- def column_definitions(table_name) #:nodoc:
1276
- exec_query(<<-end_sql, 'SCHEMA').rows
1277
- SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
1278
- FROM pg_attribute a LEFT JOIN pg_attrdef d
1279
- ON a.attrelid = d.adrelid AND a.attnum = d.adnum
1280
- WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
1281
- AND a.attnum > 0 AND NOT a.attisdropped
1282
- ORDER BY a.attnum
1283
- end_sql
812
+ def column_definitions(table_name)
813
+ query(<<~SQL, "SCHEMA")
814
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
815
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
816
+ c.collname, col_description(a.attrelid, a.attnum) AS comment
817
+ FROM pg_attribute a
818
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
819
+ LEFT JOIN pg_type t ON a.atttypid = t.oid
820
+ LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
821
+ WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
822
+ AND a.attnum > 0 AND NOT a.attisdropped
823
+ ORDER BY a.attnum
824
+ SQL
1284
825
  end
1285
826
 
1286
- def extract_pg_identifier_from_name(name)
1287
- match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
827
+ def extract_table_ref_from_insert_sql(sql)
828
+ sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
829
+ $1.strip if $1
830
+ end
1288
831
 
1289
- if match_data
1290
- rest = name[match_data[0].length, name.length]
1291
- rest = rest[1, rest.length] if rest.start_with? "."
1292
- [match_data[1], (rest.length > 0 ? rest : nil)]
832
+ def arel_visitor
833
+ Arel::Visitors::PostgreSQL.new(self)
834
+ end
835
+
836
+ def build_statement_pool
837
+ StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
838
+ end
839
+
840
+ def can_perform_case_insensitive_comparison_for?(column)
841
+ @case_insensitive_cache ||= {}
842
+ @case_insensitive_cache[column.sql_type] ||= begin
843
+ sql = <<~SQL
844
+ SELECT exists(
845
+ SELECT * FROM pg_proc
846
+ WHERE proname = 'lower'
847
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
848
+ ) OR exists(
849
+ SELECT * FROM pg_proc
850
+ INNER JOIN pg_cast
851
+ ON ARRAY[casttarget]::oidvector = proargtypes
852
+ WHERE proname = 'lower'
853
+ AND castsource = #{quote column.sql_type}::regtype
854
+ )
855
+ SQL
856
+ execute_and_clear(sql, "SCHEMA", []) do |result|
857
+ result.getvalue(0, 0)
858
+ end
1293
859
  end
1294
860
  end
1295
861
 
1296
- def extract_table_ref_from_insert_sql(sql)
1297
- sql[/into\s+([^\(]*).*values\s*\(/i]
1298
- $1.strip if $1
862
+ def add_pg_encoders
863
+ map = PG::TypeMapByClass.new
864
+ map[Integer] = PG::TextEncoder::Integer.new
865
+ map[TrueClass] = PG::TextEncoder::Boolean.new
866
+ map[FalseClass] = PG::TextEncoder::Boolean.new
867
+ @connection.type_map_for_queries = map
1299
868
  end
1300
869
 
1301
- def table_definition
1302
- TableDefinition.new(self)
870
+ def update_typemap_for_default_timezone
871
+ if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
872
+ decoder_class = ActiveRecord::Base.default_timezone == :utc ?
873
+ PG::TextDecoder::TimestampUtc :
874
+ PG::TextDecoder::TimestampWithoutTimeZone
875
+
876
+ @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
877
+ @connection.type_map_for_results.add_coder(@timestamp_decoder)
878
+ @default_timezone = ActiveRecord::Base.default_timezone
879
+ end
1303
880
  end
881
+
882
+ def add_pg_decoders
883
+ @default_timezone = nil
884
+ @timestamp_decoder = nil
885
+
886
+ coders_by_name = {
887
+ "int2" => PG::TextDecoder::Integer,
888
+ "int4" => PG::TextDecoder::Integer,
889
+ "int8" => PG::TextDecoder::Integer,
890
+ "oid" => PG::TextDecoder::Integer,
891
+ "float4" => PG::TextDecoder::Float,
892
+ "float8" => PG::TextDecoder::Float,
893
+ "bool" => PG::TextDecoder::Boolean,
894
+ }
895
+
896
+ if defined?(PG::TextDecoder::TimestampUtc)
897
+ # Use native PG encoders available since pg-1.1
898
+ coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
899
+ coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
900
+ end
901
+
902
+ known_coder_types = coders_by_name.keys.map { |n| quote(n) }
903
+ query = <<~SQL % known_coder_types.join(", ")
904
+ SELECT t.oid, t.typname
905
+ FROM pg_type as t
906
+ WHERE t.typname IN (%s)
907
+ SQL
908
+ coders = execute_and_clear(query, "SCHEMA", []) do |result|
909
+ result
910
+ .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
911
+ .compact
912
+ end
913
+
914
+ map = PG::TypeMapByOid.new
915
+ coders.each { |coder| map.add_coder(coder) }
916
+ @connection.type_map_for_results = map
917
+
918
+ # extract timestamp decoder for use in update_typemap_for_default_timezone
919
+ @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
920
+ update_typemap_for_default_timezone
921
+ end
922
+
923
+ def construct_coder(row, coder_class)
924
+ return unless coder_class
925
+ coder_class.new(oid: row["oid"].to_i, name: row["typname"])
926
+ end
927
+
928
+ ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
929
+ ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
930
+ ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
931
+ ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
932
+ ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
933
+ ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
934
+ ActiveRecord::Type.register(:date, OID::Date, adapter: :postgresql)
935
+ ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
936
+ ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
937
+ ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
938
+ ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
939
+ ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
940
+ ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
941
+ ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
942
+ ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
943
+ ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
944
+ ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
945
+ ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
946
+ ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
1304
947
  end
1305
948
  end
1306
949
  end