activerecord 5.2.8.1 → 6.1.6.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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1255 -596
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +9 -8
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +100 -41
  10. data/lib/active_record/associations/association_scope.rb +23 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +55 -48
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  13. data/lib/active_record/associations/builder/association.rb +45 -22
  14. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  15. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -2
  18. data/lib/active_record/associations/builder/has_one.rb +33 -2
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  20. data/lib/active_record/associations/collection_association.rb +44 -34
  21. data/lib/active_record/associations/collection_proxy.rb +25 -21
  22. data/lib/active_record/associations/foreign_association.rb +20 -0
  23. data/lib/active_record/associations/has_many_association.rb +26 -13
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  25. data/lib/active_record/associations/has_one_association.rb +43 -31
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  28. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/preloader/association.rb +69 -43
  31. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/singular_association.rb +3 -17
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/associations.rb +137 -25
  36. data/lib/active_record/attribute_assignment.rb +17 -19
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  38. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  40. data/lib/active_record/attribute_methods/query.rb +4 -8
  41. data/lib/active_record/attribute_methods/read.rb +14 -56
  42. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +18 -34
  45. data/lib/active_record/attribute_methods.rb +81 -143
  46. data/lib/active_record/attributes.rb +46 -9
  47. data/lib/active_record/autosave_association.rb +57 -42
  48. data/lib/active_record/base.rb +4 -17
  49. data/lib/active_record/callbacks.rb +158 -43
  50. data/lib/active_record/coders/yaml_column.rb +1 -2
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +211 -90
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  64. data/lib/active_record/connection_adapters/column.rb +30 -12
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +88 -32
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +142 -19
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -54
  81. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  99. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  101. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  102. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  104. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  105. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  106. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  107. data/lib/active_record/connection_adapters/schema_cache.rb +159 -21
  108. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  109. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
  110. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  111. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  112. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  113. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  114. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  115. data/lib/active_record/connection_adapters.rb +52 -0
  116. data/lib/active_record/connection_handling.rb +293 -33
  117. data/lib/active_record/core.rb +333 -98
  118. data/lib/active_record/counter_cache.rb +8 -30
  119. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  120. data/lib/active_record/database_configurations/database_config.rb +80 -0
  121. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  122. data/lib/active_record/database_configurations/url_config.rb +53 -0
  123. data/lib/active_record/database_configurations.rb +273 -0
  124. data/lib/active_record/delegated_type.rb +209 -0
  125. data/lib/active_record/destroy_association_async_job.rb +36 -0
  126. data/lib/active_record/dynamic_matchers.rb +3 -4
  127. data/lib/active_record/enum.rb +108 -36
  128. data/lib/active_record/errors.rb +62 -19
  129. data/lib/active_record/explain.rb +10 -6
  130. data/lib/active_record/explain_subscriber.rb +1 -1
  131. data/lib/active_record/fixture_set/file.rb +10 -17
  132. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  133. data/lib/active_record/fixture_set/render_context.rb +17 -0
  134. data/lib/active_record/fixture_set/table_row.rb +152 -0
  135. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  136. data/lib/active_record/fixtures.rb +200 -481
  137. data/lib/active_record/gem_version.rb +3 -3
  138. data/lib/active_record/inheritance.rb +53 -24
  139. data/lib/active_record/insert_all.rb +212 -0
  140. data/lib/active_record/integration.rb +67 -17
  141. data/lib/active_record/internal_metadata.rb +28 -9
  142. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  143. data/lib/active_record/locking/optimistic.rb +37 -23
  144. data/lib/active_record/locking/pessimistic.rb +9 -5
  145. data/lib/active_record/log_subscriber.rb +35 -35
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  148. data/lib/active_record/middleware/database_selector.rb +77 -0
  149. data/lib/active_record/migration/command_recorder.rb +96 -44
  150. data/lib/active_record/migration/compatibility.rb +145 -64
  151. data/lib/active_record/migration/join_table.rb +0 -1
  152. data/lib/active_record/migration.rb +206 -157
  153. data/lib/active_record/model_schema.rb +148 -22
  154. data/lib/active_record/nested_attributes.rb +4 -7
  155. data/lib/active_record/no_touching.rb +8 -1
  156. data/lib/active_record/null_relation.rb +0 -1
  157. data/lib/active_record/persistence.rb +267 -59
  158. data/lib/active_record/query_cache.rb +21 -4
  159. data/lib/active_record/querying.rb +40 -23
  160. data/lib/active_record/railtie.rb +116 -59
  161. data/lib/active_record/railties/console_sandbox.rb +2 -4
  162. data/lib/active_record/railties/controller_runtime.rb +30 -35
  163. data/lib/active_record/railties/databases.rake +411 -80
  164. data/lib/active_record/readonly_attributes.rb +4 -0
  165. data/lib/active_record/reflection.rb +109 -93
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  167. data/lib/active_record/relation/batches.rb +44 -35
  168. data/lib/active_record/relation/calculations.rb +157 -90
  169. data/lib/active_record/relation/delegation.rb +35 -50
  170. data/lib/active_record/relation/finder_methods.rb +64 -39
  171. data/lib/active_record/relation/from_clause.rb +5 -1
  172. data/lib/active_record/relation/merger.rb +32 -40
  173. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  174. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  179. data/lib/active_record/relation/predicate_builder.rb +62 -45
  180. data/lib/active_record/relation/query_attribute.rb +13 -8
  181. data/lib/active_record/relation/query_methods.rb +476 -187
  182. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  183. data/lib/active_record/relation/spawn_methods.rb +9 -9
  184. data/lib/active_record/relation/where_clause.rb +115 -62
  185. data/lib/active_record/relation.rb +379 -115
  186. data/lib/active_record/result.rb +64 -38
  187. data/lib/active_record/runtime_registry.rb +2 -2
  188. data/lib/active_record/sanitization.rb +22 -41
  189. data/lib/active_record/schema.rb +2 -11
  190. data/lib/active_record/schema_dumper.rb +54 -9
  191. data/lib/active_record/schema_migration.rb +7 -9
  192. data/lib/active_record/scoping/default.rb +4 -8
  193. data/lib/active_record/scoping/named.rb +17 -24
  194. data/lib/active_record/scoping.rb +8 -9
  195. data/lib/active_record/secure_token.rb +16 -8
  196. data/lib/active_record/serialization.rb +5 -3
  197. data/lib/active_record/signed_id.rb +116 -0
  198. data/lib/active_record/statement_cache.rb +49 -6
  199. data/lib/active_record/store.rb +88 -9
  200. data/lib/active_record/suppressor.rb +2 -2
  201. data/lib/active_record/table_metadata.rb +42 -43
  202. data/lib/active_record/tasks/database_tasks.rb +277 -81
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  206. data/lib/active_record/test_databases.rb +24 -0
  207. data/lib/active_record/test_fixtures.rb +287 -0
  208. data/lib/active_record/timestamp.rb +43 -32
  209. data/lib/active_record/touch_later.rb +23 -22
  210. data/lib/active_record/transactions.rb +62 -118
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  213. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  214. data/lib/active_record/type/serialized.rb +6 -3
  215. data/lib/active_record/type/time.rb +10 -0
  216. data/lib/active_record/type/type_map.rb +0 -1
  217. data/lib/active_record/type/unsigned_integer.rb +0 -1
  218. data/lib/active_record/type.rb +10 -5
  219. data/lib/active_record/type_caster/connection.rb +15 -15
  220. data/lib/active_record/type_caster/map.rb +8 -8
  221. data/lib/active_record/validations/associated.rb +1 -2
  222. data/lib/active_record/validations/numericality.rb +35 -0
  223. data/lib/active_record/validations/uniqueness.rb +38 -30
  224. data/lib/active_record/validations.rb +4 -3
  225. data/lib/active_record.rb +13 -12
  226. data/lib/arel/alias_predication.rb +9 -0
  227. data/lib/arel/attributes/attribute.rb +41 -0
  228. data/lib/arel/collectors/bind.rb +29 -0
  229. data/lib/arel/collectors/composite.rb +39 -0
  230. data/lib/arel/collectors/plain_string.rb +20 -0
  231. data/lib/arel/collectors/sql_string.rb +27 -0
  232. data/lib/arel/collectors/substitute_binds.rb +35 -0
  233. data/lib/arel/crud.rb +42 -0
  234. data/lib/arel/delete_manager.rb +18 -0
  235. data/lib/arel/errors.rb +9 -0
  236. data/lib/arel/expressions.rb +29 -0
  237. data/lib/arel/factory_methods.rb +49 -0
  238. data/lib/arel/insert_manager.rb +49 -0
  239. data/lib/arel/math.rb +45 -0
  240. data/lib/arel/nodes/and.rb +32 -0
  241. data/lib/arel/nodes/ascending.rb +23 -0
  242. data/lib/arel/nodes/binary.rb +126 -0
  243. data/lib/arel/nodes/bind_param.rb +44 -0
  244. data/lib/arel/nodes/case.rb +55 -0
  245. data/lib/arel/nodes/casted.rb +62 -0
  246. data/lib/arel/nodes/comment.rb +29 -0
  247. data/lib/arel/nodes/count.rb +12 -0
  248. data/lib/arel/nodes/delete_statement.rb +45 -0
  249. data/lib/arel/nodes/descending.rb +23 -0
  250. data/lib/arel/nodes/equality.rb +15 -0
  251. data/lib/arel/nodes/extract.rb +24 -0
  252. data/lib/arel/nodes/false.rb +16 -0
  253. data/lib/arel/nodes/full_outer_join.rb +8 -0
  254. data/lib/arel/nodes/function.rb +44 -0
  255. data/lib/arel/nodes/grouping.rb +11 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  257. data/lib/arel/nodes/in.rb +15 -0
  258. data/lib/arel/nodes/infix_operation.rb +92 -0
  259. data/lib/arel/nodes/inner_join.rb +8 -0
  260. data/lib/arel/nodes/insert_statement.rb +37 -0
  261. data/lib/arel/nodes/join_source.rb +20 -0
  262. data/lib/arel/nodes/matches.rb +18 -0
  263. data/lib/arel/nodes/named_function.rb +23 -0
  264. data/lib/arel/nodes/node.rb +51 -0
  265. data/lib/arel/nodes/node_expression.rb +13 -0
  266. data/lib/arel/nodes/ordering.rb +27 -0
  267. data/lib/arel/nodes/outer_join.rb +8 -0
  268. data/lib/arel/nodes/over.rb +15 -0
  269. data/lib/arel/nodes/regexp.rb +16 -0
  270. data/lib/arel/nodes/right_outer_join.rb +8 -0
  271. data/lib/arel/nodes/select_core.rb +67 -0
  272. data/lib/arel/nodes/select_statement.rb +41 -0
  273. data/lib/arel/nodes/sql_literal.rb +19 -0
  274. data/lib/arel/nodes/string_join.rb +11 -0
  275. data/lib/arel/nodes/table_alias.rb +31 -0
  276. data/lib/arel/nodes/terminal.rb +16 -0
  277. data/lib/arel/nodes/true.rb +16 -0
  278. data/lib/arel/nodes/unary.rb +44 -0
  279. data/lib/arel/nodes/unary_operation.rb +20 -0
  280. data/lib/arel/nodes/unqualified_column.rb +22 -0
  281. data/lib/arel/nodes/update_statement.rb +41 -0
  282. data/lib/arel/nodes/values_list.rb +9 -0
  283. data/lib/arel/nodes/window.rb +126 -0
  284. data/lib/arel/nodes/with.rb +11 -0
  285. data/lib/arel/nodes.rb +70 -0
  286. data/lib/arel/order_predications.rb +13 -0
  287. data/lib/arel/predications.rb +250 -0
  288. data/lib/arel/select_manager.rb +270 -0
  289. data/lib/arel/table.rb +118 -0
  290. data/lib/arel/tree_manager.rb +72 -0
  291. data/lib/arel/update_manager.rb +34 -0
  292. data/lib/arel/visitors/dot.rb +308 -0
  293. data/lib/arel/visitors/mysql.rb +93 -0
  294. data/lib/arel/visitors/postgresql.rb +120 -0
  295. data/lib/arel/visitors/sqlite.rb +38 -0
  296. data/lib/arel/visitors/to_sql.rb +899 -0
  297. data/lib/arel/visitors/visitor.rb +45 -0
  298. data/lib/arel/visitors.rb +13 -0
  299. data/lib/arel/window_predications.rb +9 -0
  300. data/lib/arel.rb +54 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  303. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  304. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  305. data/lib/rails/generators/active_record/migration.rb +19 -2
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +116 -30
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -9,67 +9,41 @@ module ActiveRecord
9
9
  PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds))
