activerecord 4.1.8 → 4.2.11.3

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 (186) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1165 -1591
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +15 -8
  5. data/lib/active_record/association_relation.rb +13 -0
  6. data/lib/active_record/associations/alias_tracker.rb +3 -12
  7. data/lib/active_record/associations/association.rb +16 -4
  8. data/lib/active_record/associations/association_scope.rb +84 -43
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  10. data/lib/active_record/associations/builder/association.rb +16 -5
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -14
  14. data/lib/active_record/associations/builder/has_many.rb +1 -1
  15. data/lib/active_record/associations/builder/has_one.rb +2 -2
  16. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  17. data/lib/active_record/associations/collection_association.rb +87 -30
  18. data/lib/active_record/associations/collection_proxy.rb +33 -35
  19. data/lib/active_record/associations/foreign_association.rb +11 -0
  20. data/lib/active_record/associations/has_many_association.rb +83 -22
  21. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  22. data/lib/active_record/associations/has_one_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  24. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -12
  26. data/lib/active_record/associations/preloader/association.rb +14 -10
  27. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  28. data/lib/active_record/associations/preloader.rb +37 -26
  29. data/lib/active_record/associations/singular_association.rb +17 -2
  30. data/lib/active_record/associations/through_association.rb +16 -12
  31. data/lib/active_record/associations.rb +158 -49
  32. data/lib/active_record/attribute.rb +163 -0
  33. data/lib/active_record/attribute_assignment.rb +20 -12
  34. data/lib/active_record/attribute_decorators.rb +66 -0
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  37. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  38. data/lib/active_record/attribute_methods/query.rb +1 -1
  39. data/lib/active_record/attribute_methods/read.rb +22 -59
  40. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -28
  42. data/lib/active_record/attribute_methods/write.rb +9 -24
  43. data/lib/active_record/attribute_methods.rb +57 -95
  44. data/lib/active_record/attribute_set/builder.rb +106 -0
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attributes.rb +147 -0
  47. data/lib/active_record/autosave_association.rb +30 -12
  48. data/lib/active_record/base.rb +13 -24
  49. data/lib/active_record/callbacks.rb +6 -6
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +85 -53
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +139 -57
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +271 -74
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -60
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +295 -141
  62. data/lib/active_record/connection_adapters/column.rb +29 -240
  63. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  64. data/lib/active_record/connection_adapters/mysql2_adapter.rb +17 -33
  65. data/lib/active_record/connection_adapters/mysql_adapter.rb +68 -145
  66. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  69. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -385
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +134 -43
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  101. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  102. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  103. data/lib/active_record/connection_handling.rb +1 -1
  104. data/lib/active_record/core.rb +163 -40
  105. data/lib/active_record/counter_cache.rb +60 -6
  106. data/lib/active_record/enum.rb +10 -12
  107. data/lib/active_record/errors.rb +53 -30
  108. data/lib/active_record/explain.rb +1 -1
  109. data/lib/active_record/explain_subscriber.rb +1 -1
  110. data/lib/active_record/fixtures.rb +62 -74
  111. data/lib/active_record/gem_version.rb +4 -4
  112. data/lib/active_record/inheritance.rb +35 -10
  113. data/lib/active_record/integration.rb +4 -4
  114. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  115. data/lib/active_record/locking/optimistic.rb +46 -26
  116. data/lib/active_record/migration/command_recorder.rb +19 -2
  117. data/lib/active_record/migration/join_table.rb +1 -1
  118. data/lib/active_record/migration.rb +79 -47
  119. data/lib/active_record/model_schema.rb +52 -58
  120. data/lib/active_record/nested_attributes.rb +18 -8
  121. data/lib/active_record/no_touching.rb +1 -1
  122. data/lib/active_record/persistence.rb +48 -27
  123. data/lib/active_record/query_cache.rb +3 -3
  124. data/lib/active_record/querying.rb +10 -7
  125. data/lib/active_record/railtie.rb +19 -14
  126. data/lib/active_record/railties/databases.rake +55 -56
  127. data/lib/active_record/readonly_attributes.rb +0 -1
  128. data/lib/active_record/reflection.rb +281 -117
  129. data/lib/active_record/relation/batches.rb +0 -1
  130. data/lib/active_record/relation/calculations.rb +41 -37
  131. data/lib/active_record/relation/delegation.rb +1 -1
  132. data/lib/active_record/relation/finder_methods.rb +71 -48
  133. data/lib/active_record/relation/merger.rb +39 -29
  134. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  135. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  136. data/lib/active_record/relation/predicate_builder.rb +42 -12
  137. data/lib/active_record/relation/query_methods.rb +130 -73
  138. data/lib/active_record/relation/spawn_methods.rb +10 -3
  139. data/lib/active_record/relation.rb +57 -25
  140. data/lib/active_record/result.rb +18 -7
  141. data/lib/active_record/sanitization.rb +12 -2
  142. data/lib/active_record/schema.rb +0 -1
  143. data/lib/active_record/schema_dumper.rb +59 -28
  144. data/lib/active_record/schema_migration.rb +5 -4
  145. data/lib/active_record/scoping/default.rb +6 -4
  146. data/lib/active_record/scoping/named.rb +4 -0
  147. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  148. data/lib/active_record/statement_cache.rb +95 -10
  149. data/lib/active_record/store.rb +5 -5
  150. data/lib/active_record/tasks/database_tasks.rb +61 -8
  151. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
  152. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  153. data/lib/active_record/timestamp.rb +9 -7
  154. data/lib/active_record/transactions.rb +54 -28
  155. data/lib/active_record/type/big_integer.rb +13 -0
  156. data/lib/active_record/type/binary.rb +50 -0
  157. data/lib/active_record/type/boolean.rb +31 -0
  158. data/lib/active_record/type/date.rb +50 -0
  159. data/lib/active_record/type/date_time.rb +54 -0
  160. data/lib/active_record/type/decimal.rb +64 -0
  161. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  162. data/lib/active_record/type/decorator.rb +14 -0
  163. data/lib/active_record/type/float.rb +19 -0
  164. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  165. data/lib/active_record/type/integer.rb +59 -0
  166. data/lib/active_record/type/mutable.rb +16 -0
  167. data/lib/active_record/type/numeric.rb +36 -0
  168. data/lib/active_record/type/serialized.rb +62 -0
  169. data/lib/active_record/type/string.rb +40 -0
  170. data/lib/active_record/type/text.rb +11 -0
  171. data/lib/active_record/type/time.rb +26 -0
  172. data/lib/active_record/type/time_value.rb +38 -0
  173. data/lib/active_record/type/type_map.rb +64 -0
  174. data/lib/active_record/type/unsigned_integer.rb +15 -0
  175. data/lib/active_record/type/value.rb +110 -0
  176. data/lib/active_record/type.rb +23 -0
  177. data/lib/active_record/validations/associated.rb +5 -3
  178. data/lib/active_record/validations/presence.rb +5 -3
  179. data/lib/active_record/validations/uniqueness.rb +24 -20
  180. data/lib/active_record/validations.rb +25 -19
  181. data/lib/active_record.rb +5 -0
  182. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  183. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  184. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  185. metadata +66 -11
  186. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Bytea < Type::Binary # :nodoc:
