activerecord 4.2.11.1 → 6.0.3.5

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