activerecord 4.2.9 → 5.2.8

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 (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +614 -1572
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -89
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +24 -12
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -593
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -188
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +135 -88
  133. data/lib/active_record/errors.rb +179 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +4 -2
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +94 -32
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -340
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +199 -124
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +24 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -50
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
@@ -6,72 +6,90 @@ module ActiveRecord
6
6
  module OID # :nodoc:
7
7
  class Range < Type::Value # :nodoc:
8
8
  attr_reader :subtype, :type
9
+ delegate :user_input_in_time_zone, to: :subtype
9
10
 
10
- def initialize(subtype, type)
11
+ def initialize(subtype, type = :range)
11
12
  @subtype = subtype
12
13
  @type = type
13
14
  end
14
15
 
15
16
  def type_cast_for_schema(value)
16
- value.inspect.gsub('Infinity', '::Float::INFINITY')
17
+ value.inspect.gsub("Infinity", "::Float::INFINITY")
17
18
  end
18
19
 
19
20
  def cast_value(value)
20
- return if value == 'empty'
21
- return value if value.is_a?(::Range)
21
+ return if value == "empty"
22
+ return value unless value.is_a?(::String)
22
23
 
23
24
  extracted = extract_bounds(value)
24
25
  from = type_cast_single extracted[:from]
25
26
  to = type_cast_single extracted[:to]
26
27
 
27
28
  if !infinity?(from) && extracted[:exclude_start]
28
- if from.respond_to?(:succ)
29
- from = from.succ
30
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
31
- Excluding the beginning of a Range is only partialy supported
32
- through `#succ`. This is not reliable and will be removed in
33
- the future.
34
- MSG
35
- else
36
- raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')"
37
- end
29
+ raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')"
38
30
  end
39
31
  ::Range.new(from, to, extracted[:exclude_end])
40
32
  end
41
33
 
42
- def type_cast_for_database(value)
34
+ def serialize(value)
43
35
  if value.is_a?(::Range)
44
36
  from = type_cast_single_for_database(value.begin)
45
37
  to = type_cast_single_for_database(value.end)
46
- "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}"
38
+ ::Range.new(from, to, value.exclude_end?)
47
39
  else
48
40
  super
49
41
  end
50
42
  end
51
43
 
52
- private
53
-
54
- def type_cast_single(value)
55
- infinity?(value) ? value : @subtype.type_cast_from_database(value)
44
+ def ==(other)
45
+ other.is_a?(Range) &&
46
+ other.subtype == subtype &&
47
+ other.type == type
56
48
  end
57
49
 
58
- def type_cast_single_for_database(value)
59
- infinity?(value) ? '' : @subtype.type_cast_for_database(value)
50
+ def map(value) # :nodoc:
51
+ new_begin = yield(value.begin)
52
+ new_end = yield(value.end)
53
+ ::Range.new(new_begin, new_end, value.exclude_end?)
60
54
  end
61
55
 
62
- def extract_bounds(value)
63
- from, to = value[1..-2].split(',')
64
- {
65
- from: (value[1] == ',' || from == '-infinity') ? @subtype.infinity(negative: true) : from,
66
- to: (value[-2] == ',' || to == 'infinity') ? @subtype.infinity : to,
67
- exclude_start: (value[0] == '('),
68
- exclude_end: (value[-1] == ')')
69
- }
56
+ def force_equality?(value)
57
+ value.is_a?(::Range)
70
58
  end
71
59
 
72
- def infinity?(value)
73
- value.respond_to?(:infinite?) && value.infinite?
74
- end
60
+ private
61
+
62
+ def type_cast_single(value)
63
+ infinity?(value) ? value : @subtype.deserialize(value)
64
+ end
65
+
66
+ def type_cast_single_for_database(value)
67
+ infinity?(value) ? value : @subtype.serialize(value)
68
+ end
69
+
70
+ def extract_bounds(value)
71
+ from, to = value[1..-2].split(",")
72
+ {
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] == ")")
77
+ }
78
+ end
79
+
80
+ def infinity(negative: false)
81
+ if subtype.respond_to?(:infinity)
82
+ subtype.infinity(negative: negative)
83
+ elsif negative
84
+ -::Float::INFINITY
85
+ else
86
+ ::Float::INFINITY
87
+ end
88
+ end
89
+
90
+ def infinity?(value)
91
+ value.respond_to?(:infinite?) && value.infinite?
92
+ end
75
93
  end
