activerecord 6.0.3.2 → 6.1.0

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

Potentially problematic release.


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

Files changed (244) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +804 -708
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_record.rb +7 -14
  6. data/lib/active_record/aggregations.rb +1 -1
  7. data/lib/active_record/association_relation.rb +22 -14
  8. data/lib/active_record/associations.rb +114 -11
  9. data/lib/active_record/associations/alias_tracker.rb +19 -15
  10. data/lib/active_record/associations/association.rb +44 -28
  11. data/lib/active_record/associations/association_scope.rb +17 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  14. data/lib/active_record/associations/builder/association.rb +9 -3
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +19 -6
  22. data/lib/active_record/associations/collection_proxy.rb +13 -5
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +72 -50
  28. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +11 -5
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -8
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  38. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -11
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  44. data/lib/active_record/attribute_methods/write.rb +12 -20
  45. data/lib/active_record/attributes.rb +32 -7
  46. data/lib/active_record/autosave_association.rb +57 -40
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +152 -22
  49. data/lib/active_record/coders/yaml_column.rb +1 -1
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +191 -134
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +112 -27
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +54 -71
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
  68. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  71. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  72. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  73. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
  74. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  76. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  77. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
  80. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  82. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
  89. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  90. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  91. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  93. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  94. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  96. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  97. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  101. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
  102. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
  103. data/lib/active_record/connection_handling.rb +210 -71
  104. data/lib/active_record/core.rb +229 -63
  105. data/lib/active_record/database_configurations.rb +124 -85
  106. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  107. data/lib/active_record/database_configurations/database_config.rb +52 -9
  108. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  109. data/lib/active_record/database_configurations/url_config.rb +15 -40
  110. data/lib/active_record/delegated_type.rb +209 -0
  111. data/lib/active_record/destroy_association_async_job.rb +36 -0
  112. data/lib/active_record/enum.rb +40 -16
  113. data/lib/active_record/errors.rb +47 -12
  114. data/lib/active_record/explain.rb +9 -4
  115. data/lib/active_record/explain_subscriber.rb +1 -1
  116. data/lib/active_record/fixture_set/file.rb +10 -17
  117. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  118. data/lib/active_record/fixture_set/render_context.rb +1 -1
  119. data/lib/active_record/fixture_set/table_row.rb +2 -2
  120. data/lib/active_record/fixtures.rb +54 -8
  121. data/lib/active_record/gem_version.rb +3 -3
  122. data/lib/active_record/inheritance.rb +40 -18
  123. data/lib/active_record/insert_all.rb +35 -6
  124. data/lib/active_record/integration.rb +3 -5
  125. data/lib/active_record/internal_metadata.rb +16 -7
  126. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  127. data/lib/active_record/locking/optimistic.rb +22 -16
  128. data/lib/active_record/locking/pessimistic.rb +6 -2
  129. data/lib/active_record/log_subscriber.rb +26 -8
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  132. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/migration/command_recorder.rb +47 -27
  135. data/lib/active_record/migration/compatibility.rb +67 -17
  136. data/lib/active_record/model_schema.rb +117 -13
  137. data/lib/active_record/nested_attributes.rb +2 -3
  138. data/lib/active_record/no_touching.rb +1 -1
  139. data/lib/active_record/persistence.rb +50 -45
  140. data/lib/active_record/query_cache.rb +15 -5
  141. data/lib/active_record/querying.rb +11 -6
  142. data/lib/active_record/railtie.rb +64 -44
  143. data/lib/active_record/railties/databases.rake +266 -95
  144. data/lib/active_record/readonly_attributes.rb +4 -0
  145. data/lib/active_record/reflection.rb +71 -57
  146. data/lib/active_record/relation.rb +96 -67
  147. data/lib/active_record/relation/batches.rb +38 -31
  148. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  149. data/lib/active_record/relation/calculations.rb +101 -44
  150. data/lib/active_record/relation/delegation.rb +2 -1
  151. data/lib/active_record/relation/finder_methods.rb +45 -15
  152. data/lib/active_record/relation/from_clause.rb +1 -1
  153. data/lib/active_record/relation/merger.rb +27 -25
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  156. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  157. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  159. data/lib/active_record/relation/query_methods.rb +330 -195
  160. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  161. data/lib/active_record/relation/spawn_methods.rb +8 -7
  162. data/lib/active_record/relation/where_clause.rb +104 -57
  163. data/lib/active_record/result.rb +41 -33
  164. data/lib/active_record/runtime_registry.rb +2 -2
  165. data/lib/active_record/sanitization.rb +6 -17
  166. data/lib/active_record/schema_dumper.rb +34 -4
  167. data/lib/active_record/schema_migration.rb +2 -8
  168. data/lib/active_record/scoping/named.rb +6 -17
  169. data/lib/active_record/secure_token.rb +16 -8
  170. data/lib/active_record/serialization.rb +5 -3
  171. data/lib/active_record/signed_id.rb +116 -0
  172. data/lib/active_record/statement_cache.rb +20 -4
  173. data/lib/active_record/store.rb +2 -2
  174. data/lib/active_record/suppressor.rb +2 -2
  175. data/lib/active_record/table_metadata.rb +39 -51
  176. data/lib/active_record/tasks/database_tasks.rb +139 -113
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  180. data/lib/active_record/test_databases.rb +5 -4
  181. data/lib/active_record/test_fixtures.rb +37 -16
  182. data/lib/active_record/timestamp.rb +4 -6
  183. data/lib/active_record/touch_later.rb +21 -21
  184. data/lib/active_record/transactions.rb +15 -64
  185. data/lib/active_record/type.rb +8 -1
  186. data/lib/active_record/type/serialized.rb +6 -2
  187. data/lib/active_record/type/time.rb +10 -0
  188. data/lib/active_record/type_caster/connection.rb +0 -1
  189. data/lib/active_record/type_caster/map.rb +8 -5
  190. data/lib/active_record/validations.rb +1 -0
  191. data/lib/active_record/validations/numericality.rb +35 -0
  192. data/lib/active_record/validations/uniqueness.rb +24 -4
  193. data/lib/arel.rb +5 -13
  194. data/lib/arel/attributes/attribute.rb +4 -0
  195. data/lib/arel/collectors/bind.rb +5 -0
  196. data/lib/arel/collectors/composite.rb +8 -0
  197. data/lib/arel/collectors/sql_string.rb +7 -0
  198. data/lib/arel/collectors/substitute_binds.rb +7 -0
  199. data/lib/arel/nodes.rb +3 -1
  200. data/lib/arel/nodes/binary.rb +82 -8
  201. data/lib/arel/nodes/bind_param.rb +8 -0
  202. data/lib/arel/nodes/casted.rb +21 -9
  203. data/lib/arel/nodes/equality.rb +6 -9
  204. data/lib/arel/nodes/grouping.rb +3 -0
  205. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  206. data/lib/arel/nodes/in.rb +8 -1
  207. data/lib/arel/nodes/infix_operation.rb +13 -1
  208. data/lib/arel/nodes/join_source.rb +1 -1
  209. data/lib/arel/nodes/node.rb +7 -6
  210. data/lib/arel/nodes/ordering.rb +27 -0
  211. data/lib/arel/nodes/sql_literal.rb +3 -0
  212. data/lib/arel/nodes/table_alias.rb +7 -3
  213. data/lib/arel/nodes/unary.rb +0 -1
  214. data/lib/arel/predications.rb +12 -18
  215. data/lib/arel/select_manager.rb +1 -2
  216. data/lib/arel/table.rb +13 -5
  217. data/lib/arel/visitors.rb +0 -7
  218. data/lib/arel/visitors/dot.rb +14 -2
  219. data/lib/arel/visitors/mysql.rb +11 -1
  220. data/lib/arel/visitors/postgresql.rb +15 -4
  221. data/lib/arel/visitors/to_sql.rb +89 -78
  222. data/lib/rails/generators/active_record/migration.rb +6 -1
  223. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  224. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  225. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  226. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  227. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  228. metadata +28 -29
  229. data/lib/active_record/advisory_lock_base.rb +0 -18
  230. data/lib/active_record/attribute_decorators.rb +0 -88
  231. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  232. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  233. data/lib/active_record/define_callbacks.rb +0 -22
  234. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  235. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  236. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  237. data/lib/arel/attributes.rb +0 -22
  238. data/lib/arel/visitors/depth_first.rb +0 -203
  239. data/lib/arel/visitors/ibm_db.rb +0 -34
  240. data/lib/arel/visitors/informix.rb +0 -62
  241. data/lib/arel/visitors/mssql.rb +0 -156
  242. data/lib/arel/visitors/oracle.rb +0 -158
  243. data/lib/arel/visitors/oracle12.rb +0 -65
  244. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class PoolConfig # :nodoc:
