torque-postgresql 4.0.1 → 4.1.1
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 +52 -0
- data/lib/generators/torque/skill_generator.rb +17 -0
- data/lib/generators/torque/templates/trigger.sql.erb +3 -0
- data/lib/generators/torque/trigger_generator.rb +13 -0
- data/lib/torque/postgresql/adapter/database_statements.rb +98 -7
- data/lib/torque/postgresql/adapter/inheritance_statements.rb +297 -0
- data/lib/torque/postgresql/adapter/oid/array.rb +13 -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 +117 -0
- data/lib/torque/postgresql/adapter/oid/enum.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/enum_set.rb +2 -2
- data/lib/torque/postgresql/adapter/oid/interval.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/line.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/lquery.rb +70 -0
- data/lib/torque/postgresql/adapter/oid/ltree.rb +111 -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/oid.rb +13 -0
- data/lib/torque/postgresql/adapter/quoting.rb +11 -2
- data/lib/torque/postgresql/adapter/schema_definitions.rb +51 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +27 -0
- 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 +114 -1
- data/lib/torque/postgresql/arel/visitors.rb +65 -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 +97 -0
- data/lib/torque/postgresql/attributes/composite.rb +98 -0
- data/lib/torque/postgresql/attributes/enum.rb +13 -7
- data/lib/torque/postgresql/attributes/lquery.rb +120 -0
- data/lib/torque/postgresql/attributes/ltree.rb +121 -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 +26 -15
- data/lib/torque/postgresql/config.rb +100 -15
- data/lib/torque/postgresql/geometry_builder.rb +4 -10
- data/lib/torque/postgresql/inheritance/expander.rb +66 -0
- data/lib/torque/postgresql/inheritance/record.rb +52 -0
- data/lib/torque/postgresql/inheritance.rb +141 -68
- data/lib/torque/postgresql/migration/command_recorder.rb +76 -0
- data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +1 -1
- data/lib/torque/postgresql/predicate_builder/array_handler.rb +16 -8
- data/lib/torque/postgresql/predicate_builder/composite_handler.rb +122 -0
- data/lib/torque/postgresql/predicate_builder/ltree_handler.rb +112 -0
- data/lib/torque/postgresql/predicate_builder/struct_handler.rb +88 -0
- data/lib/torque/postgresql/predicate_builder.rb +53 -0
- data/lib/torque/postgresql/predicate_table.rb +61 -0
- data/lib/torque/postgresql/railtie.rb +52 -0
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +7 -3
- data/lib/torque/postgresql/relation/buckets.rb +2 -1
- data/lib/torque/postgresql/relation/cross_join.rb +193 -0
- data/lib/torque/postgresql/relation/inheritance.rb +145 -28
- data/lib/torque/postgresql/relation/join_series.rb +1 -0
- data/lib/torque/postgresql/relation/merger.rb +30 -5
- data/lib/torque/postgresql/relation/segments.rb +140 -0
- data/lib/torque/postgresql/relation.rb +32 -12
- 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 +24 -1
- data/lib/torque/postgresql/versioned_commands.rb +39 -0
- data/lib/torque/postgresql.rb +4 -0
- data/sig/generators/torque/function_generator.rbs +9 -0
- data/sig/generators/torque/skill_generator.rbs +7 -0
- data/sig/generators/torque/trigger_generator.rbs +9 -0
- data/sig/generators/torque/type_generator.rbs +9 -0
- data/sig/generators/torque/view_generator.rbs +9 -0
- data/sig/manifest.yaml +7 -0
- data/sig/torque/postgresql/adapter/database_statements.rbs +42 -0
- data/sig/torque/postgresql/adapter/inheritance_statements.rbs +36 -0
- data/sig/torque/postgresql/adapter/oid/array.rbs +20 -0
- data/sig/torque/postgresql/adapter/oid/box.rbs +28 -0
- data/sig/torque/postgresql/adapter/oid/circle.rbs +33 -0
- data/sig/torque/postgresql/adapter/oid/composite.rbs +35 -0
- data/sig/torque/postgresql/adapter/oid/enum.rbs +28 -0
- data/sig/torque/postgresql/adapter/oid/enum_set.rbs +19 -0
- data/sig/torque/postgresql/adapter/oid/interval.rbs +20 -0
- data/sig/torque/postgresql/adapter/oid/line.rbs +33 -0
- data/sig/torque/postgresql/adapter/oid/lquery.rbs +21 -0
- data/sig/torque/postgresql/adapter/oid/ltree.rbs +32 -0
- data/sig/torque/postgresql/adapter/oid/range.rbs +28 -0
- data/sig/torque/postgresql/adapter/oid/segment.rbs +38 -0
- data/sig/torque/postgresql/adapter/oid/struct.rbs +41 -0
- data/sig/torque/postgresql/adapter/oid/struct_list.rbs +20 -0
- data/sig/torque/postgresql/adapter/oid/struct_set.rbs +18 -0
- data/sig/torque/postgresql/adapter/oid.rbs +9 -0
- data/sig/torque/postgresql/adapter/quoting.rbs +20 -0
- data/sig/torque/postgresql/adapter/schema_creation.rbs +13 -0
- data/sig/torque/postgresql/adapter/schema_definitions.rbs +55 -0
- data/sig/torque/postgresql/adapter/schema_dumper.rbs +37 -0
- data/sig/torque/postgresql/adapter/schema_overrides.rbs +46 -0
- data/sig/torque/postgresql/adapter/schema_statements.rbs +50 -0
- data/sig/torque/postgresql/adapter.rbs +24 -0
- data/sig/torque/postgresql/arel/infix_operation.rbs +79 -0
- data/sig/torque/postgresql/arel/join_source.rbs +19 -0
- data/sig/torque/postgresql/arel/nodes.rbs +83 -0
- data/sig/torque/postgresql/arel/operations.rbs +28 -0
- data/sig/torque/postgresql/arel/select_manager.rbs +15 -0
- data/sig/torque/postgresql/arel/visitors.rbs +30 -0
- data/sig/torque/postgresql/arel.rbs +6 -0
- data/sig/torque/postgresql/associations/association_scope.rbs +28 -0
- data/sig/torque/postgresql/associations/belongs_to_many_association.rbs +51 -0
- data/sig/torque/postgresql/associations/builder/belongs_to_many.rbs +24 -0
- data/sig/torque/postgresql/associations/builder/has_many.rbs +15 -0
- data/sig/torque/postgresql/associations/builder.rbs +8 -0
- data/sig/torque/postgresql/associations/foreign_association.rbs +16 -0
- data/sig/torque/postgresql/associations/join_dependency.rbs +16 -0
- data/sig/torque/postgresql/associations/preloader/association.rbs +32 -0
- data/sig/torque/postgresql/associations/preloader/loader_query.rbs +20 -0
- data/sig/torque/postgresql/associations/preloader.rbs +8 -0
- data/sig/torque/postgresql/associations.rbs +17 -0
- data/sig/torque/postgresql/attributes/base.rbs +37 -0
- data/sig/torque/postgresql/attributes/builder/enum.rbs +38 -0
- data/sig/torque/postgresql/attributes/builder/full_text_search.rbs +27 -0
- data/sig/torque/postgresql/attributes/builder/period.rbs +106 -0
- data/sig/torque/postgresql/attributes/builder.rbs +19 -0
- data/sig/torque/postgresql/attributes/composite.rbs +28 -0
- data/sig/torque/postgresql/attributes/enum.rbs +69 -0
- data/sig/torque/postgresql/attributes/enum_set.rbs +75 -0
- data/sig/torque/postgresql/attributes/full_text_search.rbs +13 -0
- data/sig/torque/postgresql/attributes/lazy.rbs +17 -0
- data/sig/torque/postgresql/attributes/lquery.rbs +48 -0
- data/sig/torque/postgresql/attributes/ltree.rbs +46 -0
- data/sig/torque/postgresql/attributes/period.rbs +13 -0
- data/sig/torque/postgresql/attributes/simple_enum.rbs +39 -0
- data/sig/torque/postgresql/attributes/struct.rbs +38 -0
- data/sig/torque/postgresql/attributes.rbs +6 -0
- data/sig/torque/postgresql/autosave_association.rbs +16 -0
- data/sig/torque/postgresql/auxiliary_statement/recursive.rbs +22 -0
- data/sig/torque/postgresql/auxiliary_statement/settings.rbs +63 -0
- data/sig/torque/postgresql/auxiliary_statement.rbs +57 -0
- data/sig/torque/postgresql/base.rbs +41 -0
- data/sig/torque/postgresql/collector.rbs +7 -0
- data/sig/torque/postgresql/config.rbs +9 -0
- data/sig/torque/postgresql/function.rbs +21 -0
- data/sig/torque/postgresql/geometry_builder.rbs +20 -0
- data/sig/torque/postgresql/i18n.rbs +15 -0
- data/sig/torque/postgresql/inheritance/expander.rbs +23 -0
- data/sig/torque/postgresql/inheritance/record.rbs +20 -0
- data/sig/torque/postgresql/inheritance.rbs +47 -0
- data/sig/torque/postgresql/insert_all.rbs +22 -0
- data/sig/torque/postgresql/migration/command_recorder.rbs +30 -0
- data/sig/torque/postgresql/migration.rbs +6 -0
- data/sig/torque/postgresql/predicate_builder/arel_attribute_handler.rbs +15 -0
- data/sig/torque/postgresql/predicate_builder/array_handler.rbs +26 -0
- data/sig/torque/postgresql/predicate_builder/composite_handler.rbs +26 -0
- data/sig/torque/postgresql/predicate_builder/enumerator_lazy_handler.rbs +18 -0
- data/sig/torque/postgresql/predicate_builder/ltree_handler.rbs +36 -0
- data/sig/torque/postgresql/predicate_builder/regexp_handler.rbs +16 -0
- data/sig/torque/postgresql/predicate_builder/struct_handler.rbs +24 -0
- data/sig/torque/postgresql/predicate_builder.rbs +23 -0
- data/sig/torque/postgresql/predicate_table.rbs +24 -0
- data/sig/torque/postgresql/railtie.rbs +10 -0
- data/sig/torque/postgresql/reflection/abstract_reflection.rbs +22 -0
- data/sig/torque/postgresql/reflection/association_reflection.rbs +18 -0
- data/sig/torque/postgresql/reflection/belongs_to_many_reflection.rbs +30 -0
- data/sig/torque/postgresql/reflection/has_many_reflection.rbs +14 -0
- data/sig/torque/postgresql/reflection/runtime_reflection.rbs +18 -0
- data/sig/torque/postgresql/reflection/through_reflection.rbs +14 -0
- data/sig/torque/postgresql/reflection.rbs +11 -0
- data/sig/torque/postgresql/relation/auxiliary_statement.rbs +29 -0
- data/sig/torque/postgresql/relation/buckets.rbs +35 -0
- data/sig/torque/postgresql/relation/cross_join.rbs +63 -0
- data/sig/torque/postgresql/relation/distinct_on.rbs +16 -0
- data/sig/torque/postgresql/relation/inheritance.rbs +48 -0
- data/sig/torque/postgresql/relation/join_series.rbs +28 -0
- data/sig/torque/postgresql/relation/merger.rbs +23 -0
- data/sig/torque/postgresql/relation/segments.rbs +27 -0
- data/sig/torque/postgresql/relation.rbs +48 -0
- data/sig/torque/postgresql/schema_cache/bound_schema_reflection.rbs +14 -0
- data/sig/torque/postgresql/schema_cache/inheritance.rbs +12 -0
- data/sig/torque/postgresql/schema_cache/schema_reflection.rbs +14 -0
- data/sig/torque/postgresql/schema_cache.rbs +43 -0
- data/sig/torque/postgresql/table_name.rbs +20 -0
- data/sig/torque/postgresql/validations.rbs +13 -0
- data/sig/torque/postgresql/version.rbs +5 -0
- data/sig/torque/postgresql/versioned_commands/command_migration.rbs +54 -0
- data/sig/torque/postgresql/versioned_commands/generator.rbs +17 -0
- data/sig/torque/postgresql/versioned_commands/migration_context.rbs +30 -0
- data/sig/torque/postgresql/versioned_commands/migrator.rbs +19 -0
- data/sig/torque/postgresql/versioned_commands/schema_table.rbs +23 -0
- data/sig/torque/postgresql/versioned_commands.rbs +25 -0
- data/sig/torque/postgresql.rbs +26 -0
- data/skills/torque-postgresql/SKILL.md +70 -0
- data/spec/fixtures/migrations/20250101000008_create_trigger_audit_users_v1.sql +3 -0
- data/spec/initialize.rb +29 -2
- data/spec/mocks/cache_query.rb +13 -1
- 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/rbs_helper.rb +8 -0
- data/spec/schema.rb +35 -4
- data/spec/spec_helper.rb +1 -0
- data/spec/tests/arel_spec.rb +3 -3
- data/spec/tests/auxiliary_statement_spec.rb +12 -0
- data/spec/tests/buckets_spec.rb +122 -0
- data/spec/tests/composite_spec.rb +867 -0
- data/spec/tests/cross_join_spec.rb +185 -0
- data/spec/tests/distinct_on_spec.rb +5 -0
- data/spec/tests/enum_set_spec.rb +4 -0
- data/spec/tests/enum_spec.rb +20 -4
- data/spec/tests/geometric_builder_spec.rb +16 -5
- data/spec/tests/has_many_spec.rb +2 -1
- data/spec/tests/interval_spec.rb +6 -0
- data/spec/tests/ltree_spec.rb +886 -0
- data/spec/tests/predicate_builder_spec.rb +120 -0
- data/spec/tests/relation_spec.rb +25 -69
- data/spec/tests/segments_spec.rb +264 -0
- data/spec/tests/struct_spec.rb +1170 -0
- data/spec/tests/table_inheritance_spec.rb +1037 -56
- data/spec/tests/versioned_commands_spec.rb +145 -0
- metadata +198 -2
|
@@ -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.
|
|
@@ -69,6 +115,11 @@ module Torque
|
|
|
69
115
|
execute VersionedCommands.fetch_command(dir, :view, name, version)
|
|
70
116
|
end
|
|
71
117
|
|
|
118
|
+
def create_trigger(name, version:, dir: pool.migrations_paths)
|
|
119
|
+
return super unless VersionedCommands.valid_type?(:trigger)
|
|
120
|
+
execute VersionedCommands.fetch_command(dir, :trigger, name, version)
|
|
121
|
+
end
|
|
122
|
+
|
|
72
123
|
end
|
|
73
124
|
|
|
74
125
|
ActiveRecord::ConnectionAdapters::PostgreSQL::Table.include ColumnMethods
|
|
@@ -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
|
|
@@ -47,6 +72,7 @@ module Torque
|
|
|
47
72
|
|
|
48
73
|
yield
|
|
49
74
|
versioned_commands(stream, :view, true)
|
|
75
|
+
versioned_commands(stream, :trigger, true)
|
|
50
76
|
|
|
51
77
|
functions(stream) if fx_functions_position == :end
|
|
52
78
|
triggers(stream) if defined?(::Fx::SchemaDumper::Trigger)
|
|
@@ -126,6 +152,7 @@ module Torque
|
|
|
126
152
|
def prepare_column_options(column)
|
|
127
153
|
options = super
|
|
128
154
|
parse_search_vector_options(column, options) if column.type == :tsvector
|
|
155
|
+
options[:composite_type] = column.sql_type.inspect if column.type == :composite
|
|
129
156
|
options
|
|
130
157
|
end
|
|
131
158
|
|
|
@@ -25,6 +25,100 @@ module Torque
|
|
|
25
25
|
SQL
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# Creates a new composite type, with the columns being defined using
|
|
29
|
+
# the same DSL as +create_table+, although limited to the type itself,
|
|
30
|
+
# the size-related options, and no indexes or constraints
|
|
31
|
+
#
|
|
32
|
+
# Example:
|
|
33
|
+
# create_composite_type :address do |t|
|
|
34
|
+
# t.string "street"
|
|
35
|
+
# t.integer "number"
|
|
36
|
+
# end
|
|
37
|
+
def create_composite_type(name, options = {})
|
|
38
|
+
td = create_composite_type_definition(name)
|
|
39
|
+
yield td if block_given?
|
|
40
|
+
|
|
41
|
+
validate_composite_type!(name, td)
|
|
42
|
+
drop_type(name, force: options[:force], check: true, schema: options[:schema]) \
|
|
43
|
+
if options[:force]
|
|
44
|
+
|
|
45
|
+
columns = td.columns.map { |column| composite_column_definition(name, column) }
|
|
46
|
+
type_name = sanitize_name_with_schema(name, options.dup)
|
|
47
|
+
|
|
48
|
+
internal_exec_query(<<~SQL.squish, 'SCHEMA').tap { reload_type_map }
|
|
49
|
+
CREATE TYPE #{quote_type_name(type_name)} AS (#{columns.join(', ')})
|
|
50
|
+
SQL
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Changes an existing composite type, using the same DSL as
|
|
54
|
+
# +change_table+, although limited to what a type supports
|
|
55
|
+
#
|
|
56
|
+
# Example:
|
|
57
|
+
# change_composite_type :address do |t|
|
|
58
|
+
# t.string 'zipcode'
|
|
59
|
+
# t.change 'number', :bigint
|
|
60
|
+
# t.remove 'city'
|
|
61
|
+
# t.rename 'street', 'road'
|
|
62
|
+
# end
|
|
63
|
+
def change_composite_type(name, options = {})
|
|
64
|
+
td = create_composite_type_definition(name)
|
|
65
|
+
yield td
|
|
66
|
+
|
|
67
|
+
validate_composite_type!(name, td)
|
|
68
|
+
type_name = quote_type_name(sanitize_name_with_schema(name, options.dup))
|
|
69
|
+
actions = composite_type_actions(name, td)
|
|
70
|
+
|
|
71
|
+
execute("ALTER TYPE #{type_name} #{actions.join(', ')}") if actions.any?
|
|
72
|
+
|
|
73
|
+
td.renames.each do |from, to|
|
|
74
|
+
execute <<~SQL.squish
|
|
75
|
+
ALTER TYPE #{type_name}
|
|
76
|
+
RENAME ATTRIBUTE #{quote_column_name(from)} TO #{quote_column_name(to)}
|
|
77
|
+
SQL
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
reset_composite_type(name)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Adds a single column to an existing composite type
|
|
84
|
+
def add_composite_column(type_name, column_name, type, options = {})
|
|
85
|
+
options = options.dup
|
|
86
|
+
schema = options.delete(:schema)
|
|
87
|
+
|
|
88
|
+
td = create_composite_type_definition(type_name)
|
|
89
|
+
td.column(column_name, type, **options)
|
|
90
|
+
|
|
91
|
+
alter_composite_type(type_name, schema, composite_type_actions(type_name, td))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Removes a single column of an existing composite type. The +type+ is
|
|
95
|
+
# only needed to make the migration reversible
|
|
96
|
+
def remove_composite_column(type_name, column_name, type = nil, options = {})
|
|
97
|
+
action = "DROP ATTRIBUTE #{quote_column_name(column_name)}"
|
|
98
|
+
alter_composite_type(type_name, options[:schema], [action])
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Changes the type of a single column of an existing composite type
|
|
102
|
+
def change_composite_column(type_name, column_name, type, options = {})
|
|
103
|
+
options = options.dup
|
|
104
|
+
schema = options.delete(:schema)
|
|
105
|
+
|
|
106
|
+
td = create_composite_type_definition(type_name)
|
|
107
|
+
td.change(column_name, type, **options)
|
|
108
|
+
|
|
109
|
+
alter_composite_type(type_name, schema, composite_type_actions(type_name, td))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Renames a single column of an existing composite type
|
|
113
|
+
def rename_composite_column(type_name, column_name, new_name, options = {})
|
|
114
|
+
action = <<~SQL.squish
|
|
115
|
+
RENAME ATTRIBUTE #{quote_column_name(column_name)}
|
|
116
|
+
TO #{quote_column_name(new_name)}
|
|
117
|
+
SQL
|
|
118
|
+
|
|
119
|
+
alter_composite_type(type_name, options[:schema], [action])
|
|
120
|
+
end
|
|
121
|
+
|
|
28
122
|
# Creates a column that stores the underlying language of the record so
|
|
29
123
|
# that a search vector can be created dynamically based on it. It uses
|
|
30
124
|
# a `regconfig` type, so string conversions are mandatory
|
|
@@ -92,6 +186,28 @@ module Torque
|
|
|
92
186
|
end
|
|
93
187
|
|
|
94
188
|
|
|
189
|
+
# Spread the features of the parent tables into the one being created,
|
|
190
|
+
# since PostgreSQL only propagates columns and their check constraints
|
|
191
|
+
def create_table(table_name, **options, &block)
|
|
192
|
+
sync = options.delete(:sync)
|
|
193
|
+
result = super
|
|
194
|
+
return result if sync.blank?
|
|
195
|
+
|
|
196
|
+
parents = Array.wrap(options[:inherits]).flatten.compact.map(&:to_s)
|
|
197
|
+
return result if parents.empty?
|
|
198
|
+
|
|
199
|
+
sync_inheritance_into(table_name.to_s, parents, sync_inheritance_selection(sync), false)
|
|
200
|
+
result
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# A primary key that came from a parent table is described by the +sync+
|
|
204
|
+
# option while dumping, otherwise the dumper would describe the inherited
|
|
205
|
+
# column all over again and the load would break on the duplicate
|
|
206
|
+
def primary_key(table_name)
|
|
207
|
+
return super unless @_dump_mode
|
|
208
|
+
sync_inheritance_parent_primary_key?(table_name) ? nil : super
|
|
209
|
+
end
|
|
210
|
+
|
|
95
211
|
# Add the schema option when extracting table options
|
|
96
212
|
def table_options(table_name)
|
|
97
213
|
options = super
|
|
@@ -108,6 +224,7 @@ module Torque
|
|
|
108
224
|
|
|
109
225
|
tables = inherited_table_names(table_name)
|
|
110
226
|
options[:inherits] = tables.one? ? tables.first : tables
|
|
227
|
+
options[:sync] = { primary_key: true } if sync_inheritance_parent_primary_key?(table_name)
|
|
111
228
|
end
|
|
112
229
|
|
|
113
230
|
options
|
|
@@ -137,6 +254,25 @@ module Torque
|
|
|
137
254
|
super + [:schema, :inherits]
|
|
138
255
|
end
|
|
139
256
|
|
|
257
|
+
# Add composite_type as one of the valid options for column definition
|
|
258
|
+
def valid_column_definition_options
|
|
259
|
+
super + [:composite_type]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Maps the composite type through its required option, mirroring how
|
|
263
|
+
# enums are handled
|
|
264
|
+
def type_to_sql(type, composite_type: nil, **options)
|
|
265
|
+
return super(type, **options) unless type.to_s == 'composite'
|
|
266
|
+
|
|
267
|
+
raise ArgumentError, <<~MSG.squish if composite_type.nil?
|
|
268
|
+
composite_type is required for composite columns
|
|
269
|
+
MSG
|
|
270
|
+
|
|
271
|
+
sql = composite_type.to_s
|
|
272
|
+
sql = "#{sql}[]" if options[:array]
|
|
273
|
+
sql
|
|
274
|
+
end
|
|
275
|
+
|
|
140
276
|
# Add proper support for schema load when using versioned commands
|
|
141
277
|
def assume_migrated_upto_version(version)
|
|
142
278
|
return super unless PostgreSQL.config.versioned_commands.enabled
|
|
@@ -195,6 +331,77 @@ module Torque
|
|
|
195
331
|
Quoting::Name.new(schema.to_s, name.to_s)
|
|
196
332
|
end
|
|
197
333
|
|
|
334
|
+
# A composite type is defined with the same DSL as a table, but only
|
|
335
|
+
# its columns are taken into account
|
|
336
|
+
def create_composite_type_definition(name)
|
|
337
|
+
CompositeTypeDefinition.new(self, name)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Runs a single set of changes over an existing composite type
|
|
341
|
+
def alter_composite_type(name, schema, actions)
|
|
342
|
+
type_name = quote_type_name(sanitize_name_with_schema(name, schema: schema))
|
|
343
|
+
execute("ALTER TYPE #{type_name} #{actions.join(', ')}")
|
|
344
|
+
reset_composite_type(name)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Every addition, change, and removal that a composite type can batch
|
|
348
|
+
# into a single statement. Renames are left out because PostgreSQL
|
|
349
|
+
# does not allow them to be combined with other actions
|
|
350
|
+
def composite_type_actions(name, td)
|
|
351
|
+
adds = td.columns.map do |column|
|
|
352
|
+
"ADD ATTRIBUTE #{composite_column_definition(name, column)}"
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
changes = td.changes.map do |column|
|
|
356
|
+
quoted = quote_column_name(column.name)
|
|
357
|
+
"ALTER ATTRIBUTE #{quoted} TYPE #{composite_column_type(name, column)}"
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
drops = td.removals.map { |name| "DROP ATTRIBUTE #{quote_column_name(name)}" }
|
|
361
|
+
|
|
362
|
+
adds + changes + drops
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# A composite type only holds columns, so anything else that the DSL
|
|
366
|
+
# can produce is rejected
|
|
367
|
+
def validate_composite_type!(name, td)
|
|
368
|
+
raise ArgumentError, <<~MSG.squish if td.indexes.present?
|
|
369
|
+
Unable to define the composite type "#{name}" because it does
|
|
370
|
+
not support indexes.
|
|
371
|
+
MSG
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Build a single column of a composite type
|
|
375
|
+
def composite_column_definition(name, column)
|
|
376
|
+
"#{quote_column_name(column.name)} #{composite_column_type(name, column)}"
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Resolve the type of a column, making sure that only supported
|
|
380
|
+
# options were provided
|
|
381
|
+
def composite_column_type(name, column)
|
|
382
|
+
supported = %i[limit precision scale array enum_type composite_type]
|
|
383
|
+
options = column.options.dup
|
|
384
|
+
options.delete(:primary_key) unless options[:primary_key]
|
|
385
|
+
|
|
386
|
+
unsupported = options.keys - supported
|
|
387
|
+
raise ArgumentError, <<~MSG.squish if unsupported.present?
|
|
388
|
+
Unable to define the composite type "#{name}" because the
|
|
389
|
+
column "#{column.name}" contains unsupported options:
|
|
390
|
+
#{unsupported.map(&:inspect).join(', ')}.
|
|
391
|
+
MSG
|
|
392
|
+
|
|
393
|
+
type_to_sql(column.type, **options.slice(*supported))
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Members loaded by the class that handles the type are no longer
|
|
397
|
+
# valid once the type itself has changed
|
|
398
|
+
def reset_composite_type(name)
|
|
399
|
+
reload_type_map
|
|
400
|
+
return unless PostgreSQL.config.composite.enabled
|
|
401
|
+
|
|
402
|
+
Attributes::Composite.lookup(name.to_s.split('.').last).reset_columns!
|
|
403
|
+
end
|
|
404
|
+
|
|
198
405
|
def quote_enum_values(name, values, options)
|
|
199
406
|
prefix = options[:prefix]
|
|
200
407
|
prefix = name if prefix === true
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'adapter/database_statements'
|
|
4
|
+
require_relative 'adapter/inheritance_statements'
|
|
4
5
|
require_relative 'adapter/oid'
|
|
5
6
|
require_relative 'adapter/quoting'
|
|
6
7
|
require_relative 'adapter/schema_creation'
|
|
@@ -13,6 +14,7 @@ module Torque
|
|
|
13
14
|
module Adapter
|
|
14
15
|
include Quoting
|
|
15
16
|
include DatabaseStatements
|
|
17
|
+
include InheritanceStatements
|
|
16
18
|
include SchemaStatements
|
|
17
19
|
|
|
18
20
|
# :nodoc:
|
|
@@ -31,11 +33,6 @@ module Torque
|
|
|
31
33
|
)
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
# Add `inherits` and `schema` to the list of extracted table options
|
|
35
|
-
def extract_table_options!(options)
|
|
36
|
-
super.merge(options.extract!(:inherits, :schema))
|
|
37
|
-
end
|
|
38
|
-
|
|
39
36
|
# Allow filtered bulk insert by adding the where clause. This method is
|
|
40
37
|
# only used by +InsertAll+, so it somewhat safe to override it
|
|
41
38
|
def build_insert_sql(insert)
|
|
@@ -13,12 +13,125 @@ module Torque
|
|
|
13
13
|
include ::Arel::Math
|
|
14
14
|
|
|
15
15
|
def initialize(left, right, array = false)
|
|
16
|
-
right =
|
|
16
|
+
right = right.to_s.dup
|
|
17
17
|
right << '[]' if array
|
|
18
18
|
super left, right
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# Accessing a single column of a composite value, which behaves like an
|
|
23
|
+
# attribute of the composite type it belongs to
|
|
24
|
+
class Column < ::Arel::Nodes::Binary
|
|
25
|
+
include ::Arel::Expressions
|
|
26
|
+
include ::Arel::Predications
|
|
27
|
+
include ::Arel::AliasPredication
|
|
28
|
+
include ::Arel::OrderPredications
|
|
29
|
+
include ::Arel::Math
|
|
30
|
+
|
|
31
|
+
attr_reader :type_caster
|
|
32
|
+
alias name right
|
|
33
|
+
|
|
34
|
+
def initialize(source, name, type_caster)
|
|
35
|
+
@type_caster = type_caster
|
|
36
|
+
super(source, name.to_s)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def able_to_type_cast?
|
|
40
|
+
true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def type_cast_for_database(value)
|
|
44
|
+
type_caster.serialize(value)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Accessing a single property of a document, at any depth, casted to
|
|
49
|
+
# the type that the class declares for it
|
|
50
|
+
class Property < ::Arel::Nodes::Binary
|
|
51
|
+
include ::Arel::Expressions
|
|
52
|
+
include ::Arel::Predications
|
|
53
|
+
include ::Arel::AliasPredication
|
|
54
|
+
include ::Arel::OrderPredications
|
|
55
|
+
include ::Arel::Math
|
|
56
|
+
|
|
57
|
+
attr_reader :type_caster, :cast
|
|
58
|
+
|
|
59
|
+
alias path right
|
|
60
|
+
|
|
61
|
+
# Reaching a property of another one simply adds up to the path, so
|
|
62
|
+
# that a single node always holds the whole way to it
|
|
63
|
+
def initialize(source, key, type_caster, cast = nil)
|
|
64
|
+
@type_caster = type_caster
|
|
65
|
+
@cast = cast
|
|
66
|
+
|
|
67
|
+
return super(source.left, source.path + [key.to_s]) if source.is_a?(Property)
|
|
68
|
+
super(source, [key.to_s])
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def name
|
|
72
|
+
right.last
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def able_to_type_cast?
|
|
76
|
+
true
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def type_cast_for_database(value)
|
|
80
|
+
type_caster.serialize(value)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# A cross join, which PostgreSQL needs spelled out because an inner
|
|
85
|
+
# join without a condition is a syntax error
|
|
86
|
+
class CrossJoin < ::Arel::Nodes::Join
|
|
87
|
+
def initialize(left, right = nil)
|
|
88
|
+
super
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# An aliased FROM item. +definitions+ maps a column name to the SQL
|
|
93
|
+
# type a function has to be told to return, +columns+ are the plain
|
|
94
|
+
# names the alias exposes, +ordinality+ names the appended counter,
|
|
95
|
+
# and +lateral+ is only needed when the source is a subquery
|
|
96
|
+
class FromItem < ::Arel::Nodes::Binary
|
|
97
|
+
attr_reader :columns, :definitions, :ordinality, :lateral
|
|
98
|
+
|
|
99
|
+
alias name right
|
|
100
|
+
|
|
101
|
+
def initialize(source, name, columns: nil, definitions: nil, ordinality: nil, lateral: false)
|
|
102
|
+
@columns = Array.wrap(columns).presence&.freeze
|
|
103
|
+
@definitions = definitions.presence&.freeze
|
|
104
|
+
@ordinality = ordinality&.to_s
|
|
105
|
+
@lateral = lateral
|
|
106
|
+
|
|
107
|
+
super(source, name.to_s)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# The definitions can only be moved into the source when the counter
|
|
111
|
+
# forces the +ROWS FROM+ form, since PostgreSQL refuses a column
|
|
112
|
+
# definition list next to +WITH ORDINALITY+
|
|
113
|
+
def rows_from?
|
|
114
|
+
ordinality.present? && definitions.present?
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# What the alias exposes, as name => type, where a +nil+ type means
|
|
118
|
+
# the name stands on its own. Types are only spelled out here when
|
|
119
|
+
# they have not been moved into the +ROWS FROM+ source.
|
|
120
|
+
#
|
|
121
|
+
# The list is all or nothing, because PostgreSQL matches it
|
|
122
|
+
# positionally: a partial list silently renames the columns it does
|
|
123
|
+
# reach instead of naming the ones it does not
|
|
124
|
+
def alias_columns
|
|
125
|
+
return {} if columns.nil? && definitions.nil?
|
|
126
|
+
|
|
127
|
+
plain = rows_from? ? definitions.keys : columns
|
|
128
|
+
result = plain.nil? ? definitions : plain.to_h { |name| [name.to_s, nil] }
|
|
129
|
+
|
|
130
|
+
return result if ordinality.blank?
|
|
131
|
+
result.merge(ordinality => nil)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
22
135
|
class Ref < ::Arel::Nodes::Unary
|
|
23
136
|
attr_reader :reference
|
|
24
137
|
alias to_s expr
|
|
@@ -16,19 +16,57 @@ module Torque
|
|
|
16
16
|
quote_array(o.expr, collector)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# Allow quoted arrays to get here
|
|
20
|
-
def visit_Arel_Nodes_Casted(o, collector)
|
|
21
|
-
value = o.value_for_database
|
|
22
|
-
klass = ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array::Data
|
|
23
|
-
return super unless value.is_a?(klass)
|
|
24
|
-
quote_array(value.values, collector)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
19
|
## TORQUE VISITORS
|
|
28
20
|
def visit_Torque_PostgreSQL_Arel_Nodes_Ref(o, collector)
|
|
29
21
|
collector << quote_table_name(o.expr)
|
|
30
22
|
end
|
|
31
23
|
|
|
24
|
+
# Access a single column of a composite value
|
|
25
|
+
def visit_Torque_PostgreSQL_Arel_Nodes_Column(o, collector)
|
|
26
|
+
collector << '('
|
|
27
|
+
visit(o.left, collector)
|
|
28
|
+
collector << ').' << quote_column_name(o.right)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Access a single property of a document, as text, so that it can then
|
|
32
|
+
# be casted to whatever the property is supposed to be
|
|
33
|
+
def visit_Torque_PostgreSQL_Arel_Nodes_Property(o, collector)
|
|
34
|
+
collector << '('
|
|
35
|
+
visit(o.left, collector)
|
|
36
|
+
collector << ' #>> '
|
|
37
|
+
visit(::Arel.array(o.path), collector)
|
|
38
|
+
collector << ')'
|
|
39
|
+
collector << '::' << o.cast if o.cast
|
|
40
|
+
collector
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# A cross join only ever carries its source
|
|
44
|
+
def visit_Torque_PostgreSQL_Arel_Nodes_CrossJoin(o, collector)
|
|
45
|
+
collector << 'CROSS JOIN '
|
|
46
|
+
visit(o.left, collector)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Every part here is written only when it is actually needed, since
|
|
50
|
+
# LATERAL is a noise word on functions and ROWS FROM is only forced
|
|
51
|
+
# by the counter
|
|
52
|
+
def visit_Torque_PostgreSQL_Arel_Nodes_FromItem(o, collector)
|
|
53
|
+
collector << 'LATERAL ' if o.lateral
|
|
54
|
+
|
|
55
|
+
collector << 'ROWS FROM (' if o.rows_from?
|
|
56
|
+
visit(o.left, collector)
|
|
57
|
+
collector = visit_definitions(o.definitions, collector) << ')' if o.rows_from?
|
|
58
|
+
|
|
59
|
+
collector << ' WITH ORDINALITY' if o.ordinality.present?
|
|
60
|
+
collector << ' ' << quote_table_name(o.name)
|
|
61
|
+
|
|
62
|
+
columns = o.alias_columns
|
|
63
|
+
return collector if columns.blank?
|
|
64
|
+
|
|
65
|
+
collector << '('
|
|
66
|
+
visit_alias_columns(columns, collector)
|
|
67
|
+
collector << ')'
|
|
68
|
+
end
|
|
69
|
+
|
|
32
70
|
# Allow casting any node
|
|
33
71
|
def visit_Torque_PostgreSQL_Arel_Nodes_Cast(o, collector)
|
|
34
72
|
visit(o.left, collector) << '::' << o.right
|
|
@@ -36,6 +74,25 @@ module Torque
|
|
|
36
74
|
|
|
37
75
|
private
|
|
38
76
|
|
|
77
|
+
# The column definition list a function has to be told to return
|
|
78
|
+
def visit_definitions(definitions, collector)
|
|
79
|
+
collector << ' AS ('
|
|
80
|
+
visit_alias_columns(definitions, collector)
|
|
81
|
+
collector << ')'
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# A name on its own when it has no type, otherwise the name and the
|
|
85
|
+
# type it is being given
|
|
86
|
+
def visit_alias_columns(columns, collector)
|
|
87
|
+
columns.each_with_index do |(name, type), index|
|
|
88
|
+
collector << ', ' unless index.zero?
|
|
89
|
+
collector << quote_column_name(name)
|
|
90
|
+
collector << ' ' << type if type.present?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
collector
|
|
94
|
+
end
|
|
95
|
+
|
|
39
96
|
def quote_array(value, collector)
|
|
40
97
|
value = value.map(&::Arel::Nodes.method(:build_quoted))
|
|
41
98
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Torque
|
|
4
|
+
module PostgreSQL
|
|
5
|
+
module Associations
|
|
6
|
+
module JoinDependency
|
|
7
|
+
def apply_column_aliases(relation)
|
|
8
|
+
result = super
|
|
9
|
+
return result unless relation.from_clause.empty?
|
|
10
|
+
|
|
11
|
+
relation._select!(root_record_class_marker) if cast_join_root?(relation)
|
|
12
|
+
relation._select!(-> { joined_record_class_markers })
|
|
13
|
+
result
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def cast_join_root?(relation)
|
|
19
|
+
return false if relation.itself_only_value === true
|
|
20
|
+
return false unless join_root_alias
|
|
21
|
+
|
|
22
|
+
join_root.base_klass.physically_inheritances?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def root_record_class_marker
|
|
26
|
+
table = join_root.base_klass.arel_table
|
|
27
|
+
table['tableoid'].pg_cast(:regclass).as(record_class_column_name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The root is projected under its own name and picked up by the
|
|
31
|
+
# column sweep in #instantiate, which only ever feeds the root. Every
|
|
32
|
+
# other join part has to carry the marker inside its own set of
|
|
33
|
+
# aliases, since that is all #extract_record ever reads
|
|
34
|
+
def joined_record_class_markers
|
|
35
|
+
markers = join_root.each_with_index.map do |join_part, index|
|
|
36
|
+
next if index.zero? || !join_part.base_klass.physically_inheritances?
|
|
37
|
+
next if (columns = aliases.column_aliases(join_part)).blank?
|
|
38
|
+
|
|
39
|
+
column_alias = "#{columns.first.alias[/\At\d+/]}_r#{columns.size}"
|
|
40
|
+
columns << ::ActiveRecord::Associations::JoinDependency::Aliases::Column
|
|
41
|
+
.new(record_class_column_name, column_alias)
|
|
42
|
+
|
|
43
|
+
join_part.table['tableoid'].pg_cast(:regclass).as(column_alias)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
markers.compact
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def record_class_column_name
|
|
50
|
+
::ActiveRecord::Relation._record_class_column_name
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -4,7 +4,10 @@ require_relative 'associations/foreign_association'
|
|
|
4
4
|
|
|
5
5
|
require_relative 'associations/builder'
|
|
6
6
|
require_relative 'associations/preloader'
|
|
7
|
+
require_relative 'associations/join_dependency'
|
|
7
8
|
|
|
8
9
|
association_mod = Torque::PostgreSQL::Associations::ForeignAssociation
|
|
9
10
|
::ActiveRecord::Associations::HasManyAssociation.prepend(association_mod)
|
|
10
11
|
::ActiveRecord::Associations::BelongsToManyAssociation.prepend(association_mod)
|
|
12
|
+
|
|
13
|
+
::ActiveRecord::Associations::JoinDependency.prepend(Torque::PostgreSQL::Associations::JoinDependency)
|