76
94
  end
77
95
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
@@ -5,12 +7,9 @@ module ActiveRecord
5
7
  class SpecializedString < Type::String # :nodoc:
6
8
  attr_reader :type
7
9
 
8
- def initialize(type)
10
+ def initialize(type, **options)
9
11
  @type = type
10
- end
11
-
12
- def text?
13
- false
12
+ super(options)
14
13
  end
15
14
  end
16
15
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
@@ -13,13 +15,13 @@ module ActiveRecord
13
15
  end
14
16
 
15
17
  def run(records)
16
- nodes = records.reject { |row| @store.key? row['oid'].to_i }
17
- mapped, nodes = nodes.partition { |row| @store.key? row['typname'] }
18
- ranges, nodes = nodes.partition { |row| row['typtype'] == 'r'.freeze }
19
- enums, nodes = nodes.partition { |row| row['typtype'] == 'e'.freeze }
20
- domains, nodes = nodes.partition { |row| row['typtype'] == 'd'.freeze }
21
- arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in'.freeze }
22
- composites, nodes = nodes.partition { |row| row['typelem'].to_i != 0 }
18
+ 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 }
23
25
 
24
26
  mapped.each { |row| register_mapped_type(row) }
25
27
  enums.each { |row| register_enum_type(row) }
@@ -29,8 +31,8 @@ module ActiveRecord
29
31
  composites.each { |row| register_composite_type(row) }
30
32
  end
31
33
 
32
- def query_conditions_for_initial_load(type_map)
33
- known_type_names = type_map.keys.map { |n| "'#{n}'" }
34
+ def query_conditions_for_initial_load
35
+ known_type_names = @store.keys.map { |n| "'#{n}'" }
34
36
  known_type_types = %w('r' 'e' 'd')
35
37
  <<-SQL % [known_type_names.join(", "), known_type_types.join(", ")]
36
38
  WHERE
@@ -42,66 +44,66 @@ module ActiveRecord
42
44
  end
43
45
 
44
46
  private
45
- def register_mapped_type(row)
46
- alias_type row['oid'], row['typname']
47
- end
47
+ def register_mapped_type(row)
48
+ alias_type row["oid"], row["typname"]
49
+ end
48
50
 
49
- def register_enum_type(row)
50
- register row['oid'], OID::Enum.new
51
- end
51
+ def register_enum_type(row)
52
+ register row["oid"], OID::Enum.new
53
+ end
52
54
 
53
- def register_array_type(row)
54
- register_with_subtype(row['oid'], row['typelem'].to_i) do |subtype|
55
- OID::Array.new(subtype, row['typdelim'])
55
+ def register_array_type(row)
56
+ register_with_subtype(row["oid"], row["typelem"].to_i) do |subtype|
57
+ OID::Array.new(subtype, row["typdelim"])
58
+ end
56
59
  end
57
- end
58
60
 
59
- def register_range_type(row)
60
- register_with_subtype(row['oid'], row['rngsubtype'].to_i) do |subtype|
61
- OID::Range.new(subtype, row['typname'].to_sym)
61
+ def register_range_type(row)
62
+ register_with_subtype(row["oid"], row["rngsubtype"].to_i) do |subtype|
63
+ OID::Range.new(subtype, row["typname"].to_sym)
64
+ end
62
65
  end
63
- end
64
66
 
