activerecord 6.0.0 → 6.1.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1413 -614
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record/aggregations.rb +5 -6
  6. data/lib/active_record/association_relation.rb +30 -10
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -29
  9. data/lib/active_record/associations/association_scope.rb +19 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +38 -13
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/preloader/association.rb +49 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +13 -8
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +120 -13
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +33 -9
  45. data/lib/active_record/autosave_association.rb +63 -44
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +24 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -35
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +145 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +76 -79
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  102. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql_adapter.rb +84 -66
  104. data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
  105. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  106. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +40 -12
  107. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  108. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  109. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  110. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  111. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  112. data/lib/active_record/connection_adapters.rb +52 -0
  113. data/lib/active_record/connection_handling.rb +219 -81
  114. data/lib/active_record/core.rb +283 -71
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  117. data/lib/active_record/database_configurations/database_config.rb +52 -9
  118. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  119. data/lib/active_record/database_configurations/url_config.rb +15 -41
  120. data/lib/active_record/database_configurations.rb +125 -85
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +80 -38
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +58 -12
  134. data/lib/active_record/gem_version.rb +3 -3
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +43 -10
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +18 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +33 -18
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +28 -9
  143. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  144. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  145. data/lib/active_record/middleware/database_selector.rb +4 -2
  146. data/lib/active_record/migration/command_recorder.rb +53 -45
  147. data/lib/active_record/migration/compatibility.rb +75 -21
  148. data/lib/active_record/migration/join_table.rb +0 -1
  149. data/lib/active_record/migration.rb +115 -85
  150. data/lib/active_record/model_schema.rb +120 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/console_sandbox.rb +2 -4
  159. data/lib/active_record/railties/databases.rake +280 -99
  160. data/lib/active_record/readonly_attributes.rb +4 -0
  161. data/lib/active_record/reflection.rb +77 -63
  162. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  163. data/lib/active_record/relation/batches.rb +38 -32
  164. data/lib/active_record/relation/calculations.rb +106 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +55 -17
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  170. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  171. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  172. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  173. data/lib/active_record/relation/predicate_builder.rb +59 -40
  174. data/lib/active_record/relation/query_methods.rb +346 -181
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +111 -62
  178. data/lib/active_record/relation.rb +116 -82
  179. data/lib/active_record/result.rb +41 -34
  180. data/lib/active_record/runtime_registry.rb +2 -2
  181. data/lib/active_record/sanitization.rb +6 -17
  182. data/lib/active_record/schema_dumper.rb +34 -4
  183. data/lib/active_record/schema_migration.rb +2 -8
  184. data/lib/active_record/scoping/default.rb +1 -4
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/scoping.rb +0 -1
  187. data/lib/active_record/secure_token.rb +16 -8
  188. data/lib/active_record/serialization.rb +5 -3
  189. data/lib/active_record/signed_id.rb +116 -0
  190. data/lib/active_record/statement_cache.rb +20 -4
  191. data/lib/active_record/store.rb +9 -4
  192. data/lib/active_record/suppressor.rb +2 -2
  193. data/lib/active_record/table_metadata.rb +42 -36
  194. data/lib/active_record/tasks/database_tasks.rb +140 -113
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  197. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  198. data/lib/active_record/test_databases.rb +5 -4
  199. data/lib/active_record/test_fixtures.rb +87 -20
  200. data/lib/active_record/timestamp.rb +4 -7
  201. data/lib/active_record/touch_later.rb +20 -21
  202. data/lib/active_record/transactions.rb +26 -73
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type.rb +8 -2
  210. data/lib/active_record/type_caster/connection.rb +0 -1
  211. data/lib/active_record/type_caster/map.rb +8 -5
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/active_record/validations.rb +3 -3
  216. data/lib/active_record.rb +7 -13
  217. data/lib/arel/attributes/attribute.rb +4 -0
  218. data/lib/arel/collectors/bind.rb +5 -0
  219. data/lib/arel/collectors/composite.rb +8 -0
  220. data/lib/arel/collectors/sql_string.rb +7 -0
  221. data/lib/arel/collectors/substitute_binds.rb +7 -0
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/nodes.rb +3 -1
  237. data/lib/arel/predications.rb +17 -24
  238. data/lib/arel/select_manager.rb +1 -2
  239. data/lib/arel/table.rb +13 -5
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/arel/visitors.rb +0 -7
  247. data/lib/arel.rb +15 -12
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  250. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  251. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  252. data/lib/rails/generators/active_record/migration.rb +6 -2
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +31 -27
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -9,69 +9,27 @@ 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:
61
14
  materialize_transactions