6
+ include Mutex_m
7
+
8
+ attr_reader :db_config, :connection_specification_name
9
+ attr_accessor :schema_cache
10
+
11
+ INSTANCES = ObjectSpace::WeakMap.new
12
+ private_constant :INSTANCES
13
+
14
+ class << self
15
+ def discard_pools!
16
+ INSTANCES.each_key(&:discard_pool!)
17
+ end
18
+ end
19
+
20
+ def initialize(connection_specification_name, db_config)
21
+ super()
22
+ @connection_specification_name = connection_specification_name
23
+ @db_config = db_config
24
+ @pool = nil
25
+ INSTANCES[self] = self
26
+ end
27
+
28
+ def disconnect!
29
+ ActiveSupport::ForkTracker.check!
30
+
31
+ return unless @pool
32
+
33
+ synchronize do
34
+ return unless @pool
35
+
36
+ @pool.automatic_reconnect = false
37
+ @pool.disconnect!
38
+ end
39
+
40
+ nil
41
+ end
42
+
43
+ def pool
44
+ ActiveSupport::ForkTracker.check!
45
+
46
+ @pool || synchronize { @pool ||= ConnectionAdapters::ConnectionPool.new(self) }
47
+ end
48
+
49
+ def discard_pool!
50
+ return unless @pool
51
+
52
+ synchronize do
53
+ return unless @pool
54
+
55
+ @pool.discard!
56
+ @pool = nil
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ ActiveSupport::ForkTracker.after_fork { ActiveRecord::ConnectionAdapters::PoolConfig.discard_pools! }
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class PoolManager # :nodoc:
6
+ def initialize
7
+ @name_to_role_mapping = Hash.new { |h, k| h[k] = {} }
8
+ end
9
+
10
+ def shard_names
11
+ @name_to_role_mapping.values.flat_map { |shard_map| shard_map.keys }
12
+ end
13
+
14
+ def role_names
15
+ @name_to_role_mapping.keys
16
+ end
17
+
18
+ def pool_configs(role = nil)
19
+ if role
20
+ @name_to_role_mapping[role].values
21
+ else
22
+ @name_to_role_mapping.flat_map { |_, shard_map| shard_map.values }
23
+ end
24
+ end
25
+
26
+ def remove_role(role)
27
+ @name_to_role_mapping.delete(role)
28
+ end
29
+
30
+ def remove_pool_config(role, shard)
31
+ @name_to_role_mapping[role].delete(shard)
32
+ end
33
+
34
+ def get_pool_config(role, shard)
35
+ @name_to_role_mapping[role][shard]
36
+ end
37
+
38
+ def set_pool_config(role, shard, pool_config)
39
+ @name_to_role_mapping[role][shard] = pool_config
40
+ end
41
+ end
42
+ end
43
+ end
@@ -21,7 +21,30 @@ module ActiveRecord
21
21
  alias :array? :array