65
- def register_domain_type(row)
66
- if base_type = @store.lookup(row["typbasetype"].to_i)
67
- register row['oid'], base_type
68
- else
69
- warn "unknown base type (OID: #{row["typbasetype"]}) for domain #{row["typname"]}."
67
+ def register_domain_type(row)
68
+ if base_type = @store.lookup(row["typbasetype"].to_i)
69
+ register row["oid"], base_type
70
+ else
71
+ warn "unknown base type (OID: #{row["typbasetype"]}) for domain #{row["typname"]}."
72
+ end
70
73
  end
71
- end
72
74
 
73
- def register_composite_type(row)
74
- if subtype = @store.lookup(row['typelem'].to_i)
75
- register row['oid'], OID::Vector.new(row['typdelim'], subtype)
75
+ def register_composite_type(row)
76
+ if subtype = @store.lookup(row["typelem"].to_i)
77
+ register row["oid"], OID::Vector.new(row["typdelim"], subtype)
78
+ end
76
79
  end
77
- end
78
80
 
79
- def register(oid, oid_type = nil, &block)
80
- oid = assert_valid_registration(oid, oid_type || block)
81
- if block_given?
82
- @store.register_type(oid, &block)
83
- else
84
- @store.register_type(oid, oid_type)
81
+ def register(oid, oid_type = nil, &block)
82
+ oid = assert_valid_registration(oid, oid_type || block)
83
+ if block_given?
84
+ @store.register_type(oid, &block)
85
+ else
86
+ @store.register_type(oid, oid_type)
87
+ end
85
88
  end
86
- end
87
89
 
88
- def alias_type(oid, target)
89
- oid = assert_valid_registration(oid, target)
90
- @store.alias_type(oid, target)
91
- end
90
+ def alias_type(oid, target)
91
+ oid = assert_valid_registration(oid, target)
92
+ @store.alias_type(oid, target)
93
+ end
92
94
 
93
- def register_with_subtype(oid, target_oid)
94
- if @store.key?(target_oid)
95
- register(oid) do |_, *args|
96
- yield @store.lookup(target_oid, *args)
95
+ def register_with_subtype(oid, target_oid)
96
+ if @store.key?(target_oid)
97
+ register(oid) do |_, *args|
98
+ yield @store.lookup(target_oid, *args)
99
+ end
97
100
  end
98
101
  end
99
- end
100
102
 
101
- def assert_valid_registration(oid, oid_type)
102
- raise ArgumentError, "can't register nil type for OID #{oid}" if oid_type.nil?
103
- oid.to_i
104
- end
103
+ def assert_valid_registration(oid, oid_type)
104
+ raise ArgumentError, "can't register nil type for OID #{oid}" if oid_type.nil?
105
+ oid.to_i
106
+ end
105
107
  end
106
108
  end
107
109
  end
@@ -1,17 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
4
6
  module OID # :nodoc:
5
7
  class Uuid < Type::Value # :nodoc:
6
- ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x
8
+ ACCEPTABLE_UUID = %r{\A(\{)?([a-fA-F0-9]{4}-?){8}(?(1)\}|)\z}
7
9
 
8
- alias_method :type_cast_for_database, :type_cast_from_database
10
+ alias_method :serialize, :deserialize
9
11
 
10
12
  def type
11
13
  :uuid
12
14
  end
13
15
 
14
- def type_cast(value)
16
+ def cast(value)
15
17
  value.to_s[ACCEPTABLE_UUID, 0]
16
18
  end
17
19
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
@@ -16,7 +18,7 @@ module ActiveRecord
16
18
  # FIXME: this should probably split on +delim+ and use +subtype+
17
19
  # to cast the values. Unfortunately, the current Rails behavior
18
20
  # is to just return the string.
19
- def type_cast(value)
21
+ def cast(value)
20
22
  value
21
23
  end
22
24
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
@@ -7,7 +9,7 @@ module ActiveRecord
7
9
  :xml