15
+ mark_transaction_written_if_write(sql)
62
16
 
63
17
  log(sql, name) do
64
18
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
65
- result_as_array @connection.async_exec(sql)
19
+ @connection.async_exec(sql).map_types!(@type_map_for_results).values
66
20
  end
67
21
  end
68
22
  end
69
23
 
70
- READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback) # :nodoc:
24
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
25
+ :close, :declare, :fetch, :move, :set, :show
26
+ ) # :nodoc:
71
27
  private_constant :READ_QUERY
72
28
 
73
29
  def write_query?(sql) # :nodoc:
74
30
  !READ_QUERY.match?(sql)
31
+ rescue ArgumentError # Invalid encoding
32
+ !READ_QUERY.match?(sql.b)
75
33
  end
76
34
 
77
35
  # Executes an SQL statement, returning a PG::Result object on success
@@ -84,6 +42,7 @@ module ActiveRecord
84
42
  end
85
43
 
86
44
  materialize_transactions
45
+ mark_transaction_written_if_write(sql)
87
46
 
88
47
  log(sql, name) do
89
48
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -99,9 +58,13 @@ module ActiveRecord
99
58
  fields.each_with_index do |fname, i|
100
59
  ftype = result.ftype i
101
60
  fmod = result.fmod i
102
- 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
103
66
  end
104
- ActiveRecord::Result.new(fields, result.values, types)
67
+ build_result(columns: fields, rows: result.values, column_types: types)
105
68
  end
106
69
  end
107
70
 
@@ -145,7 +108,7 @@ module ActiveRecord
145
108
 
146
109
  # Begins a transaction.
147
110
  def begin_db_transaction
148
- execute "BEGIN"
111
+ execute("BEGIN", "TRANSACTION")
149
112
  end
150
113
 
151
114
  def begin_isolated_db_transaction(isolation)
@@ -155,17 +118,21 @@ module ActiveRecord
155
118
 
156
119
  # Commits a transaction.
157
120
  def commit_db_transaction
158
- execute "COMMIT"
121
+ execute("COMMIT", "TRANSACTION")
159
122
  end
160
123
 
161
124
  # Aborts a transaction.
162
125
  def exec_rollback_db_transaction
163
- execute "ROLLBACK"
126
+ execute("ROLLBACK", "TRANSACTION")
164
127
  end
165
128
 
166
129
  private
167
- def build_truncate_statements(*table_names)
168
- "TRUNCATE TABLE #{table_names.map(&method(:quote_table_name)).join(", ")}"
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(", ")}"]
169
136
  end
170
137
 
171
138
  # Returns the current ID of a table's sequence.
@@ -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) }
@@ -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
@@ -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
@@ -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
@@ -26,9 +26,9 @@ module ActiveRecord
26
26
 
27
27
  value = value.sub(/^\((.+)\)$/, '-\1') # (4)
28
28
  case value
29
- when /^-?\D*[\d,]+\.\d{2}$/ # (1)
29
+ when /^-?\D*+[\d,]+\.\d{2}$/ # (1)
30
30
  value.gsub!(/[^-\d.]/, "")
31
- when /^-?\D*[\d.]+,\d{2}$/ # (2)
31
+ when /^-?\D*+[\d.]+,\d{2}$/ # (2)
32
32
  value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
33
33
  end
34
34
 
@@ -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
@@ -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,7 +58,6 @@ 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
@@ -68,15 +67,34 @@ module ActiveRecord
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
@@ -7,14 +7,23 @@ 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
- private
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
19
+ end
17
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
18
27
  def cast_value(value)
19
28
  casted = value.to_s
20
29
  casted if casted.match?(ACCEPTABLE_UUID)
@@ -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"
@@ -4,6 +4,12 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostgreSQL
6
6
  module Quoting
7
+ class IntegerOutOf64BitRange < StandardError
8
+ def initialize(msg)
9
+ super(msg)
10
+ end
11
+ end
12
+
7
13
  # Escapes binary strings for bytea input to the database.
