activerecord 6.0.1

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

Potentially problematic release.


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

Files changed (340) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1086 -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 +49 -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 +340 -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 +262 -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 +512 -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 +1175 -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 +516 -0
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +155 -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 +772 -0
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +830 -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 +202 -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 +184 -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 +953 -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 +120 -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 +561 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +274 -0
  129. data/lib/active_record/core.rb +603 -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 +88 -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 +545 -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 +860 -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 +561 -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 +1371 -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 +58 -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 +418 -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,953 @@
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_common_table_expressions?
365
+ true
366
+ end
367
+
368
+ def supports_lazy_transactions?
369
+ true
370
+ end
371
+
372
+ def get_advisory_lock(lock_id) # :nodoc:
373
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
374
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
375
+ end
376
+ query_value("SELECT pg_try_advisory_lock(#{lock_id})")
377
+ end
378
+
379
+ def release_advisory_lock(lock_id) # :nodoc:
380
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
381
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
382
+ end
383
+ query_value("SELECT pg_advisory_unlock(#{lock_id})")
384
+ end
385
+
386
+ def enable_extension(name)
387
+ exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
388
+ reload_type_map
389
+ }
390
+ end
391
+
392
+ def disable_extension(name)
393
+ exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
394
+ reload_type_map
395
+ }
396
+ end
397
+
398
+ def extension_available?(name)
399
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
400
+ end
401
+
402
+ def extension_enabled?(name)
403
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
404
+ end
405
+
406
+ def extensions
407
+ exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
408
+ end
409
+
410
+ # Returns the configured supported identifier length supported by PostgreSQL
411
+ def max_identifier_length
412
+ @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
413
+ end
414
+
415
+ # Set the authorized user for this session
416
+ def session_auth=(user)
417
+ clear_cache!
418
+ execute("SET SESSION AUTHORIZATION #{user}")
419
+ end
420
+
421
+ def use_insert_returning?
422
+ @use_insert_returning
423
+ end
424
+
425
+ def column_name_for_operation(operation, node) # :nodoc:
426
+ OPERATION_ALIASES.fetch(operation) { operation.downcase }
427
+ end
428
+
429
+ OPERATION_ALIASES = { # :nodoc:
430
+ "maximum" => "max",
431
+ "minimum" => "min",
432
+ "average" => "avg",
433
+ }
434
+
435
+ # Returns the version of the connected PostgreSQL server.
436
+ def get_database_version # :nodoc:
437
+ @connection.server_version
438
+ end
439
+ alias :postgresql_version :database_version
440
+
441
+ def default_index_type?(index) # :nodoc:
442
+ index.using == :btree || super
443
+ end
444
+
445
+ def build_insert_sql(insert) # :nodoc:
446
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
447
+
448
+ if insert.skip_duplicates?
449
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
450
+ elsif insert.update_duplicates?
451
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
452
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
453
+ end
454
+
455
+ sql << " RETURNING #{insert.returning}" if insert.returning
456
+ sql
457
+ end
458
+
459
+ def check_version # :nodoc:
460
+ if database_version < 90300
461
+ raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
462
+ end
463
+ end
464
+
465
+ private
466
+
467
+ # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
468
+ VALUE_LIMIT_VIOLATION = "22001"
469
+ NUMERIC_VALUE_OUT_OF_RANGE = "22003"
470
+ NOT_NULL_VIOLATION = "23502"
471
+ FOREIGN_KEY_VIOLATION = "23503"
472
+ UNIQUE_VIOLATION = "23505"
473
+ SERIALIZATION_FAILURE = "40001"
474
+ DEADLOCK_DETECTED = "40P01"
475
+ LOCK_NOT_AVAILABLE = "55P03"
476
+ QUERY_CANCELED = "57014"
477
+
478
+ def translate_exception(exception, message:, sql:, binds:)
479
+ return exception unless exception.respond_to?(:result)
480
+
481
+ case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
482
+ when UNIQUE_VIOLATION
483
+ RecordNotUnique.new(message, sql: sql, binds: binds)
484
+ when FOREIGN_KEY_VIOLATION
485
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
486
+ when VALUE_LIMIT_VIOLATION
487
+ ValueTooLong.new(message, sql: sql, binds: binds)
488
+ when NUMERIC_VALUE_OUT_OF_RANGE
489
+ RangeError.new(message, sql: sql, binds: binds)
490
+ when NOT_NULL_VIOLATION
491
+ NotNullViolation.new(message, sql: sql, binds: binds)
492
+ when SERIALIZATION_FAILURE
493
+ SerializationFailure.new(message, sql: sql, binds: binds)
494
+ when DEADLOCK_DETECTED
495
+ Deadlocked.new(message, sql: sql, binds: binds)
496
+ when LOCK_NOT_AVAILABLE
497
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
498
+ when QUERY_CANCELED
499
+ QueryCanceled.new(message, sql: sql, binds: binds)
500
+ else
501
+ super
502
+ end
503
+ end
504
+
505
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
506
+ if !type_map.key?(oid)
507
+ load_additional_types([oid])
508
+ end
509
+
510
+ type_map.fetch(oid, fmod, sql_type) {
511
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
512
+ Type.default_value.tap do |cast_type|
513
+ type_map.register_type(oid, cast_type)
514
+ end
515
+ }
516
+ end
517
+
518
+ def initialize_type_map(m = type_map)
519
+ m.register_type "int2", Type::Integer.new(limit: 2)
520
+ m.register_type "int4", Type::Integer.new(limit: 4)
521
+ m.register_type "int8", Type::Integer.new(limit: 8)
522
+ m.register_type "oid", OID::Oid.new
523
+ m.register_type "float4", Type::Float.new
524
+ m.alias_type "float8", "float4"
525
+ m.register_type "text", Type::Text.new
526
+ register_class_with_limit m, "varchar", Type::String
527
+ m.alias_type "char", "varchar"
528
+ m.alias_type "name", "varchar"
529
+ m.alias_type "bpchar", "varchar"
530
+ m.register_type "bool", Type::Boolean.new
531
+ register_class_with_limit m, "bit", OID::Bit
532
+ register_class_with_limit m, "varbit", OID::BitVarying
533
+ m.alias_type "timestamptz", "timestamp"
534
+ m.register_type "date", OID::Date.new
535
+
536
+ m.register_type "money", OID::Money.new
537
+ m.register_type "bytea", OID::Bytea.new
538
+ m.register_type "point", OID::Point.new
539
+ m.register_type "hstore", OID::Hstore.new
540
+ m.register_type "json", Type::Json.new
541
+ m.register_type "jsonb", OID::Jsonb.new
542
+ m.register_type "cidr", OID::Cidr.new
543
+ m.register_type "inet", OID::Inet.new
544
+ m.register_type "uuid", OID::Uuid.new
545
+ m.register_type "xml", OID::Xml.new
546
+ m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
547
+ m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
548
+ m.register_type "citext", OID::SpecializedString.new(:citext)
549
+ m.register_type "ltree", OID::SpecializedString.new(:ltree)
550
+ m.register_type "line", OID::SpecializedString.new(:line)
551
+ m.register_type "lseg", OID::SpecializedString.new(:lseg)
552
+ m.register_type "box", OID::SpecializedString.new(:box)
553
+ m.register_type "path", OID::SpecializedString.new(:path)
554
+ m.register_type "polygon", OID::SpecializedString.new(:polygon)
555
+ m.register_type "circle", OID::SpecializedString.new(:circle)
556
+
557
+ m.register_type "interval" do |_, _, sql_type|
558
+ precision = extract_precision(sql_type)
559
+ OID::SpecializedString.new(:interval, precision: precision)
560
+ end
561
+
562
+ register_class_with_precision m, "time", Type::Time
563
+ register_class_with_precision m, "timestamp", OID::DateTime
564
+
565
+ m.register_type "numeric" do |_, fmod, sql_type|
566
+ precision = extract_precision(sql_type)
567
+ scale = extract_scale(sql_type)
568
+
569
+ # The type for the numeric depends on the width of the field,
570
+ # so we'll do something special here.
571
+ #
572
+ # When dealing with decimal columns:
573
+ #
574
+ # places after decimal = fmod - 4 & 0xffff
575
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
576
+ if fmod && (fmod - 4 & 0xffff).zero?
577
+ # FIXME: Remove this class, and the second argument to
578
+ # lookups on PG
579
+ Type::DecimalWithoutScale.new(precision: precision)
580
+ else
581
+ OID::Decimal.new(precision: precision, scale: scale)
582
+ end
583
+ end
584
+
585
+ load_additional_types
586
+ end
587
+
588
+ # Extracts the value from a PostgreSQL column default definition.
589
+ def extract_value_from_default(default)
590
+ case default
591
+ # Quoted types
592
+ when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
593
+ # The default 'now'::date is CURRENT_DATE
594
+ if $1 == "now" && $2 == "date"
595
+ nil
596
+ else
597
+ $1.gsub("''", "'")
598
+ end
599
+ # Boolean types
600
+ when "true", "false"
601
+ default
602
+ # Numeric types
603
+ when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
604
+ $1
605
+ # Object identifier types
606
+ when /\A-?\d+\z/
607
+ $1
608
+ else
609
+ # Anything else is blank, some user type, or some function
610
+ # and we can't know the value of that, so return nil.
611
+ nil
612
+ end
613
+ end
614
+
615
+ def extract_default_function(default_value, default)
616
+ default if has_default_function?(default_value, default)
617
+ end
618
+
619
+ def has_default_function?(default_value, default)
620
+ !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
621
+ end
622
+
623
+ def load_additional_types(oids = nil)
624
+ initializer = OID::TypeMapInitializer.new(type_map)
625
+
626
+ query = <<~SQL
627
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
628
+ FROM pg_type as t
629
+ LEFT JOIN pg_range as r ON oid = rngtypid
630
+ SQL
631
+
632
+ if oids
633
+ query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
634
+ else
635
+ query += initializer.query_conditions_for_initial_load
636
+ end
637
+
638
+ execute_and_clear(query, "SCHEMA", []) do |records|
639
+ initializer.run(records)
640
+ end
641
+ end
642
+
643
+ FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
644
+
645
+ def execute_and_clear(sql, name, binds, prepare: false)
646
+ if preventing_writes? && write_query?(sql)
647
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
648
+ end
649
+
650
+ if without_prepared_statement?(binds)
651
+ result = exec_no_cache(sql, name, [])
652
+ elsif !prepare
653
+ result = exec_no_cache(sql, name, binds)
654
+ else
655
+ result = exec_cache(sql, name, binds)
656
+ end
657
+ ret = yield result
658
+ result.clear
659
+ ret
660
+ end
661
+
662
+ def exec_no_cache(sql, name, binds)
663
+ materialize_transactions
664
+
665
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
666
+ # made since we established the connection
667
+ update_typemap_for_default_timezone
668
+
669
+ type_casted_binds = type_casted_binds(binds)
670
+ log(sql, name, binds, type_casted_binds) do
671
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
672
+ @connection.exec_params(sql, type_casted_binds)
673
+ end
674
+ end
675
+ end
676
+
677
+ def exec_cache(sql, name, binds)
678
+ materialize_transactions
679
+ update_typemap_for_default_timezone
680
+
681
+ stmt_key = prepare_statement(sql, binds)
682
+ type_casted_binds = type_casted_binds(binds)
683
+
684
+ log(sql, name, binds, type_casted_binds, stmt_key) do
685
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
686
+ @connection.exec_prepared(stmt_key, type_casted_binds)
687
+ end
688
+ end
689
+ rescue ActiveRecord::StatementInvalid => e
690
+ raise unless is_cached_plan_failure?(e)
691
+
692
+ # Nothing we can do if we are in a transaction because all commands
693
+ # will raise InFailedSQLTransaction
694
+ if in_transaction?
695
+ raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
696
+ else
697
+ @lock.synchronize do
698
+ # outside of transactions we can simply flush this query and retry
699
+ @statements.delete sql_key(sql)
700
+ end
701
+ retry
702
+ end
703
+ end
704
+
705
+ # Annoyingly, the code for prepared statements whose return value may
706
+ # have changed is FEATURE_NOT_SUPPORTED.
707
+ #
708
+ # This covers various different error types so we need to do additional
709
+ # work to classify the exception definitively as a
710
+ # ActiveRecord::PreparedStatementCacheExpired
711
+ #
712
+ # Check here for more details:
713
+ # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
714
+ CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
715
+ def is_cached_plan_failure?(e)
716
+ pgerror = e.cause
717
+ code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
718
+ code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
719
+ rescue
720
+ false
721
+ end
722
+
723
+ def in_transaction?
724
+ open_transactions > 0
725
+ end
726
+
727
+ # Returns the statement identifier for the client side cache
728
+ # of statements
729
+ def sql_key(sql)
730
+ "#{schema_search_path}-#{sql}"
731
+ end
732
+
733
+ # Prepare the statement if it hasn't been prepared, return
734
+ # the statement key.
735
+ def prepare_statement(sql, binds)
736
+ @lock.synchronize do
737
+ sql_key = sql_key(sql)
738
+ unless @statements.key? sql_key
739
+ nextkey = @statements.next_key
740
+ begin
741
+ @connection.prepare nextkey, sql
742
+ rescue => e
743
+ raise translate_exception_class(e, sql, binds)
744
+ end
745
+ # Clear the queue
746
+ @connection.get_last_result
747
+ @statements[sql_key] = nextkey
748
+ end
749
+ @statements[sql_key]
750
+ end
751
+ end
752
+
753
+ # Connects to a PostgreSQL server and sets up the adapter depending on the
754
+ # connected server's characteristics.
755
+ def connect
756
+ @connection = PG.connect(@connection_parameters)
757
+ configure_connection
758
+ add_pg_encoders
759
+ add_pg_decoders
760
+ end
761
+
762
+ # Configures the encoding, verbosity, schema search path, and time zone of the connection.
763
+ # This is called by #connect and should not be called manually.
764
+ def configure_connection
765
+ if @config[:encoding]
766
+ @connection.set_client_encoding(@config[:encoding])
767
+ end
768
+ self.client_min_messages = @config[:min_messages] || "warning"
769
+ self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
770
+
771
+ # Use standard-conforming strings so we don't have to do the E'...' dance.
772
+ set_standard_conforming_strings
773
+
774
+ variables = @config.fetch(:variables, {}).stringify_keys
775
+
776
+ # If using Active Record's time zone support configure the connection to return
777
+ # TIMESTAMP WITH ZONE types in UTC.
778
+ unless variables["timezone"]
779
+ if ActiveRecord::Base.default_timezone == :utc
780
+ variables["timezone"] = "UTC"
781
+ elsif @local_tz
782
+ variables["timezone"] = @local_tz
783
+ end
784
+ end
785
+
786
+ # SET statements from :variables config hash
787
+ # https://www.postgresql.org/docs/current/static/sql-set.html
788
+ variables.map do |k, v|
789
+ if v == ":default" || v == :default
790
+ # Sets the value to the global or compile default
791
+ execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
792
+ elsif !v.nil?
793
+ execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
794
+ end
795
+ end
796
+ end
797
+
798
+ # Returns the list of a table's column names, data types, and default values.
799
+ #
800
+ # The underlying query is roughly:
801
+ # SELECT column.name, column.type, default.value, column.comment
802
+ # FROM column LEFT JOIN default
803
+ # ON column.table_id = default.table_id
804
+ # AND column.num = default.column_num
805
+ # WHERE column.table_id = get_table_id('table_name')
806
+ # AND column.num > 0
807
+ # AND NOT column.is_dropped
808
+ # ORDER BY column.num
809
+ #
810
+ # If the table name is not prefixed with a schema, the database will
811
+ # take the first match from the schema search path.
812
+ #
813
+ # Query implementation notes:
814
+ # - format_type includes the column size constraint, e.g. varchar(50)
815
+ # - ::regclass is a function that gives the id for a table name
816
+ def column_definitions(table_name)
817
+ query(<<~SQL, "SCHEMA")
818
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
819
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
820
+ c.collname, col_description(a.attrelid, a.attnum) AS comment
821
+ FROM pg_attribute a
822
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
823
+ LEFT JOIN pg_type t ON a.atttypid = t.oid
824
+ LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
825
+ WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
826
+ AND a.attnum > 0 AND NOT a.attisdropped
827
+ ORDER BY a.attnum
828
+ SQL
829
+ end
830
+
831
+ def extract_table_ref_from_insert_sql(sql)
832
+ sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
833
+ $1.strip if $1
834
+ end
835
+
836
+ def arel_visitor
837
+ Arel::Visitors::PostgreSQL.new(self)
838
+ end
839
+
840
+ def build_statement_pool
841
+ StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
842
+ end
843
+
844
+ def can_perform_case_insensitive_comparison_for?(column)
845
+ @case_insensitive_cache ||= {}
846
+ @case_insensitive_cache[column.sql_type] ||= begin
847
+ sql = <<~SQL
848
+ SELECT exists(
849
+ SELECT * FROM pg_proc
850
+ WHERE proname = 'lower'
851
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
852
+ ) OR exists(
853
+ SELECT * FROM pg_proc
854
+ INNER JOIN pg_cast
855
+ ON ARRAY[casttarget]::oidvector = proargtypes
856
+ WHERE proname = 'lower'
857
+ AND castsource = #{quote column.sql_type}::regtype
858
+ )
859
+ SQL
860
+ execute_and_clear(sql, "SCHEMA", []) do |result|
861
+ result.getvalue(0, 0)
862
+ end
863
+ end
864
+ end
865
+
866
+ def add_pg_encoders
867
+ map = PG::TypeMapByClass.new
868
+ map[Integer] = PG::TextEncoder::Integer.new
869
+ map[TrueClass] = PG::TextEncoder::Boolean.new
870
+ map[FalseClass] = PG::TextEncoder::Boolean.new
871
+ @connection.type_map_for_queries = map
872
+ end
873
+
874
+ def update_typemap_for_default_timezone
875
+ if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
876
+ decoder_class = ActiveRecord::Base.default_timezone == :utc ?
877
+ PG::TextDecoder::TimestampUtc :
878
+ PG::TextDecoder::TimestampWithoutTimeZone
879
+
880
+ @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
881
+ @connection.type_map_for_results.add_coder(@timestamp_decoder)
882
+ @default_timezone = ActiveRecord::Base.default_timezone
883
+ end
884
+ end
885
+
886
+ def add_pg_decoders
887
+ @default_timezone = nil
888
+ @timestamp_decoder = nil
889
+
890
+ coders_by_name = {
891
+ "int2" => PG::TextDecoder::Integer,
892
+ "int4" => PG::TextDecoder::Integer,
893
+ "int8" => PG::TextDecoder::Integer,
894
+ "oid" => PG::TextDecoder::Integer,
895
+ "float4" => PG::TextDecoder::Float,
896
+ "float8" => PG::TextDecoder::Float,
897
+ "bool" => PG::TextDecoder::Boolean,
898
+ }
899
+
900
+ if defined?(PG::TextDecoder::TimestampUtc)
901
+ # Use native PG encoders available since pg-1.1
902
+ coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
903
+ coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
904
+ end
905
+
906
+ known_coder_types = coders_by_name.keys.map { |n| quote(n) }
907
+ query = <<~SQL % known_coder_types.join(", ")
908
+ SELECT t.oid, t.typname
909
+ FROM pg_type as t
910
+ WHERE t.typname IN (%s)
911
+ SQL
912
+ coders = execute_and_clear(query, "SCHEMA", []) do |result|
913
+ result
914
+ .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
915
+ .compact
916
+ end
917
+
918
+ map = PG::TypeMapByOid.new
919
+ coders.each { |coder| map.add_coder(coder) }
920
+ @connection.type_map_for_results = map
921
+
922
+ # extract timestamp decoder for use in update_typemap_for_default_timezone
923
+ @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
924
+ update_typemap_for_default_timezone
925
+ end
926
+
927
+ def construct_coder(row, coder_class)
928
+ return unless coder_class
929
+ coder_class.new(oid: row["oid"].to_i, name: row["typname"])
930
+ end
931
+
932
+ ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
933
+ ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
934
+ ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
935
+ ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
936
+ ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
937
+ ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
938
+ ActiveRecord::Type.register(:date, OID::Date, adapter: :postgresql)
939
+ ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
940
+ ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
941
+ ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
942
+ ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
943
+ ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
944
+ ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
945
+ ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
946
+ ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
947
+ ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
948
+ ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
949
+ ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
950
+ ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
951
+ end
952
+ end
953
+ end