8
10
  end
9
11
 
10
- def type_cast_for_database(value)
12
+ def serialize(value)
11
13
  return unless value
12
14
  Data.new(super)
13
15
  end
@@ -1,30 +1,28 @@
1
- require 'active_record/connection_adapters/postgresql/oid/infinity'
1
+ # frozen_string_literal: true
2
2
 
3
- require 'active_record/connection_adapters/postgresql/oid/array'
4
- require 'active_record/connection_adapters/postgresql/oid/bit'
5
- require 'active_record/connection_adapters/postgresql/oid/bit_varying'
6
- require 'active_record/connection_adapters/postgresql/oid/bytea'
7
- require 'active_record/connection_adapters/postgresql/oid/cidr'
8
- require 'active_record/connection_adapters/postgresql/oid/date'
9
- require 'active_record/connection_adapters/postgresql/oid/date_time'
10
- require 'active_record/connection_adapters/postgresql/oid/decimal'
11
- require 'active_record/connection_adapters/postgresql/oid/enum'
12
- require 'active_record/connection_adapters/postgresql/oid/float'
13
- require 'active_record/connection_adapters/postgresql/oid/hstore'
14
- require 'active_record/connection_adapters/postgresql/oid/inet'
15
- require 'active_record/connection_adapters/postgresql/oid/integer'
16
- require 'active_record/connection_adapters/postgresql/oid/json'
17
- require 'active_record/connection_adapters/postgresql/oid/jsonb'
18
- require 'active_record/connection_adapters/postgresql/oid/money'
19
- require 'active_record/connection_adapters/postgresql/oid/point'
20
- require 'active_record/connection_adapters/postgresql/oid/range'
21
- require 'active_record/connection_adapters/postgresql/oid/specialized_string'
22
- require 'active_record/connection_adapters/postgresql/oid/time'
23
- require 'active_record/connection_adapters/postgresql/oid/uuid'
24
- require 'active_record/connection_adapters/postgresql/oid/vector'
25
- require 'active_record/connection_adapters/postgresql/oid/xml'
3
+ require "active_record/connection_adapters/postgresql/oid/array"
4
+ require "active_record/connection_adapters/postgresql/oid/bit"
5
+ require "active_record/connection_adapters/postgresql/oid/bit_varying"
6
+ require "active_record/connection_adapters/postgresql/oid/bytea"
7
+ require "active_record/connection_adapters/postgresql/oid/cidr"
8
+ require "active_record/connection_adapters/postgresql/oid/date"
9
+ require "active_record/connection_adapters/postgresql/oid/date_time"
10
+ require "active_record/connection_adapters/postgresql/oid/decimal"
11
+ require "active_record/connection_adapters/postgresql/oid/enum"
12
+ require "active_record/connection_adapters/postgresql/oid/hstore"
13
+ require "active_record/connection_adapters/postgresql/oid/inet"
14
+ require "active_record/connection_adapters/postgresql/oid/jsonb"
15
+ require "active_record/connection_adapters/postgresql/oid/money"
16
+ require "active_record/connection_adapters/postgresql/oid/oid"
17
+ require "active_record/connection_adapters/postgresql/oid/point"
18
+ require "active_record/connection_adapters/postgresql/oid/legacy_point"
19
+ require "active_record/connection_adapters/postgresql/oid/range"
20
+ require "active_record/connection_adapters/postgresql/oid/specialized_string"
21
+ require "active_record/connection_adapters/postgresql/oid/uuid"
22
+ require "active_record/connection_adapters/postgresql/oid/vector"
23
+ require "active_record/connection_adapters/postgresql/oid/xml"
26
24
 
27
- require 'active_record/connection_adapters/postgresql/oid/type_map_initializer'
25
+ require "active_record/connection_adapters/postgresql/oid/type_map_initializer"
28
26
 
29
27
  module ActiveRecord
