torque-postgresql 3.4.1 → 4.0.0.rc1
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/lib/torque/postgresql/adapter/database_statements.rb +63 -84
- data/lib/torque/postgresql/adapter/oid/array.rb +17 -0
- data/lib/torque/postgresql/adapter/oid/line.rb +2 -6
- data/lib/torque/postgresql/adapter/oid/range.rb +4 -4
- data/lib/torque/postgresql/adapter/oid.rb +1 -23
- data/lib/torque/postgresql/adapter/quoting.rb +13 -7
- data/lib/torque/postgresql/adapter/schema_creation.rb +7 -28
- data/lib/torque/postgresql/adapter/schema_definitions.rb +36 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +90 -34
- data/lib/torque/postgresql/adapter/schema_overrides.rb +45 -0
- data/lib/torque/postgresql/adapter/schema_statements.rb +64 -49
- data/lib/torque/postgresql/arel/infix_operation.rb +15 -28
- data/lib/torque/postgresql/arel/nodes.rb +2 -2
- data/lib/torque/postgresql/arel/operations.rb +7 -1
- data/lib/torque/postgresql/arel/visitors.rb +3 -9
- data/lib/torque/postgresql/associations/association_scope.rb +23 -31
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +25 -0
- data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +16 -0
- data/lib/torque/postgresql/attributes/builder/enum.rb +12 -9
- data/lib/torque/postgresql/attributes/builder/full_text_search.rb +121 -0
- data/lib/torque/postgresql/attributes/builder/period.rb +21 -21
- data/lib/torque/postgresql/attributes/builder.rb +49 -11
- data/lib/torque/postgresql/attributes/enum.rb +7 -7
- data/lib/torque/postgresql/attributes/enum_set.rb +7 -7
- data/lib/torque/postgresql/attributes/full_text_search.rb +19 -0
- data/lib/torque/postgresql/attributes/period.rb +2 -2
- data/lib/torque/postgresql/attributes.rb +0 -4
- data/lib/torque/postgresql/auxiliary_statement/recursive.rb +3 -3
- data/lib/torque/postgresql/base.rb +3 -10
- data/lib/torque/postgresql/collector.rb +1 -1
- data/lib/torque/postgresql/config.rb +95 -5
- data/lib/torque/postgresql/function.rb +61 -0
- data/lib/torque/postgresql/inheritance.rb +52 -36
- data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +33 -0
- data/lib/torque/postgresql/predicate_builder/array_handler.rb +47 -0
- data/lib/torque/postgresql/predicate_builder/enumerator_lazy_handler.rb +37 -0
- data/lib/torque/postgresql/predicate_builder/regexp_handler.rb +21 -0
- data/lib/torque/postgresql/predicate_builder.rb +35 -0
- data/lib/torque/postgresql/railtie.rb +112 -30
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +12 -44
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -0
- data/lib/torque/postgresql/reflection/has_many_reflection.rb +4 -0
- data/lib/torque/postgresql/reflection/runtime_reflection.rb +1 -1
- data/lib/torque/postgresql/relation/inheritance.rb +4 -7
- data/lib/torque/postgresql/relation.rb +6 -10
- data/lib/torque/postgresql/schema_cache.rb +6 -12
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql.rb +2 -1
- data/spec/initialize.rb +58 -0
- data/spec/mocks/cache_query.rb +21 -21
- data/spec/mocks/create_table.rb +6 -26
- data/spec/schema.rb +19 -12
- data/spec/spec_helper.rb +5 -1
- data/spec/tests/arel_spec.rb +32 -7
- data/spec/tests/auxiliary_statement_spec.rb +3 -3
- data/spec/tests/belongs_to_many_spec.rb +72 -5
- data/spec/tests/enum_set_spec.rb +12 -11
- data/spec/tests/enum_spec.rb +4 -2
- data/spec/tests/full_text_seach_test.rb +252 -0
- data/spec/tests/function_spec.rb +42 -0
- data/spec/tests/has_many_spec.rb +21 -8
- data/spec/tests/interval_spec.rb +1 -7
- data/spec/tests/period_spec.rb +61 -61
- data/spec/tests/predicate_builder_spec.rb +132 -0
- data/spec/tests/schema_spec.rb +2 -8
- data/spec/tests/table_inheritance_spec.rb +25 -26
- metadata +34 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a341f61802bc246d4004168a49fc5f67156b7dc3c962321a28e73fc8288e0db
|
4
|
+
data.tar.gz: ca658f344ed87f29b94eb1e115a13b4ef6d4d954c450b6f4e9892f7788b35e39
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e83150ee16db03f72e39d85c33932f38f43351501e673133e42f7df98208cacfe34e9eb0b505ede452fae872b770c5de9c8cdded5927b5d527fc9b6a7757dc7
|
7
|
+
data.tar.gz: 91c9b79d9698c720c5ed77a775f9758069ac41c5fd625c7870180e85a8f4ed16a4998c4b93ff8de04eb6e828b689046801b9a3e7c76efeef22dc0a91b9083797
|
@@ -5,7 +5,7 @@ module Torque
|
|
5
5
|
module Adapter
|
6
6
|
module DatabaseStatements
|
7
7
|
|
8
|
-
EXTENDED_DATABASE_TYPES = %i
|
8
|
+
EXTENDED_DATABASE_TYPES = %i[enum enum_set interval]
|
9
9
|
|
10
10
|
# Switch between dump mode or not
|
11
11
|
def dump_mode!
|
@@ -48,8 +48,8 @@ module Torque
|
|
48
48
|
def schema_exists?(name, filtered: true)
|
49
49
|
return user_defined_schemas.include?(name.to_s) if filtered
|
50
50
|
|
51
|
-
query_value(<<-SQL) == 1
|
52
|
-
SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname =
|
51
|
+
query_value(<<-SQL, "SCHEMA") == 1
|
52
|
+
SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = #{quote(name)}
|
53
53
|
SQL
|
54
54
|
end
|
55
55
|
|
@@ -59,96 +59,75 @@ module Torque
|
|
59
59
|
end
|
60
60
|
alias data_type_exists? type_exists?
|
61
61
|
|
62
|
-
# Configure the interval format
|
63
|
-
def configure_connection
|
64
|
-
super
|
65
|
-
execute("SET SESSION IntervalStyle TO 'iso_8601'", 'SCHEMA')
|
66
|
-
end
|
67
|
-
|
68
|
-
# Since enums create new types, type map needs to be rebooted to include
|
69
|
-
# the new ones, both normal and array one
|
70
|
-
def create_enum(name, *)
|
71
|
-
super
|
72
|
-
|
73
|
-
oid = query_value("SELECT #{quote(name)}::regtype::oid", "SCHEMA").to_i
|
74
|
-
load_additional_types([oid])
|
75
|
-
end
|
76
|
-
|
77
62
|
# Change some of the types being mapped
|
78
63
|
def initialize_type_map(m = type_map)
|
79
64
|
super
|
80
|
-
m.register_type 'box', OID::Box.new
|
81
|
-
m.register_type 'circle', OID::Circle.new
|
82
|
-
m.register_type 'interval', OID::Interval.new
|
83
|
-
m.register_type 'line', OID::Line.new
|
84
|
-
m.register_type 'segment', OID::Segment.new
|
85
65
|
|
86
|
-
|
66
|
+
if PostgreSQL.config.geometry.enabled
|
67
|
+
m.register_type 'box', OID::Box.new
|
68
|
+
m.register_type 'circle', OID::Circle.new
|
69
|
+
m.register_type 'line', OID::Line.new
|
70
|
+
m.register_type 'segment', OID::Segment.new
|
71
|
+
end
|
72
|
+
|
73
|
+
if PostgreSQL.config.interval.enabled
|
74
|
+
m.register_type 'interval', OID::Interval.new
|
75
|
+
end
|
87
76
|
end
|
88
77
|
|
89
78
|
# :nodoc:
|
90
79
|
def load_additional_types(oids = nil)
|
80
|
+
type_map.alias_type 'regclass', 'varchar'
|
81
|
+
type_map.alias_type 'regconfig', 'varchar'
|
91
82
|
super
|
92
83
|
torque_load_additional_types(oids)
|
93
84
|
end
|
94
85
|
|
95
86
|
# Add the composite types to be loaded too.
|
96
87
|
def torque_load_additional_types(oids = nil)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
AND NOT EXISTS(
|
110
|
-
SELECT 1 FROM pg_catalog.pg_type el
|
111
|
-
WHERE el.oid = t.typelem AND el.typarray = t.oid
|
112
|
-
)
|
113
|
-
AND (t.typrelid = 0 OR (
|
114
|
-
SELECT c.relkind = 'c' FROM pg_catalog.pg_class c
|
115
|
-
WHERE c.oid = t.typrelid
|
116
|
-
))
|
88
|
+
return unless torque_load_additional_types?
|
89
|
+
|
90
|
+
# Types: (b)ase, (c)omposite, (d)omain, (e)num, (p)seudotype, (r)ange
|
91
|
+
# (m)ultirange
|
92
|
+
|
93
|
+
query = <<~SQL
|
94
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput,
|
95
|
+
r.rngsubtype, t.typtype, t.typbasetype, t.typarray
|
96
|
+
FROM pg_type as t
|
97
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
98
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
99
|
+
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
|
117
100
|
SQL
|
118
101
|
|
119
|
-
|
120
|
-
|
102
|
+
if oids
|
103
|
+
query += " AND t.oid IN (%s)" % oids.join(", ")
|
104
|
+
else
|
105
|
+
query += " AND t.typtype IN ('e')"
|
121
106
|
end
|
107
|
+
|
108
|
+
options = { allow_retry: true, materialize_transactions: false }
|
109
|
+
internal_execute(query, 'SCHEMA', **options).each do |row|
|
110
|
+
if row['typtype'] == 'e' && PostgreSQL.config.enum.enabled
|
111
|
+
OID::Enum.create(row, type_map)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def torque_load_additional_types?
|
117
|
+
PostgreSQL.config.enum.enabled
|
122
118
|
end
|
123
119
|
|
124
120
|
# Gets a list of user defined types.
|
125
121
|
# You can even choose the +category+ filter
|
126
122
|
def user_defined_types(*categories)
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
CASE t.typtype
|
134
|
-
WHEN 'e' THEN 'enum'
|
135
|
-
END AS type
|
136
|
-
FROM pg_type t
|
137
|
-
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
123
|
+
categories = categories.compact.presence || %w[c e p r m]
|
124
|
+
|
125
|
+
query(<<-SQL, 'SCHEMA').to_h
|
126
|
+
SELECT t.typname, t.typtype
|
127
|
+
FROM pg_type as t
|
128
|
+
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
138
129
|
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
|
139
|
-
|
140
|
-
AND NOT EXISTS(
|
141
|
-
SELECT 1
|
142
|
-
FROM pg_catalog.pg_type el
|
143
|
-
WHERE el.oid = t.typelem
|
144
|
-
AND el.typarray = t.oid
|
145
|
-
)
|
146
|
-
AND (t.typrelid = 0 OR (
|
147
|
-
SELECT c.relkind = 'c'
|
148
|
-
FROM pg_catalog.pg_class c
|
149
|
-
WHERE c.oid = t.typrelid
|
150
|
-
))
|
151
|
-
ORDER BY t.typtype DESC
|
130
|
+
AND t.typtype IN ('#{categories.join("', '")}')
|
152
131
|
SQL
|
153
132
|
end
|
154
133
|
|
@@ -195,20 +174,20 @@ module Torque
|
|
195
174
|
# Get the list of columns, and their definition, but only from the
|
196
175
|
# actual table, does not include columns that comes from inherited table
|
197
176
|
def column_definitions(table_name)
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
177
|
+
query(<<~SQL, "SCHEMA")
|
178
|
+
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
179
|
+
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
180
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
181
|
+
#{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
|
182
|
+
#{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
|
183
|
+
FROM pg_attribute a
|
184
|
+
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
185
|
+
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
186
|
+
LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
|
187
|
+
WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
|
188
|
+
AND a.attnum > 0 AND NOT a.attisdropped
|
189
|
+
#{'AND a.attislocal' if @_dump_mode}
|
190
|
+
ORDER BY a.attnum
|
212
191
|
SQL
|
213
192
|
end
|
214
193
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Torque
|
4
|
+
module PostgreSQL
|
5
|
+
module Adapter
|
6
|
+
module OID
|
7
|
+
module Array
|
8
|
+
def force_equality?(value)
|
9
|
+
PostgreSQL.config.predicate_builder.handle_array_attributes ? false : super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.prepend(Array)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -7,9 +7,7 @@ module Torque
|
|
7
7
|
alias c= intercept=
|
8
8
|
|
9
9
|
def a=(value)
|
10
|
-
self.slope = vertical?
|
11
|
-
? Float::INFINITY \
|
12
|
-
: Rational(value, b)
|
10
|
+
self.slope = vertical? ? Float::INFINITY : Rational(value, b)
|
13
11
|
end
|
14
12
|
|
15
13
|
def a
|
@@ -17,9 +15,7 @@ module Torque
|
|
17
15
|
end
|
18
16
|
|
19
17
|
def b=(value)
|
20
|
-
self.slope = value.zero?
|
21
|
-
? Float::INFINITY \
|
22
|
-
: Rational(a, value)
|
18
|
+
self.slope = value.zero? ? Float::INFINITY : Rational(a, value)
|
23
19
|
end
|
24
20
|
|
25
21
|
def b
|
@@ -7,7 +7,7 @@ module Torque
|
|
7
7
|
class Range < ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range
|
8
8
|
HASH_PICK = %i[from start end to].freeze
|
9
9
|
|
10
|
-
module
|
10
|
+
module Comparison
|
11
11
|
def <=>(other)
|
12
12
|
return super unless other.acts_like?(:date) || other.acts_like?(:time)
|
13
13
|
other = other.to_time if other.acts_like?(:date)
|
@@ -17,9 +17,9 @@ module Torque
|
|
17
17
|
|
18
18
|
def cast_value(value)
|
19
19
|
case value
|
20
|
-
when Array
|
20
|
+
when ::Array
|
21
21
|
cast_custom(value[0], value[1])
|
22
|
-
when Hash
|
22
|
+
when ::Hash
|
23
23
|
pieces = value.with_indifferent_access.values_at(*HASH_PICK)
|
24
24
|
cast_custom(pieces[0] || pieces[1], pieces[2] || pieces[3])
|
25
25
|
else
|
@@ -54,7 +54,7 @@ module Torque
|
|
54
54
|
::ActiveRecord::ConnectionAdapters::PostgreSQL::OID.send(:remove_const, :Range)
|
55
55
|
::ActiveRecord::ConnectionAdapters::PostgreSQL::OID.const_set(:Range, Range)
|
56
56
|
|
57
|
-
::Float.prepend(Range::
|
57
|
+
::Float.prepend(Range::Comparison)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
@@ -1,24 +1,2 @@
|
|
1
|
-
require_relative 'oid/
|
2
|
-
require_relative 'oid/circle'
|
3
|
-
require_relative 'oid/enum'
|
4
|
-
require_relative 'oid/enum_set'
|
5
|
-
require_relative 'oid/interval'
|
6
|
-
require_relative 'oid/line'
|
1
|
+
require_relative 'oid/array'
|
7
2
|
require_relative 'oid/range'
|
8
|
-
require_relative 'oid/segment'
|
9
|
-
|
10
|
-
module Torque
|
11
|
-
module PostgreSQL
|
12
|
-
module Adapter
|
13
|
-
module OID
|
14
|
-
end
|
15
|
-
|
16
|
-
ActiveRecord::Type.register(:box, OID::Box, adapter: :postgresql)
|
17
|
-
ActiveRecord::Type.register(:circle, OID::Circle, adapter: :postgresql)
|
18
|
-
ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
|
19
|
-
ActiveRecord::Type.register(:enum_set, OID::EnumSet, adapter: :postgresql)
|
20
|
-
ActiveRecord::Type.register(:line, OID::Line, adapter: :postgresql)
|
21
|
-
ActiveRecord::Type.register(:segment, OID::Segment, adapter: :postgresql)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -4,21 +4,27 @@ module Torque
|
|
4
4
|
module PostgreSQL
|
5
5
|
module Adapter
|
6
6
|
module Quoting
|
7
|
+
QUOTED_TYPE_NAMES = Concurrent::Map.new
|
7
8
|
|
8
9
|
Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
|
9
10
|
Column = ActiveRecord::ConnectionAdapters::PostgreSQL::Column
|
10
11
|
ColumnDefinition = ActiveRecord::ConnectionAdapters::ColumnDefinition
|
12
|
+
Utils = ActiveRecord::ConnectionAdapters::PostgreSQL::Utils
|
11
13
|
|
12
14
|
# Quotes type names for use in SQL queries.
|
13
|
-
def quote_type_name(
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
def quote_type_name(name, *args)
|
16
|
+
QUOTED_TYPE_NAMES[args] ||= begin
|
17
|
+
name = name.to_s
|
18
|
+
args << 'public' if args.empty? && !name.include?('.')
|
19
|
+
quote_identifier_name(name, *args)
|
18
20
|
end
|
21
|
+
end
|
19
22
|
|
20
|
-
|
21
|
-
|
23
|
+
# Make sure to support all sorts of different compositions of names
|
24
|
+
def quote_identifier_name(name, schema = nil)
|
25
|
+
name = Utils.extract_schema_qualified_name(name.to_s) unless name.is_a?(Name)
|
26
|
+
name.instance_variable_set(:@schema, Utils.unquote_identifier(schema.to_s)) if schema
|
27
|
+
name.quoted.freeze
|
22
28
|
end
|
23
29
|
|
24
30
|
def quote_default_expression(value, column)
|
@@ -3,39 +3,18 @@ module Torque
|
|
3
3
|
module Adapter
|
4
4
|
module SchemaCreation
|
5
5
|
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
create_sql << "IF NOT EXISTS " if o.if_not_exists
|
10
|
-
create_sql << "#{quote_table_name(o.name)} "
|
11
|
-
|
12
|
-
statements = o.columns.map { |c| accept c }
|
13
|
-
statements << accept(o.primary_keys) if o.primary_keys
|
14
|
-
|
15
|
-
if supports_indexes_in_create?
|
16
|
-
statements.concat(o.indexes.map { |c, o| index_in_create(o.name, c, o) })
|
17
|
-
end
|
18
|
-
|
19
|
-
if @conn.supports_foreign_keys?
|
20
|
-
statements.concat(o.foreign_keys.map { |fk| accept fk })
|
21
|
-
end
|
22
|
-
|
23
|
-
if respond_to?(:supports_check_constraints?) && supports_check_constraints?
|
24
|
-
statements.concat(o.check_constraints.map { |chk| accept chk })
|
25
|
-
end
|
26
|
-
|
27
|
-
create_sql << "(#{statements.join(', ')})" \
|
28
|
-
if statements.present? || o.inherits.present?
|
29
|
-
|
30
|
-
add_table_options!(create_sql, o)
|
31
|
-
|
6
|
+
# Inherits are now setup via table options, but keep the implementation
|
7
|
+
# supported by this gem
|
8
|
+
def add_table_options!(create_sql, o)
|
32
9
|
if o.inherits.present?
|
10
|
+
# Make sure we always have parenthesis
|
11
|
+
create_sql << '()' unless create_sql[-1] == ')'
|
12
|
+
|
33
13
|
tables = o.inherits.map(&method(:quote_table_name))
|
34
14
|
create_sql << " INHERITS ( #{tables.join(' , ')} )"
|
35
15
|
end
|
36
16
|
|
37
|
-
create_sql
|
38
|
-
create_sql
|
17
|
+
super(create_sql, o)
|
39
18
|
end
|
40
19
|
end
|
41
20
|
|
@@ -3,7 +3,26 @@
|
|
3
3
|
module Torque
|
4
4
|
module PostgreSQL
|
5
5
|
module Adapter
|
6
|
+
module ColumnMethods
|
7
|
+
|
8
|
+
# Adds a search language column to the table. See +add_search_language+
|
9
|
+
def search_language(*names, **options)
|
10
|
+
raise ArgumentError, "Missing column name(s) for search_language" if names.empty?
|
11
|
+
names.each { |name| column(name, :regconfig, **options) }
|
12
|
+
end
|
13
|
+
|
14
|
+
# Add a search vector column to the table. See +add_search_vector+
|
15
|
+
def search_vector(*names, columns:, **options)
|
16
|
+
raise ArgumentError, "Missing column name(s) for search_vector" if names.empty?
|
17
|
+
options = Attributes::Builder.search_vector_options(columns: columns, **options)
|
18
|
+
names.each { |name| column(name, :virtual, **options) }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
6
23
|
module TableDefinition
|
24
|
+
include ColumnMethods
|
25
|
+
|
7
26
|
attr_reader :inherits
|
8
27
|
|
9
28
|
def initialize(*args, **options)
|
@@ -12,8 +31,25 @@ module Torque
|
|
12
31
|
@inherits = Array.wrap(options.delete(:inherits)).flatten.compact \
|
13
32
|
if options.key?(:inherits)
|
14
33
|
end
|
34
|
+
|
35
|
+
def set_primary_key(tn, id, primary_key, *, **)
|
36
|
+
super unless @inherits.present? && primary_key.blank? && id == :primary_key
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def create_column_definition(name, type, options)
|
42
|
+
if type == :enum_set
|
43
|
+
type = :enum
|
44
|
+
options ||= {}
|
45
|
+
options[:array] = true
|
46
|
+
end
|
47
|
+
|
48
|
+
super(name, type, options)
|
49
|
+
end
|
15
50
|
end
|
16
51
|
|
52
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::Table.include ColumnMethods
|
17
53
|
ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.include TableDefinition
|
18
54
|
end
|
19
55
|
end
|
@@ -4,6 +4,14 @@ module Torque
|
|
4
4
|
module PostgreSQL
|
5
5
|
module Adapter
|
6
6
|
module SchemaDumper
|
7
|
+
SEARCH_VECTOR_SCANNER = /
|
8
|
+
to_tsvector\(
|
9
|
+
('[^']+'|[a-z][a-z0-9_]*)[^,]*,[^\(]*
|
10
|
+
\(?coalesce\(([a-z][a-z0-9_]*)[^\)]*\)\)?
|
11
|
+
(?:::[^\)]*\))?
|
12
|
+
(?:\s*,\s*'([A-D])')?
|
13
|
+
/ix
|
14
|
+
|
7
15
|
def dump(stream) # :nodoc:
|
8
16
|
@connection.dump_mode!
|
9
17
|
super
|
@@ -12,16 +20,6 @@ module Torque
|
|
12
20
|
stream
|
13
21
|
end
|
14
22
|
|
15
|
-
def extensions(stream) # :nodoc:
|
16
|
-
super
|
17
|
-
user_defined_schemas(stream)
|
18
|
-
end
|
19
|
-
|
20
|
-
# Translate +:enum_set+ into +:enum+
|
21
|
-
def schema_type(column)
|
22
|
-
column.type == :enum_set ? :enum : super
|
23
|
-
end
|
24
|
-
|
25
23
|
private
|
26
24
|
|
27
25
|
def tables(stream) # :nodoc:
|
@@ -39,41 +37,46 @@ module Torque
|
|
39
37
|
|
40
38
|
def dump_tables(stream)
|
41
39
|
inherited_tables = @connection.inherited_tables
|
42
|
-
sorted_tables = (@connection.tables - @connection.views).
|
43
|
-
table_name.split(/(?:public)?\./).reverse
|
44
|
-
|
40
|
+
sorted_tables = (@connection.tables - @connection.views).filter_map do |table_name|
|
41
|
+
name_parts = table_name.split(/(?:public)?\./).reverse.compact_blank
|
42
|
+
next if ignored?(table_name) || ignored?(name_parts.join('.'))
|
43
|
+
|
44
|
+
[table_name, name_parts]
|
45
|
+
end.sort_by(&:last).to_h
|
46
|
+
|
47
|
+
postponed = []
|
45
48
|
|
46
49
|
stream.puts " # These are the common tables"
|
47
|
-
|
48
|
-
table
|
50
|
+
sorted_tables.each do |table, (table_name, _)|
|
51
|
+
next postponed << table if inherited_tables.key?(table_name)
|
52
|
+
|
53
|
+
table(table, stream)
|
54
|
+
stream.puts # Ideally we would not do this in the last one
|
49
55
|
end
|
50
56
|
|
51
|
-
if
|
57
|
+
if postponed.present?
|
52
58
|
stream.puts " # These are tables that have inheritance"
|
53
|
-
|
54
|
-
next if ignored?(table_name)
|
55
|
-
|
59
|
+
postponed.each do |table|
|
56
60
|
sub_stream = StringIO.new
|
57
|
-
table(
|
58
|
-
|
59
|
-
|
60
|
-
sub_stream.rewind
|
61
|
-
inherits.map! { |parent| parent.to_s.sub(/\Apublic\./, '') }
|
62
|
-
inherits = inherits.first if inherits.size === 1
|
63
|
-
inherits = ", inherits: #{inherits.inspect} do |t|"
|
64
|
-
table_dump = sub_stream.read.gsub(/ do \|t\|$/, inherits)
|
65
|
-
|
66
|
-
# Ensure bodyless definitions
|
67
|
-
table_dump.gsub!(/do \|t\|\n end/, '')
|
68
|
-
stream.print table_dump
|
61
|
+
table(table, sub_stream)
|
62
|
+
stream.puts sub_stream.string.sub(/do \|t\|\n end/, '')
|
63
|
+
stream.puts
|
69
64
|
end
|
70
65
|
end
|
71
66
|
|
72
|
-
#
|
67
|
+
# Fixes double new lines to single new lines
|
68
|
+
stream.pos -= 1
|
69
|
+
|
70
|
+
# dump foreign keys at the end to make sure all dependent tables exist.
|
73
71
|
if @connection.supports_foreign_keys?
|
72
|
+
foreign_keys_stream = StringIO.new
|
74
73
|
sorted_tables.each do |tbl|
|
75
|
-
foreign_keys(tbl,
|
74
|
+
foreign_keys(tbl, foreign_keys_stream)
|
76
75
|
end
|
76
|
+
|
77
|
+
foreign_keys_string = foreign_keys_stream.string
|
78
|
+
stream.puts if foreign_keys_string.length > 0
|
79
|
+
stream.print foreign_keys_string
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
@@ -83,7 +86,8 @@ module Torque
|
|
83
86
|
end
|
84
87
|
|
85
88
|
# Dump user defined schemas
|
86
|
-
def
|
89
|
+
def schemas(stream)
|
90
|
+
return super if !PostgreSQL.config.schemas.enabled
|
87
91
|
return if (list = (@connection.user_defined_schemas - ['public'])).empty?
|
88
92
|
|
89
93
|
stream.puts " # Custom schemas defined in this database."
|
@@ -91,6 +95,58 @@ module Torque
|
|
91
95
|
stream.puts
|
92
96
|
end
|
93
97
|
|
98
|
+
# Adjust the schema type for search vector
|
99
|
+
def schema_type_with_virtual(column)
|
100
|
+
column.virtual? && column.type == :tsvector ? :search_vector : super
|
101
|
+
end
|
102
|
+
|
103
|
+
# Adjust the schema type for search language
|
104
|
+
def schema_type(column)
|
105
|
+
column.sql_type == 'regconfig' ? :search_language : super
|
106
|
+
end
|
107
|
+
|
108
|
+
# Adjust table options to make the dump more readable
|
109
|
+
def prepare_column_options(column)
|
110
|
+
options = super
|
111
|
+
parse_search_vector_options(column, options) if column.type == :tsvector
|
112
|
+
options
|
113
|
+
end
|
114
|
+
|
115
|
+
# Parse the search vector operation into a readable format
|
116
|
+
def parse_search_vector_options(column, options)
|
117
|
+
settings = options[:as].scan(SEARCH_VECTOR_SCANNER)
|
118
|
+
return if settings.empty?
|
119
|
+
|
120
|
+
languages = settings.map(&:shift).uniq
|
121
|
+
return if languages.many?
|
122
|
+
|
123
|
+
language = languages.first
|
124
|
+
language = language[0] == "'" ? language[1..-2] : language.to_sym
|
125
|
+
columns = parse_search_vector_columns(settings)
|
126
|
+
|
127
|
+
options.except!(:as, :type)
|
128
|
+
options.merge!(language: language.inspect, columns: columns)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Simplify the whole columns configuration to make it more manageable
|
132
|
+
def parse_search_vector_columns(settings)
|
133
|
+
return ":#{settings.first.first}" if settings.one?
|
134
|
+
|
135
|
+
settings = settings.sort_by(&:last)
|
136
|
+
weights = %w[A B C D]
|
137
|
+
|
138
|
+
columns = settings.each.with_index.reduce([]) do |acc, (setting, index)|
|
139
|
+
column, weight = setting
|
140
|
+
break if (weights[index] || 'D') != weight
|
141
|
+
|
142
|
+
acc << column
|
143
|
+
acc
|
144
|
+
end
|
145
|
+
|
146
|
+
return columns.map(&:to_sym).inspect if columns
|
147
|
+
settings.to_h.transform_values(&:inspect)
|
148
|
+
end
|
149
|
+
|
94
150
|
def fx_functions_position
|
95
151
|
return unless defined?(::Fx::SchemaDumper::Function)
|
96
152
|
Fx.configuration.dump_functions_at_beginning_of_schema ? :beginning : :end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Torque
|
4
|
+
module PostgreSQL
|
5
|
+
module Adapter
|
6
|
+
module SchemaOverrides
|
7
|
+
# This adds better support for handling the quotation of table names
|
8
|
+
def quote_table_name(name)
|
9
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting::QUOTED_TABLE_NAMES.then do |m|
|
10
|
+
m[name] ||= quote_identifier_name(name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
%i[
|
15
|
+
table_exists? indexes index_exists? columns column_exists? primary_key
|
16
|
+
create_table change_table add_column add_columns remove_columns remove_column
|
17
|
+
change_column change_column_default change_column_null rename_column
|
18
|
+
add_index remove_index rename_index index_name_exists? foreign_keys
|
19
|
+
add_timestamps remove_timestamps change_table_comment change_column_comment
|
20
|
+
bulk_change_table
|
21
|
+
|
22
|
+
rename_table add_foreign_key remove_foreign_key foreign_key_exists?
|
23
|
+
].each do |method_name|
|
24
|
+
define_method(method_name) do |table_name, *args, **options, &block|
|
25
|
+
table_name = sanitize_name_with_schema(table_name, options)
|
26
|
+
super(table_name, *args, **options, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def drop_table(*table_names, **options)
|
31
|
+
table_names = table_names.map { |name| sanitize_name_with_schema(name, options.dup) }
|
32
|
+
super(*table_names, **options)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def validate_table_length!(table_name)
|
38
|
+
super(table_name.to_s)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
include SchemaOverrides
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|