torque-postgresql 2.4.4 → 3.0.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/README.rdoc +0 -17
- data/lib/torque/postgresql/adapter/database_statements.rb +32 -74
- data/lib/torque/postgresql/adapter/oid/enum_set.rb +1 -1
- data/lib/torque/postgresql/adapter/oid.rb +0 -3
- data/lib/torque/postgresql/adapter/quoting.rb +12 -20
- data/lib/torque/postgresql/adapter/schema_creation.rb +1 -2
- data/lib/torque/postgresql/adapter/schema_definitions.rb +0 -37
- data/lib/torque/postgresql/adapter/schema_dumper.rb +2 -60
- data/lib/torque/postgresql/adapter/schema_statements.rb +2 -74
- data/lib/torque/postgresql/adapter.rb +2 -11
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +7 -6
- data/lib/torque/postgresql/associations/{association.rb → foreign_association.rb} +1 -4
- data/lib/torque/postgresql/associations/preloader/association.rb +53 -26
- data/lib/torque/postgresql/associations/preloader/loader_query.rb +36 -0
- data/lib/torque/postgresql/associations/preloader.rb +1 -0
- data/lib/torque/postgresql/associations.rb +6 -1
- data/lib/torque/postgresql/attributes/builder/period.rb +6 -2
- data/lib/torque/postgresql/auxiliary_statement/settings.rb +22 -75
- data/lib/torque/postgresql/auxiliary_statement.rb +40 -39
- data/lib/torque/postgresql/base.rb +13 -33
- data/lib/torque/postgresql/config.rb +3 -30
- data/lib/torque/postgresql/inheritance.rb +1 -3
- data/lib/torque/postgresql/migration/command_recorder.rb +2 -12
- data/lib/torque/postgresql/railtie.rb +1 -5
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +44 -20
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +2 -2
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +15 -28
- data/lib/torque/postgresql/relation.rb +10 -12
- data/lib/torque/postgresql/schema_cache.rb +2 -7
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql.rb +1 -2
- data/lib/torque-postgresql.rb +0 -1
- data/spec/schema.rb +14 -30
- data/spec/spec_helper.rb +1 -2
- data/spec/tests/arel_spec.rb +2 -4
- data/spec/tests/auxiliary_statement_spec.rb +35 -374
- data/spec/tests/belongs_to_many_spec.rb +2 -99
- data/spec/tests/distinct_on_spec.rb +1 -1
- data/spec/tests/enum_set_spec.rb +10 -10
- data/spec/tests/enum_spec.rb +0 -90
- data/spec/tests/has_many_spec.rb +0 -46
- data/spec/tests/relation_spec.rb +1 -1
- data/spec/tests/table_inheritance_spec.rb +15 -11
- metadata +11 -37
- data/lib/torque/postgresql/auxiliary_statement/recursive.rb +0 -149
- data/lib/torque/postgresql/table_name.rb +0 -41
- data/lib/torque/range.rb +0 -22
- data/spec/models/category.rb +0 -2
- data/spec/models/internal/user.rb +0 -5
- data/spec/tests/range_spec.rb +0 -36
- data/spec/tests/schema_spec.rb +0 -134
@@ -11,8 +11,61 @@ module Torque
|
|
11
11
|
# For reflections connected through an array, make sure to properly
|
12
12
|
# decuple the list of ids and set them as associated with the owner
|
13
13
|
def run
|
14
|
+
return self if run?
|
14
15
|
return super unless connected_through_array?
|
16
|
+
|
17
|
+
@run = true
|
15
18
|
send("run_array_for_#{@reflection.macro}")
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
# Correctly correlate records when they are connected theough an array
|
23
|
+
def set_inverse(record)
|
24
|
+
return super unless connected_through_array? && @reflection.macro == :has_many
|
25
|
+
|
26
|
+
# Only the first owner is associated following the same instruction
|
27
|
+
# on the original implementation
|
28
|
+
convert_key(record[association_key_name])&.each do |key|
|
29
|
+
if owners = owners_by_key[key]
|
30
|
+
association = owners.first.association(reflection.name)
|
31
|
+
association.set_inverse_instance(record)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Requires a slight change when running on has many since the value
|
37
|
+
# of the foreign key being an array
|
38
|
+
def load_records(raw_records = nil)
|
39
|
+
return super unless connected_through_array? && @reflection.macro == :has_many
|
40
|
+
|
41
|
+
@records_by_owner = {}.compare_by_identity
|
42
|
+
raw_records ||= loader_query.records_for([self])
|
43
|
+
|
44
|
+
@preloaded_records = raw_records.select do |record|
|
45
|
+
assignments = false
|
46
|
+
|
47
|
+
keys = convert_key(record[association_key_name]) || []
|
48
|
+
owners_by_key.values_at(*keys).each do |owner|
|
49
|
+
entries = (@records_by_owner[owner] ||= [])
|
50
|
+
|
51
|
+
if reflection.collection? || entries.empty?
|
52
|
+
entries << record
|
53
|
+
assignments = true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
assignments
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Make sure to change the process when connected through an array
|
62
|
+
def owners_by_key
|
63
|
+
return super unless connected_through_array?
|
64
|
+
@owners_by_key ||= owners.each_with_object({}) do |owner, result|
|
65
|
+
Array.wrap(convert_key(owner[owner_key_name])).each do |key|
|
66
|
+
(result[key] ||= []) << owner
|
67
|
+
end
|
68
|
+
end
|
16
69
|
end
|
17
70
|
|
18
71
|
private
|
@@ -41,32 +94,6 @@ module Torque
|
|
41
94
|
end
|
42
95
|
end
|
43
96
|
|
44
|
-
if PostgreSQL::AR604
|
45
|
-
# This is how Rails 6.0.4 and 6.1 now load the records
|
46
|
-
def load_records
|
47
|
-
return super unless connected_through_array?
|
48
|
-
|
49
|
-
@records_by_owner = {}.compare_by_identity
|
50
|
-
raw_records = owner_keys.empty? ? [] : records_for(owner_keys)
|
51
|
-
|
52
|
-
@preloaded_records = raw_records.select do |record|
|
53
|
-
assignments = false
|
54
|
-
|
55
|
-
ids = convert_key(record[association_key_name])
|
56
|
-
owners_by_key.values_at(*ids).flat_map do |owner|
|
57
|
-
entries = (@records_by_owner[owner] ||= [])
|
58
|
-
|
59
|
-
if reflection.collection? || entries.empty?
|
60
|
-
entries << record
|
61
|
-
assignments = true
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
assignments
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
97
|
# Build correctly the constraint condition in order to get the
|
71
98
|
# associated ids
|
72
99
|
def records_for(ids, &block)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Torque
|
4
|
+
module PostgreSQL
|
5
|
+
module Associations
|
6
|
+
module Preloader
|
7
|
+
module LoaderQuery
|
8
|
+
def foreign_column
|
9
|
+
@foreign_column ||= scope.columns_hash[association_key_name]
|
10
|
+
end
|
11
|
+
|
12
|
+
def load_records_for_keys(keys, &block)
|
13
|
+
condition = query_condition_for(keys)
|
14
|
+
scope.where(condition).load(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def query_condition_for(keys)
|
18
|
+
if connected_through_array?
|
19
|
+
value = scope.cast_for_condition(foreign_column, keys.to_a)
|
20
|
+
scope.table[association_key_name].overlaps(value)
|
21
|
+
else
|
22
|
+
{ association_key_name => keys }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def connected_through_array?
|
27
|
+
foreign_column.array?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
::ActiveRecord::Associations::Preloader::Association::LoaderQuery
|
32
|
+
.prepend(LoaderQuery)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,5 +1,10 @@
|
|
1
|
-
require_relative 'associations/association'
|
2
1
|
require_relative 'associations/association_scope'
|
3
2
|
require_relative 'associations/belongs_to_many_association'
|
3
|
+
require_relative 'associations/foreign_association'
|
4
|
+
|
4
5
|
require_relative 'associations/builder'
|
5
6
|
require_relative 'associations/preloader'
|
7
|
+
|
8
|
+
association_mod = Torque::PostgreSQL::Associations::ForeignAssociation
|
9
|
+
::ActiveRecord::Associations::HasManyAssociation.prepend(association_mod)
|
10
|
+
::ActiveRecord::Associations::BelongsToManyAssociation.prepend(association_mod)
|
@@ -220,7 +220,7 @@ module Torque
|
|
220
220
|
@arel_threshold_value ||= begin
|
221
221
|
case threshold
|
222
222
|
when Symbol, String
|
223
|
-
"
|
223
|
+
"arel_table['#{threshold}']"
|
224
224
|
when ActiveSupport::Duration
|
225
225
|
value = "'#{threshold.to_i} seconds'"
|
226
226
|
"::Arel.sql(\"#{value}\").cast(:interval)"
|
@@ -453,7 +453,11 @@ module Torque
|
|
453
453
|
attr_value = threshold.present? ? method_names[:real] : attribute
|
454
454
|
default_value = default.inspect
|
455
455
|
|
456
|
-
|
456
|
+
[
|
457
|
+
"return #{default_value} if #{attr_value}.nil?",
|
458
|
+
"(#{attr_value}.min.try(:infinite?) || #{attr_value}.min <= value) &&",
|
459
|
+
" (#{attr_value}.max.try(:infinite?) || #{attr_value}.max > value)",
|
460
|
+
].join("\n")
|
457
461
|
end
|
458
462
|
|
459
463
|
def instance_start
|
@@ -4,9 +4,9 @@ module Torque
|
|
4
4
|
module PostgreSQL
|
5
5
|
class AuxiliaryStatement
|
6
6
|
class Settings < Collector.new(:attributes, :join, :join_type, :query, :requires,
|
7
|
-
:polymorphic, :through
|
7
|
+
:polymorphic, :through)
|
8
8
|
|
9
|
-
attr_reader :base, :source
|
9
|
+
attr_reader :base, :source
|
10
10
|
alias_method :select, :attributes
|
11
11
|
alias_method :cte, :source
|
12
12
|
|
@@ -14,10 +14,9 @@ module Torque
|
|
14
14
|
delegate :table, :table_name, to: :@source
|
15
15
|
delegate :sql, to: ::Arel
|
16
16
|
|
17
|
-
def initialize(base, source
|
17
|
+
def initialize(base, source)
|
18
18
|
@base = base
|
19
19
|
@source = source
|
20
|
-
@recursive = recursive
|
21
20
|
end
|
22
21
|
|
23
22
|
def base_name
|
@@ -28,39 +27,6 @@ module Torque
|
|
28
27
|
@base.arel_table
|
29
28
|
end
|
30
29
|
|
31
|
-
|
32
|
-
def recursive?
|
33
|
-
@recursive
|
34
|
-
end
|
35
|
-
|
36
|
-
def depth?
|
37
|
-
defined?(@depth)
|
38
|
-
end
|
39
|
-
|
40
|
-
def path?
|
41
|
-
defined?(@path)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Add an attribute to the result showing the depth of each iteration
|
45
|
-
def with_depth(name = 'depth', start: 0, as: nil)
|
46
|
-
@depth = [name.to_s, start, as&.to_s] if recursive?
|
47
|
-
end
|
48
|
-
|
49
|
-
# Add an attribute to the result showing the path of each record
|
50
|
-
def with_path(name = 'path', source: nil, as: nil)
|
51
|
-
@path = [name.to_s, source&.to_s, as&.to_s] if recursive?
|
52
|
-
end
|
53
|
-
|
54
|
-
# Set recursive operation to use union all
|
55
|
-
def union_all!
|
56
|
-
@union_all = true if recursive?
|
57
|
-
end
|
58
|
-
|
59
|
-
# Add both depth and path to the result
|
60
|
-
def with_depth_and_path
|
61
|
-
with_depth && with_path
|
62
|
-
end
|
63
|
-
|
64
30
|
# Get the arel version of the table set on the query
|
65
31
|
def query_table
|
66
32
|
raise StandardError, 'The query is not defined yet' if query.nil?
|
@@ -75,55 +41,36 @@ module Torque
|
|
75
41
|
|
76
42
|
alias column col
|
77
43
|
|
78
|
-
# There are
|
44
|
+
# There are two ways of setting the query:
|
79
45
|
# - A simple relation based on a Model
|
80
46
|
# - A Arel-based select manager
|
81
|
-
# - A string or a proc
|
47
|
+
# - A string or a proc that requires the table name as first argument
|
82
48
|
def query(value = nil, command = nil)
|
83
49
|
return @query if value.nil?
|
50
|
+
return @query = value if relation_query?(value)
|
84
51
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
return unless recursive?
|
91
|
-
return @sub_query if value.nil?
|
52
|
+
if value.is_a?(::Arel::SelectManager)
|
53
|
+
@query = value
|
54
|
+
@query_table = value.source.left.name
|
55
|
+
return
|
56
|
+
end
|
92
57
|
|
93
|
-
|
94
|
-
end
|
58
|
+
valid_type = command.respond_to?(:call) || command.is_a?(String)
|
95
59
|
|
96
|
-
|
97
|
-
|
98
|
-
|
60
|
+
raise ArgumentError, <<-MSG.squish if command.nil?
|
61
|
+
To use proc or string as query, you need to provide the table name
|
62
|
+
as the first argument
|
63
|
+
MSG
|
99
64
|
|
100
|
-
|
101
|
-
|
102
|
-
|
65
|
+
raise ArgumentError, <<-MSG.squish unless valid_type
|
66
|
+
Only relation, string and proc are valid object types for query,
|
67
|
+
#{command.inspect} given.
|
68
|
+
MSG
|
103
69
|
|
104
|
-
@
|
70
|
+
@query = command
|
71
|
+
@query_table = ::Arel::Table.new(value)
|
105
72
|
end
|
106
73
|
|
107
|
-
alias connect= connect
|
108
|
-
|
109
|
-
private
|
110
|
-
|
111
|
-
# Get the query and table from the params
|
112
|
-
def sanitize_query(value, command = nil)
|
113
|
-
return value if relation_query?(value)
|
114
|
-
return value if value.is_a?(::Arel::SelectManager)
|
115
|
-
|
116
|
-
command = value if command.nil? # For compatibility purposes
|
117
|
-
valid_type = command.respond_to?(:call) || command.is_a?(String)
|
118
|
-
|
119
|
-
raise ArgumentError, <<-MSG.squish unless valid_type
|
120
|
-
Only relation, string and proc are valid object types for query,
|
121
|
-
#{command.inspect} given.
|
122
|
-
MSG
|
123
|
-
|
124
|
-
command
|
125
|
-
end
|
126
|
-
|
127
74
|
end
|
128
75
|
end
|
129
76
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'auxiliary_statement/settings'
|
4
|
-
require_relative 'auxiliary_statement/recursive'
|
5
4
|
|
6
5
|
module Torque
|
7
6
|
module PostgreSQL
|
@@ -9,20 +8,17 @@ module Torque
|
|
9
8
|
TABLE_COLUMN_AS_STRING = /\A(?:"?(\w+)"?\.)?"?(\w+)"?\z/.freeze
|
10
9
|
|
11
10
|
class << self
|
12
|
-
attr_reader :config
|
11
|
+
attr_reader :config
|
13
12
|
|
14
13
|
# Find or create the class that will handle statement
|
15
14
|
def lookup(name, base)
|
16
15
|
const = name.to_s.camelize << '_' << self.name.demodulize
|
17
16
|
return base.const_get(const, false) if base.const_defined?(const, false)
|
18
|
-
|
19
|
-
base.const_set(const, Class.new(self)).tap do |klass|
|
20
|
-
klass.instance_variable_set(:@table_name, name.to_s)
|
21
|
-
end
|
17
|
+
base.const_set(const, Class.new(AuxiliaryStatement))
|
22
18
|
end
|
23
19
|
|
24
20
|
# Create a new instance of an auxiliary statement
|
25
|
-
def instantiate(statement, base,
|
21
|
+
def instantiate(statement, base, options = nil)
|
26
22
|
klass = while base < ActiveRecord::Base
|
27
23
|
list = base.auxiliary_statements_list
|
28
24
|
break list[statement] if list.present? && list.key?(statement)
|
@@ -30,15 +26,15 @@ module Torque
|
|
30
26
|
base = base.superclass
|
31
27
|
end
|
32
28
|
|
33
|
-
return klass.new(
|
29
|
+
return klass.new(options) unless klass.nil?
|
34
30
|
raise ArgumentError, <<-MSG.squish
|
35
31
|
There's no '#{statement}' auxiliary statement defined for #{base.class.name}.
|
36
32
|
MSG
|
37
33
|
end
|
38
34
|
|
39
35
|
# Fast access to statement build
|
40
|
-
def build(statement, base, bound_attributes = [], join_sources = []
|
41
|
-
klass = instantiate(statement, base,
|
36
|
+
def build(statement, base, options = nil, bound_attributes = [], join_sources = [])
|
37
|
+
klass = instantiate(statement, base, options)
|
42
38
|
result = klass.build(base)
|
43
39
|
|
44
40
|
bound_attributes.concat(klass.bound_attributes)
|
@@ -60,7 +56,7 @@ module Torque
|
|
60
56
|
# A way to create auxiliary statements outside of models configurations,
|
61
57
|
# being able to use on extensions
|
62
58
|
def create(table_or_settings, &block)
|
63
|
-
klass = Class.new(
|
59
|
+
klass = Class.new(AuxiliaryStatement)
|
64
60
|
|
65
61
|
if block_given?
|
66
62
|
klass.instance_variable_set(:@table_name, table_or_settings)
|
@@ -93,8 +89,7 @@ module Torque
|
|
93
89
|
def configure(base, instance)
|
94
90
|
return @config unless @config.respond_to?(:call)
|
95
91
|
|
96
|
-
|
97
|
-
settings = Settings.new(base, instance, recursive)
|
92
|
+
settings = Settings.new(base, instance)
|
98
93
|
settings.instance_exec(settings, &@config)
|
99
94
|
settings
|
100
95
|
end
|
@@ -103,6 +98,11 @@ module Torque
|
|
103
98
|
def table
|
104
99
|
@table ||= ::Arel::Table.new(table_name)
|
105
100
|
end
|
101
|
+
|
102
|
+
# Get the name of the table of the configurated statement
|
103
|
+
def table_name
|
104
|
+
@table_name ||= self.name.demodulize.split('_').first.underscore
|
105
|
+
end
|
106
106
|
end
|
107
107
|
|
108
108
|
delegate :config, :table, :table_name, :relation, :configure, :relation_query?,
|
@@ -111,14 +111,15 @@ module Torque
|
|
111
111
|
attr_reader :bound_attributes, :join_sources
|
112
112
|
|
113
113
|
# Start a new auxiliary statement giving extra options
|
114
|
-
def initialize(
|
114
|
+
def initialize(*args)
|
115
|
+
options = args.extract_options!
|
115
116
|
args_key = Torque::PostgreSQL.config.auxiliary_statement.send_arguments_key
|
116
117
|
|
117
118
|
@join = options.fetch(:join, {})
|
118
119
|
@args = options.fetch(args_key, {})
|
119
120
|
@where = options.fetch(:where, {})
|
120
121
|
@select = options.fetch(:select, {})
|
121
|
-
@join_type = options
|
122
|
+
@join_type = options.fetch(:join_type, nil)
|
122
123
|
|
123
124
|
@bound_attributes = []
|
124
125
|
@join_sources = []
|
@@ -130,7 +131,7 @@ module Torque
|
|
130
131
|
@join_sources.clear
|
131
132
|
|
132
133
|
# Prepare all the data for the statement
|
133
|
-
prepare(base
|
134
|
+
prepare(base)
|
134
135
|
|
135
136
|
# Add the join condition to the list
|
136
137
|
@join_sources << build_join(base)
|
@@ -141,7 +142,8 @@ module Torque
|
|
141
142
|
|
142
143
|
private
|
143
144
|
# Setup the statement using the class configuration
|
144
|
-
def prepare(base
|
145
|
+
def prepare(base)
|
146
|
+
settings = configure(base, self)
|
145
147
|
requires = Array.wrap(settings.requires).flatten.compact
|
146
148
|
@dependencies = ensure_dependencies(requires, base).flatten.compact
|
147
149
|
|
@@ -149,13 +151,14 @@ module Torque
|
|
149
151
|
@query = settings.query
|
150
152
|
|
151
153
|
# Call a proc to get the real query
|
152
|
-
if @query.
|
154
|
+
if @query.methods.include?(:call)
|
153
155
|
call_args = @query.try(:arity) === 0 ? [] : [OpenStruct.new(@args)]
|
154
156
|
@query = @query.call(*call_args)
|
155
157
|
@args = []
|
156
158
|
end
|
157
159
|
|
158
|
-
#
|
160
|
+
# Manually set the query table when it's not an relation query
|
161
|
+
@query_table = settings.query_table unless relation_query?(@query)
|
159
162
|
@select = settings.attributes.merge(@select) if settings.attributes.present?
|
160
163
|
|
161
164
|
# Merge join settings
|
@@ -165,7 +168,7 @@ module Torque
|
|
165
168
|
@association = settings.through.to_s
|
166
169
|
elsif relation_query?(@query)
|
167
170
|
@association = base.reflections.find do |name, reflection|
|
168
|
-
break name if @query.klass.eql?
|
171
|
+
break name if @query.klass.eql? reflection.klass
|
169
172
|
end
|
170
173
|
end
|
171
174
|
end
|
@@ -231,6 +234,15 @@ module Torque
|
|
231
234
|
as a query object on #{self.class.name}.
|
232
235
|
MSG
|
233
236
|
|
237
|
+
# Expose join columns
|
238
|
+
if relation_query?(@query)
|
239
|
+
query_table = @query.arel_table
|
240
|
+
conditions.children.each do |item|
|
241
|
+
@query.select_values += [query_table[item.left.name]] \
|
242
|
+
if item.left.relation.eql?(table)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
234
246
|
# Build the join based on the join type
|
235
247
|
arel_join.new(table, table.create_on(conditions))
|
236
248
|
end
|
@@ -251,31 +263,21 @@ module Torque
|
|
251
263
|
|
252
264
|
# Mount the list of selected attributes
|
253
265
|
def expose_columns(base, query_table = nil)
|
254
|
-
# Add the columns necessary for the join
|
255
|
-
list = @join_sources.each_with_object(@select) do |join, hash|
|
256
|
-
join.right.expr.children.each do |item|
|
257
|
-
hash[item.left.name] = nil if item.left.relation.eql?(table)
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
266
|
# Add select columns to the query and get exposed columns
|
262
|
-
|
263
|
-
base.select_extra_values += [table[right.to_s]]
|
264
|
-
|
265
|
-
|
266
|
-
col = project(left, query_table)
|
267
|
-
right.nil? ? col : col.as(right.to_s)
|
268
|
-
end.compact
|
267
|
+
@select.map do |left, right|
|
268
|
+
base.select_extra_values += [table[right.to_s]]
|
269
|
+
project(left, query_table).as(right.to_s) if query_table
|
270
|
+
end
|
269
271
|
end
|
270
272
|
|
271
273
|
# Ensure that all the dependencies are loaded in the base relation
|
272
274
|
def ensure_dependencies(list, base)
|
273
275
|
with_options = list.extract_options!.to_a
|
274
|
-
(list + with_options).map do |
|
275
|
-
dependent_klass = base.model.auxiliary_statements_list[
|
276
|
+
(list + with_options).map do |dependent, options|
|
277
|
+
dependent_klass = base.model.auxiliary_statements_list[dependent]
|
276
278
|
|
277
279
|
raise ArgumentError, <<-MSG.squish if dependent_klass.nil?
|
278
|
-
The '#{
|
280
|
+
The '#{dependent}' auxiliary statement dependency can't found on
|
279
281
|
#{self.class.name}.
|
280
282
|
MSG
|
281
283
|
|
@@ -283,8 +285,7 @@ module Torque
|
|
283
285
|
cte.is_a?(dependent_klass)
|
284
286
|
end
|
285
287
|
|
286
|
-
options
|
287
|
-
AuxiliaryStatement.build(name, base, bound_attributes, join_sources, **options)
|
288
|
+
AuxiliaryStatement.build(dependent, base, options, bound_attributes, join_sources)
|
288
289
|
end
|
289
290
|
end
|
290
291
|
|
@@ -5,27 +5,15 @@ module Torque
|
|
5
5
|
module Base
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
|
-
##
|
9
|
-
# :singleton-method: schema
|
10
|
-
# :call-seq: schema
|
11
|
-
#
|
12
|
-
# The schema to which the table belongs to.
|
13
|
-
|
14
8
|
included do
|
15
9
|
mattr_accessor :belongs_to_many_required_by_default, instance_accessor: false
|
16
|
-
class_attribute :schema, instance_writer: false
|
17
10
|
end
|
18
11
|
|
19
12
|
module ClassMethods
|
20
13
|
delegate :distinct_on, :with, :itself_only, :cast_records, to: :all
|
21
14
|
|
22
|
-
#
|
23
|
-
|
24
|
-
self.table_name = TableName.new(self, super)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Whenever the base model is inherited, add a list of auxiliary
|
28
|
-
# statements like the one that loads inherited records' relname
|
15
|
+
# Wenever it's inherited, add a new list of auxiliary statements
|
16
|
+
# It also adds an auxiliary statement to load inherited records' relname
|
29
17
|
def inherited(subclass)
|
30
18
|
super
|
31
19
|
|
@@ -36,11 +24,6 @@ module Torque
|
|
36
24
|
|
37
25
|
# Define helper methods to return the class of the given records
|
38
26
|
subclass.auxiliary_statement record_class do |cte|
|
39
|
-
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
40
|
-
Inheritance does not use this auxiliary statement and it can be removed.
|
41
|
-
You can replace it with `model.select_extra_values << 'tableoid::regclass'`.
|
42
|
-
MSG
|
43
|
-
|
44
27
|
pg_class = ::Arel::Table.new('pg_class')
|
45
28
|
arel_query = ::Arel::SelectManager.new(pg_class)
|
46
29
|
arel_query.project(pg_class['oid'], pg_class['relname'].as(record_class.to_s))
|
@@ -53,11 +36,18 @@ module Torque
|
|
53
36
|
# Define the dynamic attribute that returns the same information as
|
54
37
|
# the one provided by the auxiliary statement
|
55
38
|
subclass.dynamic_attribute(record_class) do
|
56
|
-
|
57
|
-
|
39
|
+
next self.class.table_name unless self.class.physically_inheritances?
|
40
|
+
|
41
|
+
pg_class = ::Arel::Table.new('pg_class')
|
42
|
+
source = ::Arel::Table.new(subclass.table_name, as: 'source')
|
43
|
+
quoted_id = ::Arel::Nodes::Quoted.new(id)
|
58
44
|
|
59
|
-
query =
|
60
|
-
query.
|
45
|
+
query = ::Arel::SelectManager.new(pg_class)
|
46
|
+
query.join(source).on(pg_class['oid'].eq(source['tableoid']))
|
47
|
+
query.where(source[subclass.primary_key].eq(quoted_id))
|
48
|
+
query.project(pg_class['relname'])
|
49
|
+
|
50
|
+
self.class.connection.select_value(query)
|
61
51
|
end
|
62
52
|
end
|
63
53
|
|
@@ -309,16 +299,6 @@ module Torque
|
|
309
299
|
klass.configurator(block)
|
310
300
|
end
|
311
301
|
alias cte auxiliary_statement
|
312
|
-
|
313
|
-
# Creates a new recursive auxiliary statement (CTE) under the base
|
314
|
-
# Very similar to the regular auxiliary statement, but with two-part
|
315
|
-
# query where one is executed first and the second recursively
|
316
|
-
def recursive_auxiliary_statement(table, &block)
|
317
|
-
klass = AuxiliaryStatement::Recursive.lookup(table, self)
|
318
|
-
auxiliary_statements_list[table.to_sym] = klass
|
319
|
-
klass.configurator(block)
|
320
|
-
end
|
321
|
-
alias recursive_cte recursive_auxiliary_statement
|
322
302
|
end
|
323
303
|
end
|
324
304
|
|
@@ -4,11 +4,6 @@ module Torque
|
|
4
4
|
module PostgreSQL
|
5
5
|
include ActiveSupport::Configurable
|
6
6
|
|
7
|
-
# Stores a version check for compatibility purposes
|
8
|
-
AR604 = (ActiveRecord.gem_version >= Gem::Version.new('6.0.4'))
|
9
|
-
AR610 = (ActiveRecord.gem_version >= Gem::Version.new('6.1.0'))
|
10
|
-
AR615 = (ActiveRecord.gem_version >= Gem::Version.new('6.1.5'))
|
11
|
-
|
12
7
|
# Use the same logger as the Active Record one
|
13
8
|
def self.logger
|
14
9
|
ActiveRecord::Base.logger
|
@@ -27,11 +22,6 @@ module Torque
|
|
27
22
|
# same configuration is set to true
|
28
23
|
config.eager_load = false
|
29
24
|
|
30
|
-
# This allows default values to have extended values like arrays and casted
|
31
|
-
# values. Extended defaults are still experimental, so enable and test it
|
32
|
-
# before using it in prod
|
33
|
-
config.use_extended_defaults = false
|
34
|
-
|
35
25
|
# Set a list of irregular model name when associated with table names
|
36
26
|
config.irregular_models = {}
|
37
27
|
def config.irregular_models=(hash)
|
@@ -40,19 +30,6 @@ module Torque
|
|
40
30
|
end.to_h
|
41
31
|
end
|
42
32
|
|
43
|
-
# Configure multiple schemas
|
44
|
-
config.nested(:schemas) do |schemas|
|
45
|
-
|
46
|
-
# Defines a list of LIKE-based schemas to not consider for a multiple
|
47
|
-
# schema database
|
48
|
-
schemas.blacklist = %w[information_schema pg_%]
|
49
|
-
|
50
|
-
# Defines a list of LIKE-based schemas to consider for a multiple schema
|
51
|
-
# database
|
52
|
-
schemas.whitelist = %w[public]
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
33
|
# Configure associations features
|
57
34
|
config.nested(:associations) do |assoc|
|
58
35
|
|
@@ -69,14 +46,10 @@ module Torque
|
|
69
46
|
# arguments to format string or send on a proc
|
70
47
|
cte.send_arguments_key = :args
|
71
48
|
|
72
|
-
# Estipulate a class name (which may contain namespace) that
|
49
|
+
# Estipulate a class name (which may contain namespace) that expose the
|
73
50
|
# auxiliary statement in order to perform detached CTEs
|
74
51
|
cte.exposed_class = 'TorqueCTE'
|
75
52
|
|
76
|
-
# Estipulate a class name (which may contain namespace) that exposes the
|
77
|
-
# recursive auxiliary statement in order to perform detached CTEs
|
78
|
-
cte.exposed_recursive_class = 'TorqueRecursiveCTE'
|
79
|
-
|
80
53
|
end
|
81
54
|
|
82
55
|
# Configure ENUM features
|
@@ -84,11 +57,11 @@ module Torque
|
|
84
57
|
|
85
58
|
# The name of the method to be used on any ActiveRecord::Base to
|
86
59
|
# initialize model-based enum features
|
87
|
-
enum.base_method = :
|
60
|
+
enum.base_method = :torque_enum
|
88
61
|
|
89
62
|
# The name of the method to be used on any ActiveRecord::Base to
|
90
63
|
# initialize model-based enum set features
|
91
|
-
enum.set_method = :
|
64
|
+
enum.set_method = :torque_enum_set
|
92
65
|
|
93
66
|
# Indicates if bang methods like 'disabled!' should update the record on
|
94
67
|
# database or not
|
@@ -55,9 +55,7 @@ module Torque
|
|
55
55
|
|
56
56
|
# Check if the model's table depends on any inheritance
|
57
57
|
def physically_inherited?
|
58
|
-
|
59
|
-
|
60
|
-
@physically_inherited = connection.schema_cache.dependencies(
|
58
|
+
@physically_inherited ||= connection.schema_cache.dependencies(
|
61
59
|
defined?(@table_name) ? @table_name : decorated_table_name,
|
62
60
|
).present?
|
63
61
|
rescue ActiveRecord::ConnectionNotEstablished
|