30
28
  module ConnectionAdapters
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
@@ -27,8 +29,13 @@ module ActiveRecord
27
29
  # - schema_name."table.name"
28
30
  # - "schema.name".table_name
29
31
  # - "schema.name"."table.name"
30
- def quote_table_name(name)
31
- Utils.extract_schema_qualified_name(name.to_s).quoted
32
+ def quote_table_name(name) # :nodoc:
33
+ @quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
34
+ end
35
+
36
+ # Quotes schema names for use in SQL queries.
37
+ def quote_schema_name(name)
38
+ PG::Connection.quote_ident(name)
32
39
  end
33
40
 
34
41
  def quote_table_name_for_assignment(table, attr)
@@ -36,72 +43,125 @@ module ActiveRecord
36
43
  end
37
44
 
38
45
  # Quotes column names for use in SQL queries.
39
- def quote_column_name(name) #:nodoc:
40
- PGconn.quote_ident(name.to_s)
46
+ def quote_column_name(name) # :nodoc:
47
+ @quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
41
48
  end
42
49
 
43
- # Quote date/time values for use in SQL input. Includes microseconds
44
- # if the value is a Time responding to usec.
50
+ # Quote date/time values for use in SQL input.
45
51
  def quoted_date(value) #:nodoc:
46
- result = super
47
- if value.acts_like?(:time) && value.respond_to?(:usec)
48
- result = "#{result}.#{sprintf("%06d", value.usec)}"
49
- end
50
-
51
52
  if value.year <= 0
52
53
  bce_year = format("%04d", -value.year + 1)
53
- result = result.sub(/^-?\d+/, bce_year) + " BC"
54
+ super.sub(/^-?\d+/, bce_year) + " BC"
55
+ else
56
+ super
54
57
  end
55
- result
56
58
  end
57
59
 
58
- # Does not quote function default values for UUID columns
59
- def quote_default_value(value, column) #:nodoc:
60
- if column.type == :uuid && value =~ /\(\)/
61
- value
60
+ def quoted_binary(value) # :nodoc:
61
+ "'#{escape_bytea(value.to_s)}'"
62
+ end
63
+
64
+ def quote_default_expression(value, column) # :nodoc:
65
+ if value.is_a?(Proc)
66
+ value.call
67
+ elsif column.type == :uuid && value.is_a?(String) && /\(\)/.match?(value)
68
+ value # Does not quote function default values for UUID columns
69
+ elsif column.respond_to?(:array?)
70
+ value = type_cast_from_column(column, value)
71
+ quote(value)
62
72
  else
63
- quote(value, column)
73
+ super
64
74
  end
65
75
  end
66
76
 
77
+ def lookup_cast_type_from_column(column) # :nodoc:
78
+ type_map.lookup(column.oid, column.fmod, column.sql_type)
79
+ end
80
+
67
81
  private
82
+ def lookup_cast_type(sql_type)
83
+ super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
84
+ end
68
85
 
69
- def _quote(value)
70
- case value
71
- when Type::Binary::Data
72
- "'#{escape_bytea(value.to_s)}'"
73
- when OID::Xml::Data
74
- "xml '#{quote_string(value.to_s)}'"
75
- when OID::Bit::Data
76
- if value.binary?
77
- "B'#{value}'"
78
- elsif value.hex?
79
- "X'#{value}'"
86
+ def _quote(value)
87
+ case value
88
+ when OID::Xml::Data
89
+ "xml '#{quote_string(value.to_s)}'"
90
+ when OID::Bit::Data
91
+ if value.binary?
92
+ "B'#{value}'"
93
+ elsif value.hex?
94
+ "X'#{value}'"
95
+ end
96
+ when Float
97
+ if value.infinite? || value.nan?
98
+ "'#{value}'"
99
+ else
100
+ super
101
+ end
102
+ when OID::Array::Data
103
+ _quote(encode_array(value))
104
+ when Range
105
+ _quote(encode_range(value))
106
+ else
107
+ super
80
108
  end
