activerecord 4.2.0 → 5.2.8.1

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

Potentially problematic release.


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

Files changed (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +640 -928
  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 +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  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 +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  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 +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -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 -81
  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 +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  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 +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  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 +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  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 +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -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 +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  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 +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  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 -57
  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 +5 -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 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  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 +466 -280
  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 +439 -330
  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 -324
  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 +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  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 +136 -90
  133. data/lib/active_record/errors.rb +180 -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 +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  139. data/lib/active_record/gem_version.rb +5 -3
  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 +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  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 +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -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 +117 -35
  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 +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  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 +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  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 +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  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 -339
  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 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  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 +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  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 +208 -123
  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 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  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 +30 -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 +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  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 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  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.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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,8 +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
12
+ super(options)
10
13
  end
11
14
  end
12
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' }
19
- enums, nodes = nodes.partition { |row| row['typtype'] == 'e' }
20
- domains, nodes = nodes.partition { |row| row['typtype'] == 'd' }
21
- arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
22
- composites, nodes = nodes.partition { |row| row['typelem'] != '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,67 +31,79 @@ module ActiveRecord
29
31
  composites.each { |row| register_composite_type(row) }
30
32
  end
31
33
 
32
- private
33
- def register_mapped_type(row)
34
- alias_type row['oid'], row['typname']
34
+ def query_conditions_for_initial_load
35
+ known_type_names = @store.keys.map { |n| "'#{n}'" }
36
+ known_type_types = %w('r' 'e' 'd')
37
+ <<-SQL % [known_type_names.join(", "), known_type_types.join(", ")]
38
+ WHERE
39
+ t.typname IN (%s)
40
+ OR t.typtype IN (%s)
41
+ OR t.typinput = 'array_in(cstring,oid,integer)'::regprocedure
42
+ OR t.typelem != 0
43
+ SQL
35
44
  end
36
45
 
37
- def register_enum_type(row)
38
- register row['oid'], OID::Enum.new
39
- end
46
+ private
47
+ def register_mapped_type(row)
48
+ alias_type row["oid"], row["typname"]
49
+ end
40
50
 
41
- def register_array_type(row)
42
- register_with_subtype(row['oid'], row['typelem'].to_i) do |subtype|
43
- OID::Array.new(subtype, row['typdelim'])
51
+ def register_enum_type(row)
52
+ register row["oid"], OID::Enum.new
44
53
  end
45
- end
46
54
 
47
- def register_range_type(row)
48
- register_with_subtype(row['oid'], row['rngsubtype'].to_i) do |subtype|
49
- OID::Range.new(subtype, row['typname'].to_sym)
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
50
59
  end
51
- end
52
60
 
53
- def register_domain_type(row)
54
- if base_type = @store.lookup(row["typbasetype"].to_i)
55
- register row['oid'], base_type
56
- else
57
- warn "unknown base type (OID: #{row["typbasetype"]}) for domain #{row["typname"]}."
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
65
+ end
66
+
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
58
73
  end
59
- end
60
74
 
61
- def register_composite_type(row)
62
- if subtype = @store.lookup(row['typelem'].to_i)
63
- 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
64
79
  end
65
- end
66
80
 
67
- def register(oid, oid_type = nil, &block)
68
- oid = assert_valid_registration(oid, oid_type || block)
69
- if block_given?
70
- @store.register_type(oid, &block)
71
- else
72
- @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
73
88
  end
74
- end
75
89
 
76
- def alias_type(oid, target)
77
- oid = assert_valid_registration(oid, target)
78
- @store.alias_type(oid, target)
79
- end
90
+ def alias_type(oid, target)
91
+ oid = assert_valid_registration(oid, target)
92
+ @store.alias_type(oid, target)
93
+ end
80
94
 
81
- def register_with_subtype(oid, target_oid)
82
- if @store.key?(target_oid)
83
- register(oid) do |_, *args|
84
- 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
85
100
  end
86
101
  end
87
- end
88
102
 
89
- def assert_valid_registration(oid, oid_type)
90
- raise ArgumentError, "can't register nil type for OID #{oid}" if oid_type.nil?
91
- oid.to_i
92
- 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
93
107
  end
94
108
  end
95
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