10
10
  end
11
11
 
12
- # The internal PostgreSQL identifier of the money data type.
13
- MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
14
- # The internal PostgreSQL identifier of the BYTEA data type.
15
- BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
16
-
17
- # create a 2D array representing the result set
18
- def result_as_array(res) #:nodoc:
19
- # check if we have any binary column and if they need escaping
20
- ftypes = Array.new(res.nfields) do |i|
21
- [i, res.ftype(i)]
22
- end
23
-
24
- rows = res.values
25
- return rows unless ftypes.any? { |_, x|
26
- x == BYTEA_COLUMN_TYPE_OID || x == MONEY_COLUMN_TYPE_OID
27
- }
28
-
29
- typehash = ftypes.group_by { |_, type| type }
30
- binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
31
- monies = typehash[MONEY_COLUMN_TYPE_OID] || []
32
-
33
- rows.each do |row|
34
- # unescape string passed BYTEA field (OID == 17)
35
- binaries.each do |index, _|
36
- row[index] = unescape_bytea(row[index])
37
- end
38
-
39
- # If this is a money type column and there are any currency symbols,
40
- # then strip them off. Indeed it would be prettier to do this in
41
- # PostgreSQLColumn.string_to_decimal but would break form input
42
- # fields that call value_before_type_cast.
43
- monies.each do |index, _|
44
- data = row[index]
45
- # Because money output is formatted according to the locale, there are two
46
- # cases to consider (note the decimal separators):
47
- # (1) $12,345,678.12
48
- # (2) $12.345.678,12
49
- case data
50
- when /^-?\D+[\d,]+\.\d{2}$/ # (1)
51
- data.gsub!(/[^-\d.]/, "")
52
- when /^-?\D+[\d.]+,\d{2}$/ # (2)
53
- data.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
54
- end
55
- end
56
- end
57
- end
58
-
59
12
  # Queries the database and returns the results in an Array-like object
