torque-postgresql 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +24 -0
- data/lib/torque/postgresql/adapter/database_statements.rb +89 -7
- data/lib/torque/postgresql/adapter/inheritance_statements.rb +297 -0
- data/lib/torque/postgresql/adapter/oid/array.rb +17 -0
- data/lib/torque/postgresql/adapter/oid/box.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/circle.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/composite.rb +116 -0
- data/lib/torque/postgresql/adapter/oid/line.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/lquery.rb +53 -0
- data/lib/torque/postgresql/adapter/oid/ltree.rb +53 -0
- data/lib/torque/postgresql/adapter/oid/segment.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/struct.rb +150 -0
- data/lib/torque/postgresql/adapter/oid/struct_list.rb +52 -0
- data/lib/torque/postgresql/adapter/oid/struct_set.rb +38 -0
- data/lib/torque/postgresql/adapter/quoting.rb +11 -2
- data/lib/torque/postgresql/adapter/schema_definitions.rb +46 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +28 -2
- data/lib/torque/postgresql/adapter/schema_statements.rb +207 -0
- data/lib/torque/postgresql/adapter.rb +2 -5
- data/lib/torque/postgresql/arel/nodes.rb +63 -1
- data/lib/torque/postgresql/arel/visitors.rb +19 -8
- data/lib/torque/postgresql/associations/join_dependency.rb +55 -0
- data/lib/torque/postgresql/associations.rb +3 -0
- data/lib/torque/postgresql/attributes/base.rb +94 -0
- data/lib/torque/postgresql/attributes/composite.rb +102 -0
- data/lib/torque/postgresql/attributes/enum.rb +13 -2
- data/lib/torque/postgresql/attributes/lquery.rb +180 -0
- data/lib/torque/postgresql/attributes/ltree.rb +169 -0
- data/lib/torque/postgresql/attributes/simple_enum.rb +72 -0
- data/lib/torque/postgresql/attributes/struct.rb +137 -0
- data/lib/torque/postgresql/base.rb +17 -14
- data/lib/torque/postgresql/config.rb +81 -14
- data/lib/torque/postgresql/inheritance/expander.rb +66 -0
- data/lib/torque/postgresql/inheritance/record.rb +52 -0
- data/lib/torque/postgresql/inheritance.rb +138 -65
- data/lib/torque/postgresql/migration/command_recorder.rb +76 -0
- data/lib/torque/postgresql/predicate_builder/composite_handler.rb +109 -0
- data/lib/torque/postgresql/predicate_builder/ltree_handler.rb +44 -0
- data/lib/torque/postgresql/predicate_builder/struct_handler.rb +68 -0
- data/lib/torque/postgresql/predicate_builder.rb +26 -0
- data/lib/torque/postgresql/predicate_table.rb +61 -0
- data/lib/torque/postgresql/railtie.rb +35 -0
- data/lib/torque/postgresql/relation/inheritance.rb +146 -28
- data/lib/torque/postgresql/relation/join_series.rb +8 -6
- data/lib/torque/postgresql/relation/merger.rb +23 -7
- data/lib/torque/postgresql/relation.rb +12 -11
- data/lib/torque/postgresql/schema_cache.rb +1 -0
- data/lib/torque/postgresql/validations.rb +29 -0
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql/versioned_commands/command_migration.rb +1 -1
- data/lib/torque/postgresql.rb +6 -0
- data/spec/initialize.rb +20 -2
- data/spec/mocks/cache_query.rb +12 -0
- data/spec/models/author.rb +1 -1
- data/spec/models/comment.rb +2 -0
- data/spec/models/place.rb +2 -0
- data/spec/models/profile.rb +31 -0
- data/spec/schema.rb +34 -4
- data/spec/tests/arel_spec.rb +5 -3
- data/spec/tests/composite_spec.rb +800 -0
- data/spec/tests/ltree_spec.rb +552 -0
- data/spec/tests/struct_spec.rb +968 -0
- data/spec/tests/table_inheritance_spec.rb +1027 -56
- metadata +49 -7
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Torque
|
|
4
|
+
module PostgreSQL
|
|
5
|
+
module Adapter
|
|
6
|
+
module OID
|
|
7
|
+
class Lquery < ActiveModel::Type::Value
|
|
8
|
+
|
|
9
|
+
def type
|
|
10
|
+
:lquery
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def cast(value)
|
|
14
|
+
return if value.blank?
|
|
15
|
+
return value if value.is_a?(LQuery)
|
|
16
|
+
|
|
17
|
+
LQuery.new(value)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def deserialize(value)
|
|
21
|
+
return if value.nil?
|
|
22
|
+
|
|
23
|
+
LQuery.load(value)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def serialize(value)
|
|
27
|
+
cast(value)&.to_s
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def type_cast_for_schema(value)
|
|
31
|
+
cast(value).to_s.inspect
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def changed_in_place?(raw_old_value, new_value)
|
|
35
|
+
raw_old_value != serialize(new_value)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Types resolved through the type map are deduplicated and frozen, so
|
|
39
|
+
# they need to be comparable
|
|
40
|
+
def ==(other)
|
|
41
|
+
other.is_a?(self.class)
|
|
42
|
+
end
|
|
43
|
+
alias eql? ==
|
|
44
|
+
|
|
45
|
+
def hash
|
|
46
|
+
self.class.hash
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Torque
|
|
4
|
+
module PostgreSQL
|
|
5
|
+
module Adapter
|
|
6
|
+
module OID
|
|
7
|
+
class Ltree < ActiveModel::Type::Value
|
|
8
|
+
|
|
9
|
+
def type
|
|
10
|
+
:ltree
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def cast(value)
|
|
14
|
+
return if value.blank?
|
|
15
|
+
return value if value.is_a?(LTree)
|
|
16
|
+
|
|
17
|
+
LTree.new(value)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def deserialize(value)
|
|
21
|
+
return if value.nil?
|
|
22
|
+
|
|
23
|
+
LTree.load(value)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def serialize(value)
|
|
27
|
+
cast(value)&.to_s
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def type_cast_for_schema(value)
|
|
31
|
+
cast(value).to_s.inspect
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def changed_in_place?(raw_old_value, new_value)
|
|
35
|
+
raw_old_value != serialize(new_value)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Types resolved through the type map are deduplicated and frozen, so
|
|
39
|
+
# they need to be comparable
|
|
40
|
+
def ==(other)
|
|
41
|
+
other.is_a?(self.class)
|
|
42
|
+
end
|
|
43
|
+
alias eql? ==
|
|
44
|
+
|
|
45
|
+
def hash
|
|
46
|
+
self.class.hash
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Torque
|
|
4
|
+
module PostgreSQL
|
|
5
|
+
module Adapter
|
|
6
|
+
module OID
|
|
7
|
+
class Struct < ActiveRecord::Type::Json
|
|
8
|
+
attr_reader :klass, :type, :backfill
|
|
9
|
+
|
|
10
|
+
# A comparable representation of a struct-backed value, based on the
|
|
11
|
+
# values it exposes instead of the document it generates, so that
|
|
12
|
+
# non-deterministic types like encrypted attributes stay stable
|
|
13
|
+
def self.state(value)
|
|
14
|
+
case value
|
|
15
|
+
when nil then nil
|
|
16
|
+
when ::Array then value.map { |item| state(item) }
|
|
17
|
+
else value.respond_to?(:attributes) ? [value.class, value.attributes] : value
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(klass, type: :jsonb, backfill: nil)
|
|
22
|
+
super()
|
|
23
|
+
@klass = klass
|
|
24
|
+
@type = type
|
|
25
|
+
@backfill = backfill
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def cast(value)
|
|
29
|
+
case value
|
|
30
|
+
when klass then value
|
|
31
|
+
when ::Hash then build(value)
|
|
32
|
+
when ::String then cast(parse(value))
|
|
33
|
+
else value
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def deserialize(value)
|
|
38
|
+
build(parse(value), from_database: true, blank: true)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def serialize(value)
|
|
42
|
+
case value
|
|
43
|
+
when klass then empty?(value) ? nil : super(document(value))
|
|
44
|
+
when nil then nil
|
|
45
|
+
else super
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def changed?(old_value, new_value, _new_value_before_cast)
|
|
50
|
+
Struct.state(old_value) != Struct.state(new_value)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def changed_in_place?(raw_old_value, new_value)
|
|
54
|
+
Struct.state(without_backfill.deserialize(raw_old_value)) != Struct.state(new_value)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The same type, but reading documents exactly as they are stored, so
|
|
58
|
+
# that backfilled properties are seen as changes
|
|
59
|
+
def without_backfill
|
|
60
|
+
return self if backfill.nil?
|
|
61
|
+
@without_backfill ||= self.class.new(klass, type: type)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def empty?(value)
|
|
65
|
+
document(value).empty?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The value as it goes inside another document, which is a hash, and
|
|
69
|
+
# not the encoded JSON that a column receives
|
|
70
|
+
def document_for(value)
|
|
71
|
+
value.nil? ? nil : document(value)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The hash that represents the value on the database, holding only the
|
|
75
|
+
# properties that were actually written to it. Properties backed by
|
|
76
|
+
# another struct are kept as documents, so that they are not stored as
|
|
77
|
+
# an encoded string inside this one
|
|
78
|
+
def document(value)
|
|
79
|
+
set = value.instance_variable_get(:@attributes)
|
|
80
|
+
set.keys.to_h do |name|
|
|
81
|
+
attribute = set[name]
|
|
82
|
+
type = attribute.type
|
|
83
|
+
|
|
84
|
+
next [name, type.document_for(attribute.value)] if type.is_a?(Struct)
|
|
85
|
+
[name, attribute.value_for_database]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def blank_document
|
|
90
|
+
{}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def ==(other)
|
|
94
|
+
other.class == self.class && other.klass == klass && other.type == type
|
|
95
|
+
end
|
|
96
|
+
alias eql? ==
|
|
97
|
+
|
|
98
|
+
def hash
|
|
99
|
+
[self.class, klass, type].hash
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def parse(value)
|
|
105
|
+
return value unless value.is_a?(::String)
|
|
106
|
+
ActiveSupport::JSON.decode(value) rescue nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def build(data, from_database: false, blank: false)
|
|
110
|
+
instance = klass.new
|
|
111
|
+
blank!(instance) if blank
|
|
112
|
+
return instance unless data.is_a?(::Hash)
|
|
113
|
+
|
|
114
|
+
from_database ? load(instance, data) : instance.assign_attributes(data)
|
|
115
|
+
instance
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Reset the instance to the state of an empty document, keeping the
|
|
119
|
+
# class-level defaults that were marked to be backfilled
|
|
120
|
+
def blank!(instance)
|
|
121
|
+
return if backfill == true
|
|
122
|
+
|
|
123
|
+
keep = ::Array.wrap(backfill).map(&:to_s)
|
|
124
|
+
set = instance.instance_variable_get(:@attributes).map do |attribute|
|
|
125
|
+
next attribute if keep.include?(attribute.name)
|
|
126
|
+
ActiveModel::Attribute.uninitialized(attribute.name, attribute.type)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
instance.instance_variable_set(:@attributes, set)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Properties that the class doesn't declare are kept as they are
|
|
133
|
+
# stored, regardless of the class being strict about them or not
|
|
134
|
+
def load(instance, data)
|
|
135
|
+
default = ActiveModel::Type.default_value
|
|
136
|
+
set = instance.instance_variable_get(:@attributes)
|
|
137
|
+
|
|
138
|
+
data.each do |key, value|
|
|
139
|
+
if klass.attribute_names.include?(key = key.to_s)
|
|
140
|
+
set.write_from_database(key, value)
|
|
141
|
+
else
|
|
142
|
+
set[key] = ActiveModel::Attribute.from_database(key, value, default)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Torque
|
|
4
|
+
module PostgreSQL
|
|
5
|
+
module Adapter
|
|
6
|
+
module OID
|
|
7
|
+
class StructList < Struct
|
|
8
|
+
def cast(value)
|
|
9
|
+
case value
|
|
10
|
+
when ::String then cast(parse(value))
|
|
11
|
+
when ::Array then value.map { |item| super(item) }
|
|
12
|
+
else value
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def deserialize(value)
|
|
17
|
+
::Array.wrap(parse(value)).map do |item|
|
|
18
|
+
build(item, from_database: true, blank: true)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def serialize(value)
|
|
23
|
+
case value
|
|
24
|
+
when ::Array then value.empty? ? nil : encode(value)
|
|
25
|
+
when nil then nil
|
|
26
|
+
else super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def document_for(value)
|
|
31
|
+
::Array.wrap(value).map { |item| item.is_a?(klass) ? document(item) : item }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def empty?(value)
|
|
35
|
+
value.is_a?(::Array) ? value.empty? : super
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def blank_document
|
|
39
|
+
[]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def encode(items)
|
|
45
|
+
hashes = items.map { |item| item.is_a?(klass) ? document(item) : item }
|
|
46
|
+
ActiveSupport::JSON.encode(hashes)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Torque
|
|
4
|
+
module PostgreSQL
|
|
5
|
+
module Adapter
|
|
6
|
+
module OID
|
|
7
|
+
class StructSet < ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array
|
|
8
|
+
def deserialize(value)
|
|
9
|
+
value.nil? ? [] : super
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def serialize(value)
|
|
13
|
+
return if value.is_a?(::Array) && value.empty?
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def changed_in_place?(raw_old_value, new_value)
|
|
18
|
+
Struct.state(without_backfill.deserialize(raw_old_value)) !=
|
|
19
|
+
Struct.state(new_value)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def without_backfill
|
|
23
|
+
return self if subtype.backfill.nil?
|
|
24
|
+
@without_backfill ||= self.class.new(subtype.without_backfill, delimiter)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def empty?(value)
|
|
28
|
+
value.is_a?(::Array) ? value.empty? : subtype.empty?(value)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def blank_document
|
|
32
|
+
[]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -13,7 +13,7 @@ module Torque
|
|
|
13
13
|
|
|
14
14
|
# Quotes type names for use in SQL queries.
|
|
15
15
|
def quote_type_name(name, *args)
|
|
16
|
-
QUOTED_TYPE_NAMES[args] ||= begin
|
|
16
|
+
QUOTED_TYPE_NAMES[[name.to_s, *args]] ||= begin
|
|
17
17
|
name = name.to_s
|
|
18
18
|
args << 'public' if args.empty? && !name.include?('.')
|
|
19
19
|
quote_identifier_name(name, *args)
|
|
@@ -36,11 +36,20 @@ module Torque
|
|
|
36
36
|
lookup_cast_type(column.sql_type)
|
|
37
37
|
elsif column.is_a?(Column) && column.array?
|
|
38
38
|
# When using +change_column_default+
|
|
39
|
-
|
|
39
|
+
lookup_cast_type_for_column(column)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
type.nil? ? super : quote(type.serialize(value.to_a))
|
|
43
43
|
end
|
|
44
|
+
|
|
45
|
+
# Rails 8.1 dropped +lookup_cast_type_from_column+, and the sql_type of
|
|
46
|
+
# an array column does not carry the array part, so only the oid can
|
|
47
|
+
# resolve the real type
|
|
48
|
+
def lookup_cast_type_for_column(column)
|
|
49
|
+
return lookup_cast_type_from_column(column) unless AR810
|
|
50
|
+
|
|
51
|
+
type_map.lookup(column.oid, column.fmod, column.sql_type)
|
|
52
|
+
end
|
|
44
53
|
end
|
|
45
54
|
end
|
|
46
55
|
end
|
|
@@ -18,6 +18,26 @@ module Torque
|
|
|
18
18
|
names.each { |name| column(name, :virtual, **options) }
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
# Add a composite type column to the table
|
|
22
|
+
def composite(*names, composite_type:, **options)
|
|
23
|
+
raise ArgumentError, "Missing column name(s) for composite" if names.empty?
|
|
24
|
+
names.each { |name| column(name, :composite, composite_type: composite_type, **options) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Add a label tree column to the table. Tree operators are only indexed
|
|
28
|
+
# by GiST, as in +index: { using: :gist }+, whose default operator class
|
|
29
|
+
# is already the right one for both a path and an array of paths
|
|
30
|
+
def ltree(*names, **options)
|
|
31
|
+
raise ArgumentError, "Missing column name(s) for ltree" if names.empty?
|
|
32
|
+
names.each { |name| column(name, :ltree, **options) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Add a label tree pattern column to the table
|
|
36
|
+
def lquery(*names, **options)
|
|
37
|
+
raise ArgumentError, "Missing column name(s) for lquery" if names.empty?
|
|
38
|
+
names.each { |name| column(name, :lquery, **options) }
|
|
39
|
+
end
|
|
40
|
+
|
|
21
41
|
end
|
|
22
42
|
|
|
23
43
|
module TableDefinition
|
|
@@ -49,6 +69,32 @@ module Torque
|
|
|
49
69
|
end
|
|
50
70
|
end
|
|
51
71
|
|
|
72
|
+
# Composite types are defined with the same DSL as tables, where declaring
|
|
73
|
+
# a column adds one, plus the operations that +change_table+ provides
|
|
74
|
+
class CompositeTypeDefinition < ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition
|
|
75
|
+
attr_reader :changes, :removals, :renames
|
|
76
|
+
|
|
77
|
+
def initialize(*args, **xargs)
|
|
78
|
+
super
|
|
79
|
+
|
|
80
|
+
@changes = []
|
|
81
|
+
@removals = []
|
|
82
|
+
@renames = []
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def change(name, type, **options)
|
|
86
|
+
@changes << new_column_definition(name, type, **options)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def remove(*names)
|
|
90
|
+
@removals.concat(names.map(&:to_s))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def rename(name, new_name)
|
|
94
|
+
@renames << [name.to_s, new_name.to_s]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
52
98
|
# Add exclusive support for versioned commands when importing from schema
|
|
53
99
|
# dump. This ensures that such methods are not available in regular
|
|
54
100
|
# migrations.
|
|
@@ -34,10 +34,35 @@ module Torque
|
|
|
34
34
|
def types(stream) # :nodoc:
|
|
35
35
|
super
|
|
36
36
|
|
|
37
|
+
composite_types(stream)
|
|
38
|
+
|
|
37
39
|
versioned_commands(stream, :type)
|
|
38
40
|
versioned_commands(stream, :function)
|
|
39
41
|
end
|
|
40
42
|
|
|
43
|
+
# Dump user defined composite types, sorted by their dependencies
|
|
44
|
+
def composite_types(stream)
|
|
45
|
+
return unless PostgreSQL.config.composite.enabled
|
|
46
|
+
|
|
47
|
+
list = @connection.composite_types
|
|
48
|
+
list -= @versioned_commands.versions_of('type').map(&:first) \
|
|
49
|
+
if with_versioned_commands?
|
|
50
|
+
return if list.empty?
|
|
51
|
+
|
|
52
|
+
stream.puts " # Custom composite types defined in this database."
|
|
53
|
+
list.each do |name|
|
|
54
|
+
stream.puts " create_composite_type #{name.inspect}, force: :cascade do |t|"
|
|
55
|
+
@connection.columns(name).each do |column|
|
|
56
|
+
type, colspec = column_spec(column)
|
|
57
|
+
stream.print " t.#{type} #{column.name.inspect}"
|
|
58
|
+
stream.print ", #{format_colspec(colspec)}" if colspec.present?
|
|
59
|
+
stream.puts
|
|
60
|
+
end
|
|
61
|
+
stream.puts " end"
|
|
62
|
+
stream.puts
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
41
66
|
def tables(stream) # :nodoc:
|
|
42
67
|
around_tables(stream) { dump_tables(stream) }
|
|
43
68
|
end
|
|
@@ -126,13 +151,14 @@ module Torque
|
|
|
126
151
|
def prepare_column_options(column)
|
|
127
152
|
options = super
|
|
128
153
|
parse_search_vector_options(column, options) if column.type == :tsvector
|
|
154
|
+
options[:composite_type] = column.sql_type.inspect if column.type == :composite
|
|
129
155
|
options
|
|
130
156
|
end
|
|
131
157
|
|
|
132
158
|
# Parse the search vector operation into a readable format
|
|
133
159
|
def parse_search_vector_options(column, options)
|
|
134
|
-
settings = options[:as]
|
|
135
|
-
return if settings.
|
|
160
|
+
settings = options[:as]&.scan(SEARCH_VECTOR_SCANNER)
|
|
161
|
+
return if settings.blank?
|
|
136
162
|
|
|
137
163
|
languages = settings.map(&:shift).uniq
|
|
138
164
|
return if languages.many?
|