6
+ def type_cast_from_database(value)
7
+ return if value.nil?
8
+ return value.to_s if value.is_a?(Type::Binary::Data)
9
+ PGconn.unescape_bytea(super)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Cidr < Type::Value # :nodoc:
6
+ def type
7
+ :cidr
8
+ end
9
+
10
+ def type_cast_for_schema(value)
11
+ subnet_mask = value.instance_variable_get(:@mask_addr)
12
+
13
+ # If the subnet mask is equal to /32, don't output it
14
+ if subnet_mask == (2**32 - 1)
15
+ "\"#{value}\""
16
+ else
17
+ "\"#{value}/#{subnet_mask.to_s(2).count('1')}\""
18
+ end
19
+ end
20
+
21
+ def type_cast_for_database(value)
22
+ if IPAddr === value
23
+ "#{value}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
24
+ else
25
+ value
26
+ end
27
+ end
28
+
29
+ def cast_value(value)
30
+ if value.nil?
31
+ nil
32
+ elsif String === value
33
+ begin
34
+ IPAddr.new(value)
35
+ rescue ArgumentError
36
+ nil
37
+ end
38
+ else
39
+ value
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Date < Type::Date # :nodoc:
6
+ include Infinity
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class DateTime < Type::DateTime # :nodoc:
6
+ include Infinity
7
+
8
+ def type_cast_for_database(value)
9
+ if has_precision? && value.acts_like?(:time) && value.year <= 0
10
+ bce_year = format("%04d", -value.year + 1)
11
+ super.sub(/^-?\d+/, bce_year) + " BC"
12
+ else
13
+ super
14
+ end
15
+ end
16
+
17
+ def cast_value(value)
18
+ if value.is_a?(::String)
19
+ case value
20
+ when 'infinity' then ::Float::INFINITY
21
+ when '-infinity' then -::Float::INFINITY
22
+ when / BC$/
23
+ astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
24
+ super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
25
+ else
26
+ super
27
+ end
28
+ else
29
+ value
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Decimal < Type::Decimal # :nodoc:
6
+ def infinity(options = {})
7
+ BigDecimal.new("Infinity") * (options[:negative] ? -1 : 1)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Enum < Type::Value # :nodoc:
6
+ def type
7
+ :enum
8
+ end
9
+
10
+ private
11
+
12
+ def cast_value(value)
13
+ value.to_s
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Float < Type::Float # :nodoc:
6
+ include Infinity
7
+
8
+ def cast_value(value)
9
+ case value
10
+ when ::Float then value
11
+ when 'Infinity' then ::Float::INFINITY
12
+ when '-Infinity' then -::Float::INFINITY
13
+ when 'NaN' then ::Float::NAN
14
+ else value.to_f
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,59 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Hstore < Type::Value # :nodoc:
6
+ include Type::Mutable
7
+
8
+ def type
9
+ :hstore
10
+ end
11
+
12
+ def type_cast_from_database(value)
13
+ if value.is_a?(::String)
14
+ ::Hash[value.scan(HstorePair).map { |k, v|
15
+ v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
16
+ k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
17
+ [k, v]
18
+ }]
19
+ else
20
+ value
21
+ end
22
+ end
23
+
24
+ def type_cast_for_database(value)
25
+ if value.is_a?(::Hash)
26
+ value.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(', ')
27
+ else
28
+ value
29
+ end
30
+ end
31
+
32
+ def accessor
33
+ ActiveRecord::Store::StringKeyedHashAccessor
34
+ end
35
+
36
+ private
37
+
38
+ HstorePair = begin
39
+ quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
40
+ unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
41
+ /(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
42
+ end
43
+
44
+ def escape_hstore(value)
45
+ if value.nil?
46
+ 'NULL'
47
+ else
48
+ if value == ""
49
+ '""'
50
+ else
51
+ '"%s"' % value.to_s.gsub(/(["\\])/, '\\\\\1')
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Inet < Cidr # :nodoc:
6
+ def type
7
+ :inet
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ module Infinity # :nodoc:
6
+ def infinity(options = {})
7
+ options[:negative] ? -::Float::INFINITY : ::Float::INFINITY
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Integer < Type::Integer # :nodoc:
6
+ include Infinity
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Json < Type::Value # :nodoc:
6
+ include Type::Mutable
7
+
8
+ def type
9
+ :json
10
+ end
11
+
12
+ def type_cast_from_database(value)
13
+ if value.is_a?(::String)
14
+ ::ActiveSupport::JSON.decode(value) rescue nil
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ def type_cast_for_database(value)
21
+ if value.is_a?(::Array) || value.is_a?(::Hash)
22
+ ::ActiveSupport::JSON.encode(value)
23
+ else
24
+ super
25
+ end
26
+ end
27
+
28
+ def accessor
29
+ ActiveRecord::Store::StringKeyedHashAccessor
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Jsonb < Json # :nodoc:
6
+ def type
7
+ :jsonb
8
+ end
9
+
10
+ def changed_in_place?(raw_old_value, new_value)
11
+ # Postgres does not preserve insignificant whitespaces when
12
+ # roundtripping jsonb columns. This causes some false positives for
13
+ # the comparison here. Therefore, we need to parse and re-dump the
14
+ # raw value here to ensure the insignificant whitespaces are
15
+ # consistent with our encoder's output.
16
+ raw_old_value = type_cast_for_database(type_cast_from_database(raw_old_value))
17
+ super(raw_old_value, new_value)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,43 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Money < Type::Decimal # :nodoc:
6
+ include Infinity
7
+
8
+ class_attribute :precision
9
+
10
+ def type
11
+ :money
12
+ end
13
+
14
+ def scale
15
+ 2
16
+ end
17
+
18
+ def cast_value(value)
19
+ return value unless ::String === value
20
+
21
+ # Because money output is formatted according to the locale, there are two
22
+ # cases to consider (note the decimal separators):
23
+ # (1) $12,345,678.12
24
+ # (2) $12.345.678,12
25
+ # Negative values are represented as follows:
26
+ # (3) -$2.55
27
+ # (4) ($2.55)
28
+
29
+ value.sub!(/^\((.+)\)$/, '-\1') # (4)
30
+ case value
31
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
32
+ value.gsub!(/[^-\d.]/, '')
33
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
34
+ value.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
35
+ end
36
+
37
+ super(value)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Point < Type::Value # :nodoc:
6
+ include Type::Mutable
7
+
8
+ def type
9
+ :point
10
+ end
11
+
12
+ def type_cast(value)
13
+ case value
14
+ when ::String
15
+ if value[0] == '(' && value[-1] == ')'
16
+ value = value[1...-1]
17
+ end
18
+ type_cast(value.split(','))
19
+ when ::Array
20
+ value.map { |v| Float(v) }
21
+ else
22
+ value
23
+ end
24
+ end
25
+
26
+ def type_cast_for_database(value)
27
+ if value.is_a?(::Array)
28
+ "(#{number_for_point(value[0])},#{number_for_point(value[1])})"
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def number_for_point(number)
37
+ number.to_s.gsub(/\.0$/, '')
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,79 @@
1
+ require 'active_support/core_ext/string/filters'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ module OID # :nodoc:
7
+ class Range < Type::Value # :nodoc:
8
+ attr_reader :subtype, :type
9
+
10
+ def initialize(subtype, type)
11
+ @subtype = subtype
12
+ @type = type
13
+ end
14
+
15
+ def type_cast_for_schema(value)
16
+ value.inspect.gsub('Infinity', '::Float::INFINITY')
17
+ end
18
+
19
+ def cast_value(value)
20
+ return if value == 'empty'
21
+ return value if value.is_a?(::Range)
22
+
23
+ extracted = extract_bounds(value)
24
+ from = type_cast_single extracted[:from]
25
+ to = type_cast_single extracted[:to]
26
+
27
+ 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
38
+ end
39
+ ::Range.new(from, to, extracted[:exclude_end])
40
+ end
41
+
42
+ def type_cast_for_database(value)
43
+ if value.is_a?(::Range)
44
+ from = type_cast_single_for_database(value.begin)
45
+ to = type_cast_single_for_database(value.end)
46
+ "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}"
47
+ else
48
+ super
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def type_cast_single(value)
55
+ infinity?(value) ? value : @subtype.type_cast_from_database(value)
56
+ end
57
+
58
+ def type_cast_single_for_database(value)
59
+ infinity?(value) ? '' : @subtype.type_cast_for_database(value)
60
+ end
61
+
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
+ }
70
+ end
71
+
72
+ def infinity?(value)
73
+ value.respond_to?(:infinite?) && value.infinite?
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class SpecializedString < Type::String # :nodoc:
6
+ attr_reader :type
7
+
8
+ def initialize(type)
9
+ @type = type
10
+ end
11
+
12
+ def text?
13
+ false
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Time < Type::Time # :nodoc:
6
+ include Infinity
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,109 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ # This class uses the data from PostgreSQL pg_type table to build
6
+ # the OID -> Type mapping.
7
+ # - OID is an integer representing the type.
8
+ # - Type is an OID::Type object.
9
+ # This class has side effects on the +store+ passed during initialization.
10
+ class TypeMapInitializer # :nodoc:
11
+ def initialize(store)
12
+ @store = store
13
+ end
14
+
15
+ 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 }
23
+
24
+ mapped.each { |row| register_mapped_type(row) }
25
+ enums.each { |row| register_enum_type(row) }
26
+ domains.each { |row| register_domain_type(row) }
27
+ arrays.each { |row| register_array_type(row) }
28
+ ranges.each { |row| register_range_type(row) }
29
+ composites.each { |row| register_composite_type(row) }
30
+ end
31
+
32
+ def query_conditions_for_initial_load(type_map)
33
+ known_type_names = type_map.keys.map { |n| "'#{n}'" }
34
+ known_type_types = %w('r' 'e' 'd')
35
+ <<-SQL % [known_type_names.join(", "), known_type_types.join(", ")]
36
+ WHERE
37
+ t.typname IN (%s)
38
+ OR t.typtype IN (%s)
39
+ OR t.typinput = 'array_in(cstring,oid,integer)'::regprocedure
40
+ OR t.typelem != 0
41
+ SQL
42
+ end
43
+
44
+ private
45
+ def register_mapped_type(row)
46
+ alias_type row['oid'], row['typname']
47
+ end
48
+
49
+ def register_enum_type(row)
50
+ register row['oid'], OID::Enum.new
51
+ end
52
+
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'])
56
+ end
57
+ end
58
+
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)
62
+ end
63
+ end
64
+
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"]}."
70
+ end
71
+ end
72
+
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)
76
+ end
77
+ end
78
+
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)
85
+ end
86
+ end
87
+
88
+ def alias_type(oid, target)
89
+ oid = assert_valid_registration(oid, target)
90
+ @store.alias_type(oid, target)
91
+ end
92
+
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)
97
+ end
98
+ end
99
+ end
100
+
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
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Uuid < Type::Value # :nodoc:
6
+ ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x
7
+
8
+ alias_method :type_cast_for_database, :type_cast_from_database
9
+
10
+ def type
11
+ :uuid
12
+ end
13
+
14
+ def type_cast(value)
15
+ value.to_s[ACCEPTABLE_UUID, 0]
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Vector < Type::Value # :nodoc:
6
+ attr_reader :delim, :subtype
7
+
8
+ # +delim+ corresponds to the `typdelim` column in the pg_types
9
+ # table. +subtype+ is derived from the `typelem` column in the
10
+ # pg_types table.
11
+ def initialize(delim, subtype)
12
+ @delim = delim
13
+ @subtype = subtype
14
+ end
15
+
16
+ # FIXME: this should probably split on +delim+ and use +subtype+
17
+ # to cast the values. Unfortunately, the current Rails behavior
18
+ # is to just return the string.
19
+ def type_cast(value)
20
+ value
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end