60
13
  def query(sql, name = nil) #:nodoc:
14
+ materialize_transactions
15
+ mark_transaction_written_if_write(sql)
16
+
61
17
  log(sql, name) do
62
18
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
63
- result_as_array @connection.async_exec(sql)
19
+ @connection.async_exec(sql).map_types!(@type_map_for_results).values
64
20
  end
65
21
  end
66
22
  end
67
23
 
24
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
25
+ :close, :declare, :fetch, :move, :set, :show
26
+ ) # :nodoc:
27
+ private_constant :READ_QUERY
28
+
29
+ def write_query?(sql) # :nodoc:
30
+ !READ_QUERY.match?(sql)
31
+ rescue ArgumentError # Invalid encoding
32
+ !READ_QUERY.match?(sql.b)
33
+ end
34
+
68
35
  # Executes an SQL statement, returning a PG::Result object on success
69
36
  # or raising a PG::Error exception otherwise.
70
37
  # Note: the PG::Result object is manually memory managed; if you don't
71
38
  # need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
72
39
  def execute(sql, name = nil)
40
+ if preventing_writes? && write_query?(sql)
41
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
42
+ end
43
+
44
+ materialize_transactions
45
+ mark_transaction_written_if_write(sql)
46
+
73
47
  log(sql, name) do