81
- when Float
82
- if value.infinite? || value.nan?
83
- "'#{value}'"
109
+ end
110
+
111
+ def _type_cast(value)
112
+ case value
113
+ when Type::Binary::Data
114
+ # Return a bind param hash with format as binary.
115
+ # See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
116
+ # for more information
117
+ { value: value.to_s, format: 1 }
118
+ when OID::Xml::Data, OID::Bit::Data
119
+ value.to_s
120
+ when OID::Array::Data
121
+ encode_array(value)
122
+ when Range
123
+ encode_range(value)
84
124
  else
85
125
  super
86
126
  end
87
- else
88
- super
89
127
  end
90
- end
91
128
 
92
- def _type_cast(value)
93
- case value
94
- when Type::Binary::Data
95
- # Return a bind param hash with format as binary.
96
- # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
97
- # for more information
98
- { value: value.to_s, format: 1 }
99
- when OID::Xml::Data, OID::Bit::Data
100
- value.to_s
101
- else
102
- super
129
+ def encode_array(array_data)
130
+ encoder = array_data.encoder
131
+ values = type_cast_array(array_data.values)
132
+
133
+ result = encoder.encode(values)
134
+ if encoding = determine_encoding_of_strings_in_array(values)
135
+ result.force_encoding(encoding)
136
+ end
137
+ result
138
+ end
139
+
140
+ def encode_range(range)
141
+ "[#{type_cast_range_value(range.first)},#{type_cast_range_value(range.last)}#{range.exclude_end? ? ')' : ']'}"
142
+ end
143
+
144
+ def determine_encoding_of_strings_in_array(value)
145
+ case value
146
+ when ::Array then determine_encoding_of_strings_in_array(value.first)
147
+ when ::String then value.encoding
148
+ end
149
+ end
150
+
151
+ def type_cast_array(values)
152
+ case values
153
+ when ::Array then values.map { |item| type_cast_array(item) }
154
+ else _type_cast(values)
155
+ end
156
+ end
157
+
158
+ def type_cast_range_value(value)
159
+ infinity?(value) ? "" : type_cast(value)
160
+ end
161
+
162
+ def infinity?(value)
163
+ value.respond_to?(:infinite?) && value.infinite?
103
164
  end
104
- end
105
165
  end
106
166
  end
107
167
  end
@@ -1,27 +1,40 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
4
6
  module ReferentialIntegrity # :nodoc:
5
- def supports_disable_referential_integrity? # :nodoc:
6
- true
7
- end
8
-
9
7
  def disable_referential_integrity # :nodoc:
10
- if supports_disable_referential_integrity?
11
- begin
8
+ original_exception = nil
9
+
10
+ begin
11
+ transaction(requires_new: true) do
12
12
  execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
13
- rescue
14
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER USER" }.join(";"))
15
13
  end
14
+ rescue ActiveRecord::ActiveRecordError => e
15
+ original_exception = e
16
+ end
17
+
18
+ begin
19
+ yield
20
+ rescue ActiveRecord::InvalidForeignKey => e
21
+ warn <<-WARNING
22
+ WARNING: Rails was not able to disable referential integrity.
23
+
24
+ This is most likely caused due to missing permissions.
25
+ Rails needs superuser privileges to disable referential integrity.
26
+
27
+ cause: #{original_exception.try(:message)}
28
+
29
+ WARNING
30
+ raise e
16
31
  end
17
- yield
18
- ensure
19
- if supports_disable_referential_integrity?
20
- begin
32
+
33
+ begin
34
+ transaction(requires_new: true) do
21
35
  execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
22
- rescue
23
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER USER" }.join(";"))
24
36
  end
37
+ rescue ActiveRecord::ActiveRecordError
25
38
  end
26
39
  end
27
40
  end