activerecord 4.1.16 → 4.2.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +634 -2185
- data/README.rdoc +15 -10
- data/lib/active_record.rb +2 -1
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/attribute.rb +131 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- data/lib/active_record/migration.rb +22 -32
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +51 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record/validations/uniqueness.rb +9 -23
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +71 -14
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -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,76 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module OID # :nodoc:
|
5
|
+
class Range < Type::Value # :nodoc:
|
6
|
+
attr_reader :subtype, :type
|
7
|
+
|
8
|
+
def initialize(subtype, type)
|
9
|
+
@subtype = subtype
|
10
|
+
@type = type
|
11
|
+
end
|
12
|
+
|
13
|
+
def type_cast_for_schema(value)
|
14
|
+
value.inspect.gsub('Infinity', '::Float::INFINITY')
|
15
|
+
end
|
16
|
+
|
17
|
+
def cast_value(value)
|
18
|
+
return if value == 'empty'
|
19
|
+
return value if value.is_a?(::Range)
|
20
|
+
|
21
|
+
extracted = extract_bounds(value)
|
22
|
+
from = type_cast_single extracted[:from]
|
23
|
+
to = type_cast_single extracted[:to]
|
24
|
+
|
25
|
+
if !infinity?(from) && extracted[:exclude_start]
|
26
|
+
if from.respond_to?(:succ)
|
27
|
+
from = from.succ
|
28
|
+
ActiveSupport::Deprecation.warn <<-MESSAGE
|
29
|
+
Excluding the beginning of a Range is only partialy supported through `#succ`.
|
30
|
+
This is not reliable and will be removed in the future.
|
31
|
+
MESSAGE
|
32
|
+
else
|
33
|
+
raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
::Range.new(from, to, extracted[:exclude_end])
|
37
|
+
end
|
38
|
+
|
39
|
+
def type_cast_for_database(value)
|
40
|
+
if value.is_a?(::Range)
|
41
|
+
from = type_cast_single_for_database(value.begin)
|
42
|
+
to = type_cast_single_for_database(value.end)
|
43
|
+
"[#{from},#{to}#{value.exclude_end? ? ')' : ']'}"
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def type_cast_single(value)
|
52
|
+
infinity?(value) ? value : @subtype.type_cast_from_database(value)
|
53
|
+
end
|
54
|
+
|
55
|
+
def type_cast_single_for_database(value)
|
56
|
+
infinity?(value) ? '' : @subtype.type_cast_for_database(value)
|
57
|
+
end
|
58
|
+
|
59
|
+
def extract_bounds(value)
|
60
|
+
from, to = value[1..-2].split(',')
|
61
|
+
{
|
62
|
+
from: (value[1] == ',' || from == '-infinity') ? @subtype.infinity(negative: true) : from,
|
63
|
+
to: (value[-2] == ',' || to == 'infinity') ? @subtype.infinity : to,
|
64
|
+
exclude_start: (value[0] == '('),
|
65
|
+
exclude_end: (value[-1] == ')')
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def infinity?(value)
|
70
|
+
value.respond_to?(:infinite?) && value.infinite?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,85 @@
|
|
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 and 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' }
|
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' }
|
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
|
+
private
|
33
|
+
def register_mapped_type(row)
|
34
|
+
alias_type row['oid'], row['typname']
|
35
|
+
end
|
36
|
+
|
37
|
+
def register_enum_type(row)
|
38
|
+
register row['oid'], OID::Enum.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def register_array_type(row)
|
42
|
+
if subtype = @store.lookup(row['typelem'].to_i)
|
43
|
+
register row['oid'], OID::Array.new(subtype, row['typdelim'])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def register_range_type(row)
|
48
|
+
if subtype = @store.lookup(row['rngsubtype'].to_i)
|
49
|
+
register row['oid'], OID::Range.new(subtype, row['typname'].to_sym)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
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"]}."
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
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)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def register(oid, oid_type)
|
68
|
+
oid = assert_valid_registration(oid, oid_type)
|
69
|
+
@store.register_type(oid, oid_type)
|
70
|
+
end
|
71
|
+
|
72
|
+
def alias_type(oid, target)
|
73
|
+
oid = assert_valid_registration(oid, target)
|
74
|
+
@store.alias_type(oid, target)
|
75
|
+
end
|
76
|
+
|
77
|
+
def assert_valid_registration(oid, oid_type)
|
78
|
+
raise ArgumentError, "can't register nil type for OID #{oid}" if oid_type.nil?
|
79
|
+
oid.to_i
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module OID # :nodoc:
|
5
|
+
class Uuid < Type::Value # :nodoc:
|
6
|
+
RFC_4122 = %r{\A\{?[a-fA-F0-9]{4}-?
|
7
|
+
[a-fA-F0-9]{4}-?
|
8
|
+
[a-fA-F0-9]{4}-?
|
9
|
+
[1-5][a-fA-F0-9]{3}-?
|
10
|
+
[8-Bab][a-fA-F0-9]{3}-?
|
11
|
+
[a-fA-F0-9]{4}-?
|
12
|
+
[a-fA-F0-9]{4}-?
|
13
|
+
[a-fA-F0-9]{4}-?\}?\z}x
|
14
|
+
|
15
|
+
def type
|
16
|
+
:uuid
|
17
|
+
end
|
18
|
+
|
19
|
+
def type_cast(value)
|
20
|
+
value.to_s[RFC_4122, 0]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
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
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module OID # :nodoc:
|
5
|
+
class Xml < Type::String # :nodoc:
|
6
|
+
def type
|
7
|
+
:xml
|
8
|
+
end
|
9
|
+
|
10
|
+
def type_cast_for_database(value)
|
11
|
+
return unless value
|
12
|
+
Data.new(super)
|
13
|
+
end
|
14
|
+
|
15
|
+
class Data # :nodoc:
|
16
|
+
def initialize(value)
|
17
|
+
@value = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
@value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,142 +1,35 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
|
-
|
3
|
+
module PostgreSQL
|
4
4
|
module Quoting
|
5
5
|
# Escapes binary strings for bytea input to the database.
|
6
6
|
def escape_bytea(value)
|
7
|
-
|
7
|
+
@connection.escape_bytea(value) if value
|
8
8
|
end
|
9
9
|
|
10
10
|
# Unescapes bytea output from a database to the binary string it represents.
|
11
11
|
# NOTE: This is NOT an inverse of escape_bytea! This is only to be used
|
12
12
|
# on escaped binary output from database drive.
|
13
13
|
def unescape_bytea(value)
|
14
|
-
|
14
|
+
@connection.unescape_bytea(value) if value
|
15
15
|
end
|
16
16
|
|
17
17
|
# Quotes PostgreSQL-specific data types for SQL input.
|
18
18
|
def quote(value, column = nil) #:nodoc:
|
19
|
-
return super unless column
|
20
|
-
|
21
|
-
sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale)
|
19
|
+
return super unless column
|
22
20
|
|
23
21
|
case value
|
24
|
-
when Range
|
25
|
-
if /range$/ =~ sql_type
|
26
|
-
escaped = quote_string(PostgreSQLColumn.range_to_string(value))
|
27
|
-
"'#{escaped}'::#{sql_type}"
|
28
|
-
else
|
29
|
-
super
|
30
|
-
end
|
31
|
-
when Array
|
32
|
-
case sql_type
|
33
|
-
when 'point' then super(PostgreSQLColumn.point_to_string(value))
|
34
|
-
when 'json' then super(PostgreSQLColumn.json_to_string(value))
|
35
|
-
else
|
36
|
-
if column.array
|
37
|
-
"'#{PostgreSQLColumn.array_to_string(value, column, self).gsub(/'/, "''")}'"
|
38
|
-
else
|
39
|
-
super
|
40
|
-
end
|
41
|
-
end
|
42
|
-
when Hash
|
43
|
-
case sql_type
|
44
|
-
when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
|
45
|
-
when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
|
46
|
-
else super
|
47
|
-
end
|
48
|
-
when IPAddr
|
49
|
-
case sql_type
|
50
|
-
when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
|
51
|
-
else super
|
52
|
-
end
|
53
22
|
when Float
|
54
|
-
if value.infinite?
|
55
|
-
"'#{value.to_s.downcase}'"
|
56
|
-
elsif value.infinite? || value.nan?
|
23
|
+
if value.infinite? || value.nan?
|
57
24
|
"'#{value.to_s}'"
|
58
25
|
else
|
59
26
|
super
|
60
27
|
end
|
61
|
-
when Numeric
|
62
|
-
if sql_type == 'money' || [:string, :text].include?(column.type)
|
63
|
-
# Not truly string input, so doesn't require (or allow) escape string syntax.
|
64
|
-
"'#{value}'"
|
65
|
-
else
|
66
|
-
super
|
67
|
-
end
|
68
|
-
when String
|
69
|
-
case sql_type
|
70
|
-
when 'bytea' then "'#{escape_bytea(value)}'"
|
71
|
-
when 'xml' then "xml '#{quote_string(value)}'"
|
72
|
-
when /^bit/
|
73
|
-
case value
|
74
|
-
when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation
|
75
|
-
when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation
|
76
|
-
end
|
77
|
-
else
|
78
|
-
super
|
79
|
-
end
|
80
28
|
else
|
81
29
|
super
|
82
30
|
end
|
83
31
|
end
|
84
32
|
|
85
|
-
def type_cast(value, column, array_member = false)
|
86
|
-
return super(value, column) unless column
|
87
|
-
|
88
|
-
case value
|
89
|
-
when Range
|
90
|
-
if /range$/ =~ column.sql_type
|
91
|
-
PostgreSQLColumn.range_to_string(value)
|
92
|
-
else
|
93
|
-
super(value, column)
|
94
|
-
end
|
95
|
-
when NilClass
|
96
|
-
if column.array && array_member
|
97
|
-
'NULL'
|
98
|
-
elsif column.array
|
99
|
-
value
|
100
|
-
else
|
101
|
-
super(value, column)
|
102
|
-
end
|
103
|
-
when Array
|
104
|
-
case column.sql_type
|
105
|
-
when 'point' then PostgreSQLColumn.point_to_string(value)
|
106
|
-
when 'json' then PostgreSQLColumn.json_to_string(value)
|
107
|
-
else
|
108
|
-
if column.array
|
109
|
-
PostgreSQLColumn.array_to_string(value, column, self)
|
110
|
-
else
|
111
|
-
super(value, column)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
when String
|
115
|
-
if 'bytea' == column.sql_type
|
116
|
-
# Return a bind param hash with format as binary.
|
117
|
-
# See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
|
118
|
-
# for more information
|
119
|
-
{ value: value, format: 1 }
|
120
|
-
else
|
121
|
-
super(value, column)
|
122
|
-
end
|
123
|
-
when Hash
|
124
|
-
case column.sql_type
|
125
|
-
when 'hstore' then PostgreSQLColumn.hstore_to_string(value, array_member)
|
126
|
-
when 'json' then PostgreSQLColumn.json_to_string(value)
|
127
|
-
else super(value, column)
|
128
|
-
end
|
129
|
-
when IPAddr
|
130
|
-
if %w(inet cidr).include? column.sql_type
|
131
|
-
PostgreSQLColumn.cidr_to_string(value)
|
132
|
-
else
|
133
|
-
super(value, column)
|
134
|
-
end
|
135
|
-
else
|
136
|
-
super(value, column)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
33
|
# Quotes strings for use in SQL input.
|
141
34
|
def quote_string(s) #:nodoc:
|
142
35
|
@connection.escape(s)
|
@@ -151,14 +44,7 @@ module ActiveRecord
|
|
151
44
|
# - "schema.name".table_name
|
152
45
|
# - "schema.name"."table.name"
|
153
46
|
def quote_table_name(name)
|
154
|
-
|
155
|
-
|
156
|
-
unless name_part
|
157
|
-
quote_column_name(schema)
|
158
|
-
else
|
159
|
-
table_name, name_part = extract_pg_identifier_from_name(name_part)
|
160
|
-
"#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
|
161
|
-
end
|
47
|
+
Utils.extract_schema_qualified_name(name.to_s).quoted
|
162
48
|
end
|
163
49
|
|
164
50
|
def quote_table_name_for_assignment(table, attr)
|
@@ -178,8 +64,9 @@ module ActiveRecord
|
|
178
64
|
result = "#{result}.#{sprintf("%06d", value.usec)}"
|
179
65
|
end
|
180
66
|
|
181
|
-
if value.year
|
182
|
-
|
67
|
+
if value.year <= 0
|
68
|
+
bce_year = format("%04d", -value.year + 1)
|
69
|
+
result = result.sub(/^-?\d+/, bce_year) + " BC"
|
183
70
|
end
|
184
71
|
result
|
185
72
|
end
|
@@ -192,6 +79,39 @@ module ActiveRecord
|
|
192
79
|
quote(value, column)
|
193
80
|
end
|
194
81
|
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def _quote(value)
|
86
|
+
case value
|
87
|
+
when Type::Binary::Data
|
88
|
+
"'#{escape_bytea(value.to_s)}'"
|
89
|
+
when OID::Xml::Data
|
90
|
+
"xml '#{quote_string(value.to_s)}'"
|
91
|
+
when OID::Bit::Data
|
92
|
+
if value.binary?
|
93
|
+
"B'#{value}'"
|
94
|
+
elsif value.hex?
|
95
|
+
"X'#{value}'"
|
96
|
+
end
|
97
|
+
else
|
98
|
+
super
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def _type_cast(value)
|
103
|
+
case value
|
104
|
+
when Type::Binary::Data
|
105
|
+
# Return a bind param hash with format as binary.
|
106
|
+
# See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
|
107
|
+
# for more information
|
108
|
+
{ value: value.to_s, format: 1 }
|
109
|
+
when OID::Xml::Data, OID::Bit::Data
|
110
|
+
value.to_s
|
111
|
+
else
|
112
|
+
super
|
113
|
+
end
|
114
|
+
end
|
195
115
|
end
|
196
116
|
end
|
197
117
|
end
|