74
48
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
75
49
  @connection.async_exec(sql)
@@ -84,9 +58,13 @@ module ActiveRecord
84
58
  fields.each_with_index do |fname, i|
85
59
  ftype = result.ftype i
86
60
  fmod = result.fmod i
87
- types[fname] = get_oid_type(ftype, fmod, fname)
61
+ case type = get_oid_type(ftype, fmod, fname)
62
+ when Type::Integer, Type::Float, OID::Decimal, Type::String, Type::DateTime, Type::Boolean
63
+ # skip if a column has already been type casted by pg decoders
64
+ else types[fname] = type
65
+ end
88
66
  end
89
- ActiveRecord::Result.new(fields, result.values, types)
67
+ build_result(columns: fields, rows: result.values, column_types: types)
90
68
  end
91
69
  end
92
70
 
@@ -95,7 +73,7 @@ module ActiveRecord
95
73
  end
96
74
  alias :exec_update :exec_delete
97
75
 
98
- def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
76
+ def sql_for_insert(sql, pk, binds) # :nodoc:
99
77
  if pk.nil?
100
78
  # Extract the table from the insert sql. Yuck.
101
79
  table_ref = extract_table_ref_from_insert_sql(sql)
@@ -130,7 +108,7 @@ module ActiveRecord
130
108
 
131
109
  # Begins a transaction.
132
110
  def begin_db_transaction
133
- execute "BEGIN"
111
+ execute("BEGIN", "TRANSACTION")
134
112
  end
135
113
 
136
114
  def begin_isolated_db_transaction(isolation)
@@ -140,15 +118,23 @@ module ActiveRecord
140
118
 
141
119
  # Commits a transaction.
142
120
  def commit_db_transaction
143
- execute "COMMIT"
121
+ execute("COMMIT", "TRANSACTION")
144
122
  end
145
123
 
146
124
  # Aborts a transaction.
147
125
  def exec_rollback_db_transaction
148
- execute "ROLLBACK"
126
+ execute("ROLLBACK", "TRANSACTION")
149
127
  end
150
128
 
151
129
  private
130
+ def execute_batch(statements, name = nil)
131
+ execute(combine_multi_statements(statements))
132
+ end
133
+
134
+ def build_truncate_statements(table_names)
135
+ ["TRUNCATE TABLE #{table_names.map(&method(:quote_table_name)).join(", ")}"]
136
+ end
137
+
152
138
  # Returns the current ID of a table's sequence.