8
14
  def escape_bytea(value)
9
15
  @connection.escape_bytea(value) if value
@@ -18,7 +24,7 @@ module ActiveRecord
18
24
 
19
25
  # Quotes strings for use in SQL input.
20
26
  def quote_string(s) #:nodoc:
21
- @connection.escape(s)
27
+ PG::Connection.escape(s)
22
28
  end
23
29
 
24
30
  # Checks the following cases:
@@ -67,8 +73,8 @@ module ActiveRecord
67
73
  elsif column.type == :uuid && value.is_a?(String) && /\(\)/.match?(value)
68
74
  value # Does not quote function default values for UUID columns
69
75
  elsif column.respond_to?(:array?)
70
- value = type_cast_from_column(column, value)
71
- quote(value)
76
+ type = lookup_cast_type_from_column(column)
77
+ quote(type.serialize(value))
72
78
  else
73
79
  super
74
80
  end
@@ -93,7 +99,7 @@ module ActiveRecord
93
99
  # "table_name"."column_name"::type_name | function(one or no argument)::type_name
94
100
  ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
95
101
  )
96
- (?:\s+AS\s+(?:\w+|"\w+"))?
102
+ (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
97
103
  )
98
104
  (?:\s*,\s*\g<1>)*
99
105
  \z
@@ -120,7 +126,27 @@ module ActiveRecord
120
126
  super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
121
127
  end
122
128
 
129
+ def check_int_in_range(value)
130
+ if value.to_int > 9223372036854775807 || value.to_int < -9223372036854775808
131
+ exception = <<~ERROR
132
+ Provided value outside of the range of a signed 64bit integer.
133
+
134
+ PostgreSQL will treat the column type in question as a numeric.
135
+ This may result in a slow sequential scan due to a comparison
136
+ being performed between an integer or bigint value and a numeric value.
137
+
138
+ To allow for this potentially unwanted behavior, set
139
+ ActiveRecord::Base.raise_int_wider_than_64bit to false.
140
+ ERROR
141
+ raise IntegerOutOf64BitRange.new exception
142
+ end
143
+ end
144
+
123
145
  def _quote(value)
146
+ if ActiveRecord::Base.raise_int_wider_than_64bit && value.is_a?(Integer)
147
+ check_int_in_range(value)
148
+ end
149
+
124
150
  case value
125
151
  when OID::Xml::Data
126
152
  "xml '#{quote_string(value.to_s)}'"
@@ -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
 
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostgreSQL
6
- class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
6
+ class SchemaCreation < SchemaCreation # :nodoc:
7
7
  private
8
8
  def visit_AlterTable(o)
9
9
  super << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
@@ -13,13 +13,17 @@ module ActiveRecord
13
13
  super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
14
14
  end
15
15
 
16
+ def visit_CheckConstraintDefinition(o)
17
+ super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
18
+ end
19
+
16
20
  def visit_ValidateConstraint(name)
17
21
  "VALIDATE CONSTRAINT #{quote_column_name(name)}"
18
22
  end
19
23
 
20
24
  def visit_ChangeColumnDefinition(o)
21
25
  column = o.column
22
- column.sql_type = type_to_sql(column.type, column.options)
26
+ column.sql_type = type_to_sql(column.type, **column.options)
23
27
  quoted_column_name = quote_column_name(o.name)
24
28
 
25
29
  change_column_sql = +"ALTER COLUMN #{quoted_column_name} TYPE #{column.sql_type}"
@@ -33,7 +37,7 @@ module ActiveRecord
33
37
  if options[:using]
34
38
  change_column_sql << " USING #{options[:using]}"
35
39
  elsif options[:cast_as]
36
- cast_as_type = type_to_sql(options[:cast_as], options)
40
+ cast_as_type = type_to_sql(options[:cast_as], **options)
37
41
  change_column_sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
38
42
  end
39
43
 
@@ -186,7 +186,7 @@ module ActiveRecord
186
186
 
187
187
  attr_reader :unlogged
188
188
 
189
- def initialize(*)
189
+ def initialize(*, **)
190
190
  super
191
191
  @unlogged = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables
192
192
  end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module PostgreSQL
6
6
  class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
7
7
  private
8
-
9
8
  def extensions(stream)
10
9
  extensions = @connection.extensions
11
10
  if extensions.any?