22
22
 
23
23
  def sql_type
24
- super.sub(/\[\]\z/, "")
24
+ super.delete_suffix("[]")
25
+ end
26
+
27
+ def init_with(coder)
28
+ @serial = coder["serial"]
29
+ super
30
+ end
31
+
32
+ def encode_with(coder)
33
+ coder["serial"] = @serial
34
+ super
35
+ end
36
+
37
+ def ==(other)
38
+ other.is_a?(Column) &&
39
+ super &&
40
+ serial? == other.serial?
41
+ end
42
+ alias :eql? :==
43
+
44
+ def hash
45
+ Column.hash ^
46
+ super.hash ^
47
+ serial?.hash
25
48
  end
26
49
  end
27
50
  end
@@ -9,66 +9,20 @@ 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
24
  READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
71
- :begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback, :with
25
+ :close, :declare, :fetch, :move, :set, :show
72
26
  ) # :nodoc:
73
27
  private_constant :READ_QUERY
74
28
 
@@ -86,6 +40,7 @@ module ActiveRecord
86
40
  end
87
41
 
88
42
  materialize_transactions
43
+ mark_transaction_written_if_write(sql)
89
44
 
90
45
  log(sql, name) do
91
46
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -101,9 +56,13 @@ module ActiveRecord
101
56
  fields.each_with_index do |fname, i|