153
139
  def last_insert_id_result(sequence_name)
154
140
  exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module PostgreSQL
6
6
  module OID # :nodoc:
7
7
  class Array < Type::Value # :nodoc:
8
- include Type::Helpers::Mutable
8
+ include ActiveModel::Type::Helpers::Mutable
9
9
 
10
10
  Data = Struct.new(:encoder, :values) # :nodoc:
11
11
 
@@ -77,7 +77,6 @@ module ActiveRecord
77
77
  end
78
78
 
79
79
  private
80
-
81
80
  def type_cast_array(value, method)
82
81
  if value.is_a?(::Array)
83
82
  value.map { |item| type_cast_array(item, method) }
@@ -43,10 +43,7 @@ module ActiveRecord
43
43
  /\A[0-9A-F]*\Z/i.match?(value)
44
44
  end
45
45
 
46
- # TODO Change this to private once we've dropped Ruby 2.2 support.
47
- # Workaround for Ruby 2.2 "private attribute?" warning.
48
- protected
49
-
46
+ private
50
47
  attr_reader :value
51
48
  end
52
49
  end
@@ -12,19 +12,17 @@ module ActiveRecord
12
12
  end
13
13
 
14
14
  def type_cast_for_schema(value)
15
- subnet_mask = value.instance_variable_get(:@mask_addr)
16
-
17
15
  # If the subnet mask is equal to /32, don't output it
18
- if subnet_mask == (2**32 - 1)
16
+ if value.prefix == 32
19
17
  "\"#{value}\""
20
18
  else
21
- "\"#{value}/#{subnet_mask.to_s(2).count('1')}\""
19
+ "\"#{value}/#{value.prefix}\""
22
20
  end
23
21
  end
24
22
 
25
23
  def serialize(value)
26
24
  if IPAddr === value
27
- "#{value}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
25
+ "#{value}/#{value.prefix}"
28
26
  else
29
27
  value
30
28
  end
@@ -10,8 +10,8 @@ module ActiveRecord
10
10
  when "infinity" then ::Float::INFINITY
11
11
  when "-infinity" then -::Float::INFINITY
12
12
  when / BC$/
13
- astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
14
- super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
13
+ value = value.sub(/^\d+/) { |year| format("%04d", -year.to_i + 1) }
14
+ super(value.delete_suffix!(" BC"))
15
15
  else
16
16
  super
17
17
  end
@@ -10,12 +10,20 @@ module ActiveRecord
10
10
  when "infinity" then ::Float::INFINITY
11
11
  when "-infinity" then -::Float::INFINITY
12
12
  when / BC$/
13
- astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
14
- super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
13
+ value = value.sub(/^\d+/) { |year| format("%04d", -year.to_i + 1) }
14
+ super(value.delete_suffix!(" BC"))
15
15
  else
16
16
  super
17
17
  end
18
18
  end
19
+
20
+ def type_cast_for_schema(value)
21
+ case value
22
+ when ::Float::INFINITY then "::Float::INFINITY"
23
+ when -::Float::INFINITY then "-::Float::INFINITY"
24
+ else super
25
+ end
26
+ end
19
27
  end
20
28
  end
21
29
  end
@@ -10,7 +10,6 @@ module ActiveRecord
10
10
  end
11
11
 
12
12
  private
13
-
14
13
  def cast_value(value)
15
14
  value.to_s
16
15
  end
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module PostgreSQL
6
6
  module OID # :nodoc:
7
7
  class Hstore < Type::Value # :nodoc:
8
- include Type::Helpers::Mutable
8
+ include ActiveModel::Type::Helpers::Mutable
9
9
 
10
10
  def type
11
11
  :hstore
@@ -46,7 +46,6 @@ module ActiveRecord
46
46
  end
47
47
 
48
48
  private
49
-
50
49
  HstorePair = begin
51
50
  quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
52
51
  unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/duration"
