activerecord 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 (340) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1013 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +195 -0
  8. data/lib/active_record/aggregations.rb +285 -0
  9. data/lib/active_record/association_relation.rb +40 -0
  10. data/lib/active_record/associations.rb +1865 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +332 -0
  13. data/lib/active_record/associations/association_scope.rb +166 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +124 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +36 -0
  16. data/lib/active_record/associations/builder/association.rb +136 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +130 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +72 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +114 -0
  20. data/lib/active_record/associations/builder/has_many.rb +19 -0
  21. data/lib/active_record/associations/builder/has_one.rb +64 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +44 -0
  23. data/lib/active_record/associations/collection_association.rb +498 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1128 -0
  25. data/lib/active_record/associations/foreign_association.rb +20 -0
  26. data/lib/active_record/associations/has_many_association.rb +136 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +220 -0
  28. data/lib/active_record/associations/has_one_association.rb +118 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +258 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +80 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +201 -0
  35. data/lib/active_record/associations/preloader/association.rb +133 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +116 -0
  37. data/lib/active_record/associations/singular_association.rb +59 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +85 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +420 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +81 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +221 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +136 -0
  45. data/lib/active_record/attribute_methods/query.rb +41 -0
  46. data/lib/active_record/attribute_methods/read.rb +47 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +61 -0
  50. data/lib/active_record/attributes.rb +279 -0
  51. data/lib/active_record/autosave_association.rb +508 -0
  52. data/lib/active_record/base.rb +328 -0
  53. data/lib/active_record/callbacks.rb +339 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1165 -0
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +85 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +512 -0
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +154 -0
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +251 -0
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +713 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1475 -0
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +761 -0
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +821 -0
  69. data/lib/active_record/connection_adapters/column.rb +95 -0
  70. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +146 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +949 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +141 -0
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +557 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +267 -0
  129. data/lib/active_record/core.rb +599 -0
  130. data/lib/active_record/counter_cache.rb +193 -0
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +122 -0
  137. data/lib/active_record/enum.rb +274 -0
  138. data/lib/active_record/errors.rb +388 -0
  139. data/lib/active_record/explain.rb +50 -0
  140. data/lib/active_record/explain_registry.rb +32 -0
  141. data/lib/active_record/explain_subscriber.rb +34 -0
  142. data/lib/active_record/fixture_set/file.rb +82 -0
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +738 -0
  148. data/lib/active_record/gem_version.rb +17 -0
  149. data/lib/active_record/inheritance.rb +293 -0
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +207 -0
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  154. data/lib/active_record/locale/en.yml +48 -0
  155. data/lib/active_record/locking/optimistic.rb +197 -0
  156. data/lib/active_record/locking/pessimistic.rb +89 -0
  157. data/lib/active_record/log_subscriber.rb +118 -0
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +1397 -0
  162. data/lib/active_record/migration/command_recorder.rb +284 -0
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +17 -0
  165. data/lib/active_record/model_schema.rb +542 -0
  166. data/lib/active_record/nested_attributes.rb +600 -0
  167. data/lib/active_record/no_touching.rb +65 -0
  168. data/lib/active_record/null_relation.rb +68 -0
  169. data/lib/active_record/persistence.rb +967 -0
  170. data/lib/active_record/query_cache.rb +52 -0
  171. data/lib/active_record/querying.rb +82 -0
  172. data/lib/active_record/railtie.rb +263 -0
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +7 -0
  175. data/lib/active_record/railties/controller_runtime.rb +51 -0
  176. data/lib/active_record/railties/databases.rake +527 -0
  177. data/lib/active_record/readonly_attributes.rb +24 -0
  178. data/lib/active_record/reflection.rb +1042 -0
  179. data/lib/active_record/relation.rb +859 -0
  180. data/lib/active_record/relation/batches.rb +290 -0
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +424 -0
  183. data/lib/active_record/relation/delegation.rb +130 -0
  184. data/lib/active_record/relation/finder_methods.rb +552 -0
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +184 -0
  187. data/lib/active_record/relation/predicate_builder.rb +150 -0
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +1359 -0
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +77 -0
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +168 -0
  202. data/lib/active_record/runtime_registry.rb +24 -0
  203. data/lib/active_record/sanitization.rb +214 -0
  204. data/lib/active_record/schema.rb +61 -0
  205. data/lib/active_record/schema_dumper.rb +270 -0
  206. data/lib/active_record/schema_migration.rb +60 -0
  207. data/lib/active_record/scoping.rb +106 -0
  208. data/lib/active_record/scoping/default.rb +151 -0
  209. data/lib/active_record/scoping/named.rb +217 -0
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +22 -0
  212. data/lib/active_record/statement_cache.rb +148 -0
  213. data/lib/active_record/store.rb +290 -0
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +506 -0
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +167 -0
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +493 -0
  225. data/lib/active_record/translation.rb +24 -0
  226. data/lib/active_record/type.rb +78 -0
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +9 -0
  229. data/lib/active_record/type/date_time.rb +9 -0
  230. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  231. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +71 -0
  235. data/lib/active_record/type/text.rb +11 -0
  236. data/lib/active_record/type/time.rb +21 -0
  237. data/lib/active_record/type/type_map.rb +62 -0
  238. data/lib/active_record/type/unsigned_integer.rb +17 -0
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +94 -0
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +60 -0
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +68 -0
  247. data/lib/active_record/validations/uniqueness.rb +226 -0
  248. data/lib/active_record/version.rb +10 -0
  249. data/lib/arel.rb +51 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +19 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +48 -0
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +75 -0
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  337. data/lib/rails/generators/active_record/model/model_generator.rb +49 -0
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  340. metadata +415 -0
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # :stopdoc:
5
+ module ConnectionAdapters
6
+ module PostgreSQL
7
+ class TypeMetadata < DelegateClass(SqlTypeMetadata)
8
+ undef to_yaml if method_defined?(:to_yaml)
9
+
10
+ attr_reader :oid, :fmod
11
+
12
+ def initialize(type_metadata, oid: nil, fmod: nil)
13
+ super(type_metadata)
14
+ @oid = oid
15
+ @fmod = fmod
16
+ end
17
+
18
+ def ==(other)
19
+ other.is_a?(TypeMetadata) &&
20
+ __getobj__ == other.__getobj__ &&
21
+ oid == other.oid &&
22
+ fmod == other.fmod
23
+ end
24
+ alias eql? ==
25
+
26
+ def hash
27
+ TypeMetadata.hash ^
28
+ __getobj__.hash ^
29
+ oid.hash ^
30
+ fmod.hash
31
+ end
32
+ end
33
+ end
34
+ PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
35
+ end
36
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ # Value Object to hold a schema qualified name.
7
+ # This is usually the name of a PostgreSQL relation but it can also represent
8
+ # schema qualified type names. +schema+ and +identifier+ are unquoted to prevent
9
+ # double quoting.
10
+ class Name # :nodoc:
11
+ SEPARATOR = "."
12
+ attr_reader :schema, :identifier
13
+
14
+ def initialize(schema, identifier)
15
+ @schema, @identifier = unquote(schema), unquote(identifier)
16
+ end
17
+
18
+ def to_s
19
+ parts.join SEPARATOR
20
+ end
21
+
22
+ def quoted
23
+ if schema
24
+ PG::Connection.quote_ident(schema) << SEPARATOR << PG::Connection.quote_ident(identifier)
25
+ else
26
+ PG::Connection.quote_ident(identifier)
27
+ end
28
+ end
29
+
30
+ def ==(o)
31
+ o.class == self.class && o.parts == parts
32
+ end
33
+ alias_method :eql?, :==
34
+
35
+ def hash
36
+ parts.hash
37
+ end
38
+
39
+ protected
40
+
41
+ def parts
42
+ @parts ||= [@schema, @identifier].compact
43
+ end
44
+
45
+ private
46
+ def unquote(part)
47
+ if part && part.start_with?('"')
48
+ part[1..-2]
49
+ else
50
+ part
51
+ end
52
+ end
53
+ end
54
+
55
+ module Utils # :nodoc:
56
+ extend self
57
+
58
+ # Returns an instance of <tt>ActiveRecord::ConnectionAdapters::PostgreSQL::Name</tt>
59
+ # extracted from +string+.
60
+ # +schema+ is +nil+ if not specified in +string+.
61
+ # +schema+ and +identifier+ exclude surrounding quotes (regardless of whether provided in +string+)
62
+ # +string+ supports the range of schema/table references understood by PostgreSQL, for example:
63
+ #
64
+ # * <tt>table_name</tt>
65
+ # * <tt>"table.name"</tt>
66
+ # * <tt>schema_name.table_name</tt>
67
+ # * <tt>schema_name."table.name"</tt>
68
+ # * <tt>"schema_name".table_name</tt>
69
+ # * <tt>"schema.name"."table name"</tt>
70
+ def extract_schema_qualified_name(string)
71
+ schema, table = string.scan(/[^".]+|"[^"]*"/)
72
+ if table.nil?
73
+ table = schema
74
+ schema = nil
75
+ end
76
+ PostgreSQL::Name.new(schema, table)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,949 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
4
+ gem "pg", ">= 0.18", "< 2.0"
5
+ require "pg"
6
+
7
+ # Use async_exec instead of exec_params on pg versions before 1.1
8
+ class ::PG::Connection # :nodoc:
9
+ unless self.public_method_defined?(:async_exec_params)
10
+ remove_method :exec_params
11
+ alias exec_params async_exec
12
+ end
13
+ end
14
+
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"
29
+
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
35
+
36
+ conn_params.delete_if { |_, v| v.nil? }
37
+
38
+ # Map ActiveRecords param names to PGs.
39
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
40
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
41
+
42
+ # Forward only valid config params to PG::Connection.connect.
43
+ valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
44
+ conn_params.slice!(*valid_conn_param_keys)
45
+
46
+ conn = PG.connect(conn_params)
47
+ ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
48
+ rescue ::PG::Error => error
49
+ if error.message.include?(conn_params[:dbname])
50
+ raise ActiveRecord::NoDatabaseError
51
+ else
52
+ raise
53
+ end
54
+ end
55
+ end
56
+
57
+ module ConnectionAdapters
58
+ # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
59
+ #
60
+ # Options:
61
+ #
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.
64
+ # * <tt>:port</tt> - Defaults to 5432.
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.
68
+ # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
69
+ # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
70
+ # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
71
+ # <encoding></tt> call on the connection.
72
+ # * <tt>:min_messages</tt> - An optional client min messages that is used in a
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 .
85
+ class PostgreSQLAdapter < AbstractAdapter
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
100
+
101
+ NATIVE_DATABASE_TYPES = {
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" },
142
+ }
143
+
144
+ OID = PostgreSQL::OID #:nodoc:
145
+
146
+ include PostgreSQL::Quoting
147
+ include PostgreSQL::ReferentialIntegrity
148
+ include PostgreSQL::SchemaStatements
149
+ include PostgreSQL::DatabaseStatements
150
+
151
+ def supports_bulk_alter?
152
+ true
153
+ end
154
+
155
+ def supports_index_sort_order?
156
+ true
157
+ end
158
+
159
+ def supports_partial_index?
160
+ true
161
+ end
162
+
163
+ def supports_expression_index?
164
+ true
165
+ end
166
+
167
+ def supports_transaction_isolation?
168
+ true
169
+ end
170
+
171
+ def supports_foreign_keys?
172
+ true
173
+ end
174
+
175
+ def supports_validate_constraints?
176
+ true
177
+ end
178
+
179
+ def supports_views?
180
+ true
181
+ end
182
+
183
+ def supports_datetime_with_precision?
184
+ true
185
+ end
186
+
187
+ def supports_json?
188
+ true
189
+ end
190
+
191
+ def supports_comments?
192
+ true
193
+ end
194
+
195
+ def supports_savepoints?
196
+ true
197
+ end
198
+
199
+ def supports_insert_returning?
200
+ true
201
+ end
202
+
203
+ def supports_insert_on_conflict?
204
+ database_version >= 90500
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?
209
+
210
+ def index_algorithms
211
+ { concurrently: "CONCURRENTLY" }
212
+ end
213
+
214
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
215
+ def initialize(connection, max)
216
+ super(max)
217
+ @connection = connection
218
+ @counter = 0
219
+ end
220
+
221
+ def next_key
222
+ "a#{@counter + 1}"
223
+ end
224
+
225
+ def []=(sql, key)
226
+ super.tap { @counter += 1 }
227
+ end
228
+
229
+ private
230
+ def dealloc(key)
231
+ @connection.query "DEALLOCATE #{key}" if connection_active?
232
+ rescue PG::Error
233
+ end
234
+
235
+ def connection_active?
236
+ @connection.status == PG::CONNECTION_OK
237
+ rescue PG::Error
238
+ false
239
+ end
240
+ end
241
+
242
+ # Initializes and connects a PostgreSQL adapter.
243
+ def initialize(connection, logger, connection_parameters, config)
244
+ super(connection, logger, config)
245
+
246
+ @connection_parameters = connection_parameters
247
+
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
251
+
252
+ configure_connection
253
+ add_pg_encoders
254
+ add_pg_decoders
255
+
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
260
+ end
261
+
262
+ def self.database_exists?(config)
263
+ !!ActiveRecord::Base.postgresql_connection(config)
264
+ rescue ActiveRecord::NoDatabaseError
265
+ false
266
+ end
267
+
268
+ # Is this connection alive and ready for queries?
269
+ def active?
270
+ @lock.synchronize do
271
+ @connection.query "SELECT 1"
272
+ end
273
+ true
274
+ rescue PG::Error
275
+ false
276
+ end
277
+
278
+ # Close then reopen the connection.
279
+ def reconnect!
280
+ @lock.synchronize do
281
+ super
282
+ @connection.reset
283
+ configure_connection
284
+ rescue PG::ConnectionBad
285
+ connect
286
+ end
287
+ end
288
+
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"
295
+ end
296
+ @connection.query "DISCARD ALL"
297
+ configure_connection
298
+ end
299
+ end
300
+
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
307
+ end
308
+ end
309
+
310
+ def discard! # :nodoc:
311
+ super
312
+ @connection.socket_io.reopen(IO::NULL) rescue nil
313
+ @connection = nil
314
+ end
315
+
316
+ def native_database_types #:nodoc:
317
+ NATIVE_DATABASE_TYPES
318
+ end
319
+
320
+ def set_standard_conforming_strings
321
+ execute("SET standard_conforming_strings = on", "SCHEMA")
322
+ end
323
+
324
+ def supports_ddl_transactions?
325
+ true
326
+ end
327
+
328
+ def supports_advisory_locks?
329
+ true
330
+ end
331
+
332
+ def supports_explain?
333
+ true
334
+ end
335
+
336
+ def supports_extensions?
337
+ true
338
+ end
339
+
340
+ def supports_ranges?
341
+ true
342
+ end
343
+ deprecate :supports_ranges?
344
+
345
+ def supports_materialized_views?
346
+ true
347
+ end
348
+
349
+ def supports_foreign_tables?
350
+ true
351
+ end
352
+
353
+ def supports_pgcrypto_uuid?
354
+ database_version >= 90400
355
+ end
356
+
357
+ def supports_optimizer_hints?
358
+ unless defined?(@has_pg_hint_plan)
359
+ @has_pg_hint_plan = extension_available?("pg_hint_plan")
360
+ end
361
+ @has_pg_hint_plan
362
+ end
363
+
364
+ def supports_lazy_transactions?
365
+ true
366
+ end
367
+
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})")
373
+ end
374
+
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})")
380
+ end
381
+
382
+ def enable_extension(name)
383
+ exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
384
+ reload_type_map
385
+ }
386
+ end
387
+
388
+ def disable_extension(name)
389
+ exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
390
+ reload_type_map
391
+ }
392
+ end
393
+
394
+ def extension_available?(name)
395
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
396
+ end
397
+
398
+ def extension_enabled?(name)
399
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
400
+ end
401
+
402
+ def extensions
403
+ exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
404
+ end
405
+
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
409
+ end
410
+
411
+ # Set the authorized user for this session
412
+ def session_auth=(user)
413
+ clear_cache!
414
+ execute("SET SESSION AUTHORIZATION #{user}")
415
+ end
416
+
417
+ def use_insert_returning?
418
+ @use_insert_returning
419
+ end
420
+
421
+ def column_name_for_operation(operation, node) # :nodoc:
422
+ OPERATION_ALIASES.fetch(operation) { operation.downcase }
423
+ end
424
+
425
+ OPERATION_ALIASES = { # :nodoc:
426
+ "maximum" => "max",
427
+ "minimum" => "min",
428
+ "average" => "avg",
429
+ }
430
+
431
+ # Returns the version of the connected PostgreSQL server.
432
+ def get_database_version # :nodoc:
433
+ @connection.server_version
434
+ end
435
+ alias :postgresql_version :database_version
436
+
437
+ def default_index_type?(index) # :nodoc:
438
+ index.using == :btree || super
439
+ end
440
+
441
+ def build_insert_sql(insert) # :nodoc:
442
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
443
+
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(",")
449
+ end
450
+
451
+ sql << " RETURNING #{insert.returning}" if insert.returning
452
+ sql
453
+ end
454
+
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."
458
+ end
459
+ end
460
+
461
+ private
462
+
463
+ # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
464
+ VALUE_LIMIT_VIOLATION = "22001"
465
+ NUMERIC_VALUE_OUT_OF_RANGE = "22003"
466
+ NOT_NULL_VIOLATION = "23502"
467
+ FOREIGN_KEY_VIOLATION = "23503"
468
+ UNIQUE_VIOLATION = "23505"
469
+ SERIALIZATION_FAILURE = "40001"
470
+ DEADLOCK_DETECTED = "40P01"
471
+ LOCK_NOT_AVAILABLE = "55P03"
472
+ QUERY_CANCELED = "57014"
473
+
474
+ def translate_exception(exception, message:, sql:, binds:)
475
+ return exception unless exception.respond_to?(:result)
476
+
477
+ case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
478
+ when UNIQUE_VIOLATION
479
+ RecordNotUnique.new(message, sql: sql, binds: binds)
480
+ when FOREIGN_KEY_VIOLATION
481
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
482
+ when VALUE_LIMIT_VIOLATION
483
+ ValueTooLong.new(message, sql: sql, binds: binds)
484
+ when NUMERIC_VALUE_OUT_OF_RANGE
485
+ RangeError.new(message, sql: sql, binds: binds)
486
+ when NOT_NULL_VIOLATION
487
+ NotNullViolation.new(message, sql: sql, binds: binds)
488
+ when SERIALIZATION_FAILURE
489
+ SerializationFailure.new(message, sql: sql, binds: binds)
490
+ when DEADLOCK_DETECTED
491
+ Deadlocked.new(message, sql: sql, binds: binds)
492
+ when LOCK_NOT_AVAILABLE
493
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
494
+ when QUERY_CANCELED
495
+ QueryCanceled.new(message, sql: sql, binds: binds)
496
+ else
497
+ super
498
+ end
499
+ end
500
+
501
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
502
+ if !type_map.key?(oid)
503
+ load_additional_types([oid])
504
+ end
505
+
506
+ type_map.fetch(oid, fmod, sql_type) {
507
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
508
+ Type.default_value.tap do |cast_type|
509
+ type_map.register_type(oid, cast_type)
510
+ end
511
+ }
512
+ end
513
+
514
+ def initialize_type_map(m = type_map)
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
557
+
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
580
+
581
+ load_additional_types
582
+ end
583
+
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
610
+
611
+ def extract_default_function(default_value, default)
612
+ default if has_default_function?(default_value, default)
613
+ end
614
+
615
+ def has_default_function?(default_value, default)
616
+ !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
617
+ end
618
+
619
+ def load_additional_types(oids = nil)
620
+ initializer = OID::TypeMapInitializer.new(type_map)
621
+
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
627
+
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
633
+
634
+ execute_and_clear(query, "SCHEMA", []) do |records|
635
+ initializer.run(records)
636
+ end
637
+ end
638
+
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}"
644
+ end
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)
652
+ end
653
+ ret = yield result
654
+ result.clear
655
+ ret
656
+ end
657
+
658
+ def exec_no_cache(sql, name, binds)
659
+ materialize_transactions
660
+
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
664
+
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
672
+
673
+ def exec_cache(sql, name, binds)
674
+ materialize_transactions
675
+ update_typemap_for_default_timezone
676
+
677
+ stmt_key = prepare_statement(sql, binds)
678
+ type_casted_binds = type_casted_binds(binds)
679
+
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)
687
+
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)
692
+ else
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
698
+ end
699
+ end
700
+
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
717
+ end
718
+
719
+ def in_transaction?
720
+ open_transactions > 0
721
+ end
722
+
723
+ # Returns the statement identifier for the client side cache
724
+ # of statements
725
+ def sql_key(sql)
726
+ "#{schema_search_path}-#{sql}"
727
+ end
728
+
729
+ # Prepare the statement if it hasn't been prepared, return
730
+ # the statement key.
731
+ def prepare_statement(sql, binds)
732
+ @lock.synchronize do
733
+ sql_key = sql_key(sql)
734
+ unless @statements.key? sql_key
735
+ nextkey = @statements.next_key
736
+ begin
737
+ @connection.prepare nextkey, sql
738
+ rescue => e
739
+ raise translate_exception_class(e, sql, binds)
740
+ end
741
+ # Clear the queue
742
+ @connection.get_last_result
743
+ @statements[sql_key] = nextkey
744
+ end
745
+ @statements[sql_key]
746
+ end
747
+ end
748
+
749
+ # Connects to a PostgreSQL server and sets up the adapter depending on the
750
+ # connected server's characteristics.
751
+ def connect
752
+ @connection = PG.connect(@connection_parameters)
753
+ configure_connection
754
+ add_pg_encoders
755
+ add_pg_decoders
756
+ end
757
+
758
+ # Configures the encoding, verbosity, schema search path, and time zone of the connection.
759
+ # This is called by #connect and should not be called manually.
760
+ def configure_connection
761
+ if @config[:encoding]
762
+ @connection.set_client_encoding(@config[:encoding])
763
+ end
764
+ self.client_min_messages = @config[:min_messages] || "warning"
765
+ self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
766
+
767
+ # Use standard-conforming strings so we don't have to do the E'...' dance.
768
+ set_standard_conforming_strings
769
+
770
+ variables = @config.fetch(:variables, {}).stringify_keys
771
+
772
+ # If using Active Record's time zone support configure the connection to return
773
+ # TIMESTAMP WITH ZONE types in UTC.
774
+ unless variables["timezone"]
775
+ if ActiveRecord::Base.default_timezone == :utc
776
+ variables["timezone"] = "UTC"
777
+ elsif @local_tz
778
+ variables["timezone"] = @local_tz
779
+ end
780
+ end
781
+
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
792
+ end
793
+
794
+ # Returns the list of a table's column names, data types, and default values.
795
+ #
796
+ # The underlying query is roughly:
797
+ # SELECT column.name, column.type, default.value, column.comment
798
+ # FROM column LEFT JOIN default
799
+ # ON column.table_id = default.table_id
800
+ # AND column.num = default.column_num
801
+ # WHERE column.table_id = get_table_id('table_name')
802
+ # AND column.num > 0
803
+ # AND NOT column.is_dropped
804
+ # ORDER BY column.num
805
+ #
806
+ # If the table name is not prefixed with a schema, the database will
807
+ # take the first match from the schema search path.
808
+ #
809
+ # Query implementation notes:
810
+ # - format_type includes the column size constraint, e.g. varchar(50)
811
+ # - ::regclass is a function that gives the id for a table name
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
825
+ end
826
+
827
+ def extract_table_ref_from_insert_sql(sql)
828
+ sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
829
+ $1.strip if $1
830
+ end
831
+
832
+ 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
859
+ end
860
+ end
861
+
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
868
+ end
869
+
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
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)
947
+ end
948
+ end
949
+ end