102
57
  ftype = result.ftype i
103
58
  fmod = result.fmod i
104
- types[fname] = get_oid_type(ftype, fmod, fname)
59
+ case type = get_oid_type(ftype, fmod, fname)
60
+ when Type::Integer, Type::Float, Type::Decimal, Type::String, Type::DateTime, Type::Boolean
61
+ # skip if a column has already been type casted by pg decoders
62
+ else types[fname] = type
63
+ end
105
64
  end
106
- ActiveRecord::Result.new(fields, result.values, types)
65
+ build_result(columns: fields, rows: result.values, column_types: types)
107
66
  end
108
67
  end
109
68
 
@@ -147,7 +106,7 @@ module ActiveRecord
147
106
 
148
107
  # Begins a transaction.
149
108
  def begin_db_transaction
150
- execute "BEGIN"
109
+ execute("BEGIN", "TRANSACTION")
151
110
  end
152
111
 
153
112
  def begin_isolated_db_transaction(isolation)
@@ -157,12 +116,12 @@ module ActiveRecord
157
116
 
158
117
  # Commits a transaction.
159
118
  def commit_db_transaction
160
- execute "COMMIT"
119
+ execute("COMMIT", "TRANSACTION")
161
120
  end
162
121
 
163
122
  # Aborts a transaction.
164
123
  def exec_rollback_db_transaction
165
- execute "ROLLBACK"
124
+ execute("ROLLBACK", "TRANSACTION")
166
125
  end
167
126
 
168
127
  private
@@ -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"
@@ -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
@@ -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(","))
@@ -35,7 +35,7 @@ module ActiveRecord
35
35
 
36
36
  private
37
37
  def number_for_point(number)
38
- number.to_s.gsub(/\.0$/, "")
38
+ number.to_s.delete_suffix(".0")
39
39
  end
40
40
  end
41
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
@@ -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(",")
@@ -51,7 +51,7 @@ module ActiveRecord
51
51
 
52
52
  private
53
53
  def number_for_point(number)
54
- number.to_s.gsub(/\.0$/, "")
54
+ number.to_s.delete_suffix(".0")
55
55
  end
56
56
 
57
57
  def build_point(x, y)
@@ -67,15 +67,34 @@ module ActiveRecord
67
67
  end
68
68
 
69
69
  def extract_bounds(value)
70
- from, to = value[1..-2].split(",")
70
+ from, to = value[1..-2].split(",", 2)
71
71
  {
72
- from: (value[1] == "," || from == "-infinity") ? infinity(negative: true) : from,
73
- to: (value[-2] == "," || to == "infinity") ? infinity : to,
74
- exclude_start: (value[0] == "("),
75
- 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?(")")
76
76
  }
77
77
  end
78
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
+
79
98
  def infinity(negative: false)
80
99
  if subtype.respond_to?(:infinity)
81
100
  subtype.infinity(negative: negative)