4
+
5
+ module ActiveRecord
6
+ module ConnectionAdapters
7
+ module PostgreSQL
8
+ module OID # :nodoc:
9
+ class Interval < Type::Value # :nodoc:
10
+ def type
11
+ :interval
12
+ end
13
+
14
+ def cast_value(value)
15
+ case value
16
+ when ::ActiveSupport::Duration
17
+ value
18
+ when ::String
19
+ begin
20
+ ::ActiveSupport::Duration.parse(value)
21
+ rescue ::ActiveSupport::Duration::ISO8601Parser::ParsingError
22
+ nil
23
+ end
24
+ else
25
+ super
26
+ end
27
+ end
28
+
29
+ def serialize(value)
30
+ case value
31
+ when ::ActiveSupport::Duration
32
+ value.iso8601(precision: self.precision)
33
+ when ::Numeric
34
+ # Sometimes operations on Times returns just float number of seconds so we need to handle that.
35
+ # Example: Time.current - (Time.current + 1.hour) # => -3600.000001776 (Float)
36
+ value.seconds.iso8601(precision: self.precision)
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ def type_cast_for_schema(value)
43
+ serialize(value).inspect
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module PostgreSQL
6
6
  module OID # :nodoc:
7
7
  class LegacyPoint < Type::Value # :nodoc:
8
- include Type::Helpers::Mutable
8
+ include ActiveModel::Type::Helpers::Mutable
9
9
 
10
10
  def type
11
11
  :point
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  def cast(value)
15
15
  case value
16
16
  when ::String
17
- if value[0] == "(" && value[-1] == ")"
17
+ if value.start_with?("(") && value.end_with?(")")
18
18
  value = value[1...-1]
19
19
  end
20
20
  cast(value.split(","))
@@ -34,9 +34,8 @@ module ActiveRecord
34
34
  end
35
35
 
36
36
  private
37
-
38
37
  def number_for_point(number)
39
- number.to_s.gsub(/\.0$/, "")
38
+ number.to_s.delete_suffix(".0")
40
39
  end
41
40
  end
42
41
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ module OID # :nodoc:
7
+ class Macaddr < Type::String # :nodoc:
8
+ def type
9
+ :macaddr
10
+ end
11
+
12
+ def changed?(old_value, new_value, _new_value_before_type_cast)
13
+ old_value.class != new_value.class ||
14
+ new_value && old_value.casecmp(new_value) != 0
15
+ end
16
+
17
+ def changed_in_place?(raw_old_value, new_value)
18
+ raw_old_value.class != new_value.class ||
19
+ new_value && raw_old_value.casecmp(new_value) != 0
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostgreSQL
6
6
  module OID # :nodoc:
7
- class Oid < Type::Integer # :nodoc:
7
+ class Oid < Type::UnsignedInteger # :nodoc:
8
8
  def type
9
9
  :oid
10
10
  end
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  module PostgreSQL
8
8
  module OID # :nodoc:
9
9
  class Point < Type::Value # :nodoc:
10
- include Type::Helpers::Mutable
10
+ include ActiveModel::Type::Helpers::Mutable
11
11
 
12
12
  def type
13
13
  :point
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
  when ::String
19
19
  return if value.blank?
20
20
 
21
- if value[0] == "(" && value[-1] == ")"
21
+ if value.start_with?("(") && value.end_with?(")")
22
22
  value = value[1...-1]
23
23
  end
24
24
  x, y = value.split(",")
@@ -50,9 +50,8 @@ module ActiveRecord
50
50
  end
51
51
 
52
52
  private
53
-
54
53
  def number_for_point(number)
55
- number.to_s.gsub(/\.0$/, "")
54
+ number.to_s.delete_suffix(".0")
56
55
  end
57
56
 
58
57
  def build_point(x, y)
@@ -58,25 +58,43 @@ module ActiveRecord
58
58
  end
59
59
 
60
60
  private
61
-
62
61
  def type_cast_single(value)
63
62
  infinity?(value) ? value : @subtype.deserialize(value)
64
63
  end
65
64
 
66
65
  def type_cast_single_for_database(value)
67
- infinity?(value) ? value : @subtype.serialize(value)
66
+ infinity?(value) ? value : @subtype.serialize(@subtype.cast(value))
68
67
  end
69
68
 
70
69
  def extract_bounds(value)
71
- from, to = value[1..-2].split(",")
70
+ from, to = value[1..-2].split(",", 2)
72
71
  {
73
- from: (value[1] == "," || from == "-infinity") ? infinity(negative: true) : from,
74
- to: (value[-2] == "," || to == "infinity") ? infinity : to,
75
- exclude_start: (value[0] == "("),
76
- exclude_end: (value[-1] == ")")
72
+ from: (from == "" || from == "-infinity") ? infinity(negative: true) : unquote(from),
73
+ to: (to == "" || to == "infinity") ? infinity : unquote(to),
74
+ exclude_start: value.start_with?("("),
75
+ exclude_end: value.end_with?(")")
77
76
  }
78
77
  end
79
78
 
79
+ # When formatting the bound values of range types, PostgreSQL quotes
80
+ # the bound value using double-quotes in certain conditions. Within
81
+ # a double-quoted string, literal " and \ characters are themselves
82
+ # escaped. In input, PostgreSQL accepts multiple escape styles for "
83
+ # (either \" or "") but in output always uses "".
84
+ # See:
85
+ # * https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-IO
86
+ # * https://www.postgresql.org/docs/current/rowtypes.html#ROWTYPES-IO-SYNTAX
87
+ def unquote(value)
88
+ if value.start_with?('"') && value.end_with?('"')
89
+ unquoted_value = value[1..-2]
90
+ unquoted_value.gsub!('""', '"')
91
+ unquoted_value.gsub!('\\\\', '\\')
92
+ unquoted_value
93
+ else
94
+ value
95
+ end
96
+ end
97
+
80
98
  def infinity(negative: false)
81
99
  if subtype.respond_to?(:infinity)
82
100
  subtype.infinity(negative: negative)
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
 
10
10
  def initialize(type, **options)
11
11
  @type = type
12
- super(options)
12
+ super(**options)
13
13
  end
14
14
  end
15
15
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/array/extract"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  module PostgreSQL
@@ -16,12 +18,12 @@ module ActiveRecord
16
18
 
17
19
  def run(records)
18
20
  nodes = records.reject { |row| @store.key? row["oid"].to_i }
19
- mapped, nodes = nodes.partition { |row| @store.key? row["typname"] }
20
- ranges, nodes = nodes.partition { |row| row["typtype"] == "r".freeze }
21
- enums, nodes = nodes.partition { |row| row["typtype"] == "e".freeze }
22
- domains, nodes = nodes.partition { |row| row["typtype"] == "d".freeze }
23
- arrays, nodes = nodes.partition { |row| row["typinput"] == "array_in".freeze }
24
- composites, nodes = nodes.partition { |row| row["typelem"].to_i != 0 }
21
+ mapped = nodes.extract! { |row| @store.key? row["typname"] }
22
+ ranges = nodes.extract! { |row| row["typtype"] == "r" }
23
+ enums = nodes.extract! { |row| row["typtype"] == "e" }
24
+ domains = nodes.extract! { |row| row["typtype"] == "d" }
25
+ arrays = nodes.extract! { |row| row["typinput"] == "array_in" }
26
+ composites = nodes.extract! { |row| row["typelem"].to_i != 0 }
25
27
 
26
28
  mapped.each { |row| register_mapped_type(row) }
27
29
  enums.each { |row| register_enum_type(row) }
@@ -34,7 +36,7 @@ module ActiveRecord
34
36
  def query_conditions_for_initial_load
35
37
  known_type_names = @store.keys.map { |n| "'#{n}'" }
36
38
  known_type_types = %w('r' 'e' 'd')
37
- <<-SQL % [known_type_names.join(", "), known_type_types.join(", ")]
39
+ <<~SQL % [known_type_names.join(", "), known_type_types.join(", ")]
38
40
  WHERE
39
41
  t.typname IN (%s)
40
42
  OR t.typtype IN (%s)
@@ -7,15 +7,27 @@ module ActiveRecord
7
7
  class Uuid < Type::Value # :nodoc:
8
8
  ACCEPTABLE_UUID = %r{\A(\{)?([a-fA-F0-9]{4}-?){8}(?(1)\}|)\z}
9
9
 
10
- alias_method :serialize, :deserialize
10
+ alias :serialize :deserialize
11
11
 
12
12
  def type
13
13
  :uuid
14
14
  end
15
15
 
16
- def cast(value)
17
- value.to_s[ACCEPTABLE_UUID, 0]
16
+ def changed?(old_value, new_value, _new_value_before_type_cast)
17
+ old_value.class != new_value.class ||
18
+ new_value && old_value.casecmp(new_value) != 0
18
19
  end
20
+
21
+ def changed_in_place?(raw_old_value, new_value)
22
+ raw_old_value.class != new_value.class ||
23
+ new_value && raw_old_value.casecmp(new_value) != 0
24
+ end
25
+
26
+ private
27
+ def cast_value(value)
28
+ casted = value.to_s
29
+ casted if casted.match?(ACCEPTABLE_UUID)
30
+ end
19
31
  end
20
32
  end
21
33
  end
@@ -11,7 +11,9 @@ require "active_record/connection_adapters/postgresql/oid/decimal"
11
11
  require "active_record/connection_adapters/postgresql/oid/enum"
12
12
  require "active_record/connection_adapters/postgresql/oid/hstore"
13
13
  require "active_record/connection_adapters/postgresql/oid/inet"
14
+ require "active_record/connection_adapters/postgresql/oid/interval"
14
15
  require "active_record/connection_adapters/postgresql/oid/jsonb"
16
+ require "active_record/connection_adapters/postgresql/oid/macaddr"
15
17
  require "active_record/connection_adapters/postgresql/oid/money"
16
18
  require "active_record/connection_adapters/postgresql/oid/oid"
17
19
  require "active_record/connection_adapters/postgresql/oid/point"
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
 
19
19
  # Quotes strings for use in SQL input.
20
20
  def quote_string(s) #:nodoc:
21
- @connection.escape(s)
21
+ PG::Connection.escape(s)
22
22
  end
23
23
 
24
24
  # Checks the following cases:
@@ -30,7 +30,7 @@ module ActiveRecord
30
30
  # - "schema.name".table_name
31
31
  # - "schema.name"."table.name"
32
32
  def quote_table_name(name) # :nodoc:
33
- @quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
33
+ self.class.quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
34
34
  end
35
35
 
36
36
  # Quotes schema names for use in SQL queries.
@@ -44,7 +44,7 @@ module ActiveRecord
44
44
 
45
45
  # Quotes column names for use in SQL queries.
46
46
  def quote_column_name(name) # :nodoc:
47
- @quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
47
+ self.class.quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
48
48
  end
49
49
 
50
50
  # Quote date/time values for use in SQL input.
@@ -67,8 +67,8 @@ module ActiveRecord
67
67
  elsif column.type == :uuid && value.is_a?(String) && /\(\)/.match?(value)
68
68
  value # Does not quote function default values for UUID columns
69
69
  elsif column.respond_to?(:array?)
70
- value = type_cast_from_column(column, value)
71
- quote(value)
70
+ type = lookup_cast_type_from_column(column)
71
+ quote(type.serialize(value))
72
72
  else
73
73
  super
74
74
  end
@@ -78,6 +78,43 @@ module ActiveRecord
78
78
  type_map.lookup(column.oid, column.fmod, column.sql_type)
79
79
  end
80
80
 
81
+ def column_name_matcher
82
+ COLUMN_NAME
83
+ end
84
+
85
+ def column_name_with_order_matcher
86
+ COLUMN_NAME_WITH_ORDER
87
+ end
88
+
89
+ COLUMN_NAME = /
90
+ \A
91
+ (
92
+ (?:
93
+ # "table_name"."column_name"::type_name | function(one or no argument)::type_name
94
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
95
+ )
96
+ (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
97
+ )
98
+ (?:\s*,\s*\g<1>)*
99
+ \z
100
+ /ix
101
+
102
+ COLUMN_NAME_WITH_ORDER = /
103
+ \A
104
+ (
105
+ (?:
106
+ # "table_name"."column_name"::type_name | function(one or no argument)::type_name
107
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
108
+ )
109
+ (?:\s+ASC|\s+DESC)?
110
+ (?:\s+NULLS\s+(?:FIRST|LAST))?
111
+ )
112
+ (?:\s*,\s*\g<1>)*
113
+ \z
114
+ /ix
115
+
116
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
117
+
81
118
  private
82
119
  def lookup_cast_type(sql_type)
83
120
  super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
@@ -93,11 +130,11 @@ module ActiveRecord
93
130
  elsif value.hex?
94
131
  "X'#{value}'"
95
132
  end
96
- when Float
97
- if value.infinite? || value.nan?
98
- "'#{value}'"
99
- else
133
+ when Numeric
134
+ if value.finite?
100
135
  super
136
+ else
137
+ "'#{value}'"
101
138
  end
102
139
  when OID::Array::Data
103
140
  _quote(encode_array(value))
@@ -138,7 +175,7 @@ module ActiveRecord
138
175
  end
139
176
 
140
177
  def encode_range(range)
141
- "[#{type_cast_range_value(range.first)},#{type_cast_range_value(range.last)}#{range.exclude_end? ? ')' : ']'}"
178
+ "[#{type_cast_range_value(range.begin)},#{type_cast_range_value(range.end)}#{range.exclude_end? ? ')' : ']'}"
142
179
  end
143
180
 
144
181
  def determine_encoding_of_strings_in_array(value)
@@ -24,9 +24,9 @@ WARNING: Rails was not able to disable referential integrity.
24
24
  This is most likely caused due to missing permissions.
25
25
  Rails needs superuser privileges to disable referential integrity.
26
26
 
27
- cause: #{original_exception.try(:message)}
27
+ cause: #{original_exception&.message}
28
28
 
29
- WARNING
29
+ WARNING
30
30
  raise e
31
31
  end
32
32