active_record_extended 1.1.0 → 2.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.md +87 -15
- data/lib/active_record_extended.rb +2 -1
- data/lib/active_record_extended/active_record.rb +2 -9
- data/lib/active_record_extended/active_record/relation_patch.rb +21 -4
- data/lib/active_record_extended/arel.rb +2 -0
- data/lib/active_record_extended/arel/aggregate_function_name.rb +40 -0
- data/lib/active_record_extended/arel/nodes.rb +32 -41
- data/lib/active_record_extended/arel/predications.rb +4 -1
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +40 -1
- data/lib/active_record_extended/query_methods/any_of.rb +10 -8
- data/lib/active_record_extended/query_methods/either.rb +1 -1
- data/lib/active_record_extended/query_methods/inet.rb +7 -3
- data/lib/active_record_extended/query_methods/json.rb +156 -50
- data/lib/active_record_extended/query_methods/select.rb +118 -0
- data/lib/active_record_extended/query_methods/unionize.rb +14 -43
- data/lib/active_record_extended/query_methods/where_chain.rb +14 -6
- data/lib/active_record_extended/query_methods/window.rb +93 -0
- data/lib/active_record_extended/query_methods/with_cte.rb +102 -35
- data/lib/active_record_extended/utilities/order_by.rb +77 -0
- data/lib/active_record_extended/utilities/support.rb +178 -0
- data/lib/active_record_extended/version.rb +1 -1
- data/spec/query_methods/any_of_spec.rb +40 -40
- data/spec/query_methods/array_query_spec.rb +14 -14
- data/spec/query_methods/either_spec.rb +14 -14
- data/spec/query_methods/hash_query_spec.rb +11 -11
- data/spec/query_methods/inet_query_spec.rb +33 -31
- data/spec/query_methods/json_spec.rb +42 -27
- data/spec/query_methods/select_spec.rb +115 -0
- data/spec/query_methods/unionize_spec.rb +56 -56
- data/spec/query_methods/window_spec.rb +51 -0
- data/spec/query_methods/with_cte_spec.rb +22 -12
- data/spec/spec_helper.rb +1 -1
- data/spec/sql_inspections/any_of_sql_spec.rb +12 -12
- data/spec/sql_inspections/arel/aggregate_function_name_spec.rb +41 -0
- data/spec/sql_inspections/arel/array_spec.rb +7 -7
- data/spec/sql_inspections/arel/inet_spec.rb +7 -7
- data/spec/sql_inspections/contains_sql_queries_spec.rb +14 -14
- data/spec/sql_inspections/either_sql_spec.rb +11 -11
- data/spec/sql_inspections/json_sql_spec.rb +44 -8
- data/spec/sql_inspections/unionize_sql_spec.rb +27 -27
- data/spec/sql_inspections/window_sql_spec.rb +98 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +52 -23
- data/spec/support/models.rb +24 -4
- metadata +31 -20
- data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +0 -87
- data/lib/active_record_extended/utilities.rb +0 -141
@@ -7,7 +7,8 @@ module ActiveRecordExtended
|
|
7
7
|
UNIONIZE_METHODS = [:union, :union_all, :union_except, :union_intersect].freeze
|
8
8
|
|
9
9
|
class UnionChain
|
10
|
-
include ::ActiveRecordExtended::Utilities
|
10
|
+
include ::ActiveRecordExtended::Utilities::Support
|
11
|
+
include ::ActiveRecordExtended::Utilities::OrderBy
|
11
12
|
|
12
13
|
def initialize(scope)
|
13
14
|
@scope = scope
|
@@ -58,7 +59,7 @@ module ActiveRecordExtended
|
|
58
59
|
protected
|
59
60
|
|
60
61
|
def append_union_order!(union_type, args)
|
61
|
-
args.each
|
62
|
+
args.each { |arg| pipe_cte_with!(arg) }
|
62
63
|
flatten_scopes = flatten_to_sql(args)
|
63
64
|
@scope.union_values += flatten_scopes
|
64
65
|
calculate_union_operation!(union_type, flatten_scopes.size)
|
@@ -69,38 +70,6 @@ module ActiveRecordExtended
|
|
69
70
|
scope_count = 1 if scope_count <= 0 && @scope.union_values.size <= 1
|
70
71
|
@scope.union_operations += [union_type] * scope_count
|
71
72
|
end
|
72
|
-
|
73
|
-
# We'll need to preprocess these arguments for allowing `ActiveRecord::Relation#preprocess_order_args`,
|
74
|
-
# to check for sanitization issues and convert over to `Arel::Nodes::[Ascending/Descending]`.
|
75
|
-
# Without reflecting / prepending the parent's table name.
|
76
|
-
|
77
|
-
if ActiveRecord.gem_version < Gem::Version.new("5.1")
|
78
|
-
# TODO: Rails 5.0.x order logic will *always* append the parents name to the column when its an HASH obj
|
79
|
-
# We should really do this stuff better. Maybe even just ignore `preprocess_order_args` altogether?
|
80
|
-
# Maybe I'm just stupidly over paranoid on just the 'ORDER BY' for some odd reason.
|
81
|
-
def process_ordering_arguments!(ordering_args)
|
82
|
-
ordering_args.flatten!
|
83
|
-
ordering_args.compact!
|
84
|
-
ordering_args.map! do |arg|
|
85
|
-
next to_arel_sql(arg) unless arg.is_a?(Hash) # ActiveRecord will reflect if an argument is a symbol
|
86
|
-
arg.each_with_object([]) do |(field, dir), ordering_object|
|
87
|
-
ordering_object << to_arel_sql(field).send(dir.to_s.downcase)
|
88
|
-
end
|
89
|
-
end.flatten!
|
90
|
-
end
|
91
|
-
else
|
92
|
-
def process_ordering_arguments!(ordering_args)
|
93
|
-
ordering_args.flatten!
|
94
|
-
ordering_args.compact!
|
95
|
-
ordering_args.map! do |arg|
|
96
|
-
next to_arel_sql(arg) unless arg.is_a?(Hash) # ActiveRecord will reflect if an argument is a symbol
|
97
|
-
arg.each_with_object({}) do |(field, dir), ordering_obj|
|
98
|
-
# ActiveRecord will not reflect if the Hash keys are a `Arel::Nodes::SqlLiteral` klass
|
99
|
-
ordering_obj[to_arel_sql(field)] = dir.to_s.downcase
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
73
|
end
|
105
74
|
|
106
75
|
def unionize_storage
|
@@ -112,7 +81,7 @@ module ActiveRecordExtended
|
|
112
81
|
union_values: [],
|
113
82
|
union_operations: [],
|
114
83
|
union_ordering_values: [],
|
115
|
-
unionized_name: nil
|
84
|
+
unionized_name: nil
|
116
85
|
}
|
117
86
|
end
|
118
87
|
|
@@ -120,10 +89,11 @@ module ActiveRecordExtended
|
|
120
89
|
union_values: Array,
|
121
90
|
union_operations: Array,
|
122
91
|
union_ordering_values: Array,
|
123
|
-
unionized_name: lambda { |klass| klass.arel_table.name }
|
92
|
+
unionized_name: lambda { |klass| klass.arel_table.name }
|
124
93
|
}.each_pair do |method_name, default|
|
125
94
|
define_method(method_name) do
|
126
95
|
return unionize_storage[method_name] if send("#{method_name}?")
|
96
|
+
|
127
97
|
(default.is_a?(Proc) ? default.call(@klass) : default.new)
|
128
98
|
end
|
129
99
|
|
@@ -138,11 +108,13 @@ module ActiveRecordExtended
|
|
138
108
|
|
139
109
|
def union(opts = :chain, *args)
|
140
110
|
return UnionChain.new(spawn) if opts == :chain
|
111
|
+
|
141
112
|
opts.nil? ? self : spawn.union!(opts, *args, chain_method: __callee__)
|
142
113
|
end
|
143
114
|
|
144
115
|
(UNIONIZE_METHODS + UNION_RELATION_METHODS).each do |union_method|
|
145
116
|
next if union_method == :union
|
117
|
+
|
146
118
|
alias_method union_method, :union
|
147
119
|
end
|
148
120
|
|
@@ -157,11 +129,13 @@ module ActiveRecordExtended
|
|
157
129
|
# Will construct *Just* the union SQL statement that was been built thus far
|
158
130
|
def to_union_sql
|
159
131
|
return unless union_values?
|
132
|
+
|
160
133
|
apply_union_ordering(build_union_nodes!(false)).to_sql
|
161
134
|
end
|
162
135
|
|
163
136
|
def to_nice_union_sql(color = true)
|
164
137
|
return to_union_sql unless defined?(::Niceql)
|
138
|
+
|
165
139
|
::Niceql::Prettifier.prettify_sql(to_union_sql, color)
|
166
140
|
end
|
167
141
|
|
@@ -171,7 +145,7 @@ module ActiveRecordExtended
|
|
171
145
|
return unless union_values?
|
172
146
|
|
173
147
|
union_nodes = apply_union_ordering(build_union_nodes!)
|
174
|
-
table_name = Arel
|
148
|
+
table_name = Arel.sql(unionized_name)
|
175
149
|
table_alias = arel.create_table_alias(arel.grouping(union_nodes), table_name)
|
176
150
|
arel.from(table_alias)
|
177
151
|
end
|
@@ -203,7 +177,7 @@ module ActiveRecordExtended
|
|
203
177
|
|
204
178
|
def build_union_nodes!(raise_error = true)
|
205
179
|
unionize_error_or_warn!(raise_error)
|
206
|
-
union_values.each_with_index.
|
180
|
+
union_values.each_with_index.reduce(nil) do |union_node, (relation_node, index)|
|
207
181
|
next resolve_relation_node(relation_node) if union_node.nil?
|
208
182
|
|
209
183
|
operation = union_operations.fetch(index - 1, :union)
|
@@ -247,17 +221,14 @@ module ActiveRecordExtended
|
|
247
221
|
def apply_union_ordering(union_nodes)
|
248
222
|
return union_nodes unless union_ordering_values?
|
249
223
|
|
250
|
-
|
251
|
-
preprocess_order_args(union_ordering_values)
|
252
|
-
union_ordering_values.uniq!
|
253
|
-
Arel::Nodes::InfixOperation.new("ORDER BY", union_nodes, union_ordering_values)
|
224
|
+
UnionChain.new(self).inline_order_by(union_nodes, union_ordering_values)
|
254
225
|
end
|
255
226
|
|
256
227
|
private
|
257
228
|
|
258
229
|
def unionize_error_or_warn!(raise_error = true)
|
259
230
|
if raise_error && union_values.size <= 1
|
260
|
-
raise ArgumentError
|
231
|
+
raise ArgumentError.new("You are required to provide 2 or more unions to join!")
|
261
232
|
elsif !raise_error && union_values.size <= 1
|
262
233
|
warn("Warning: You are required to provide 2 or more unions to join.")
|
263
234
|
end
|
@@ -54,10 +54,10 @@ module ActiveRecordExtended
|
|
54
54
|
elsif column.try(:array)
|
55
55
|
Arel::Nodes::ContainsArray.new(arel.left, arel.right)
|
56
56
|
else
|
57
|
-
raise ArgumentError
|
57
|
+
raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
|
58
58
|
end
|
59
59
|
else
|
60
|
-
raise ArgumentError
|
60
|
+
raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
@@ -88,7 +88,7 @@ module ActiveRecordExtended
|
|
88
88
|
when Arel::Nodes::Equality
|
89
89
|
Arel::Nodes::Equality.new(arel.right, Arel::Nodes::NamedFunction.new(function_name, [arel.left]))
|
90
90
|
else
|
91
|
-
raise ArgumentError
|
91
|
+
raise ArgumentError.new("Invalid argument for .where.#{function_name.downcase}(), got #{arel.class}")
|
92
92
|
end
|
93
93
|
end
|
94
94
|
end
|
@@ -99,10 +99,18 @@ module ActiveRecordExtended
|
|
99
99
|
when Arel::Nodes::In, Arel::Nodes::Equality
|
100
100
|
arel_node_class.new(arel.left, arel.right)
|
101
101
|
else
|
102
|
-
raise ArgumentError
|
102
|
+
raise ArgumentError.new("Invalid argument for .where.#{method}(), got #{arel.class}")
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
106
|
+
|
107
|
+
def build_where_clause_for(scope, opts, rest)
|
108
|
+
if ActiveRecord::VERSION::MAJOR == 6 && ActiveRecord::VERSION::MINOR == 1
|
109
|
+
scope.send(:build_where_clause, opts, rest)
|
110
|
+
else
|
111
|
+
scope.send(:where_clause_factory).build(opts, rest)
|
112
|
+
end
|
113
|
+
end
|
106
114
|
end
|
107
115
|
end
|
108
116
|
|
@@ -112,9 +120,9 @@ module ActiveRecord
|
|
112
120
|
prepend ActiveRecordExtended::WhereChain
|
113
121
|
|
114
122
|
def build_where_chain(opts, rest, &block)
|
115
|
-
where_clause = @scope
|
123
|
+
where_clause = build_where_clause_for(@scope, opts, rest)
|
116
124
|
@scope.tap do |scope|
|
117
|
-
scope.references!(PredicateBuilder.references(opts)) if opts.is_a?(Hash)
|
125
|
+
scope.references!(PredicateBuilder.references(opts.stringify_keys)) if opts.is_a?(Hash)
|
118
126
|
scope.where_clause += where_clause.modified_predicates(&block)
|
119
127
|
end
|
120
128
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordExtended
|
4
|
+
module QueryMethods
|
5
|
+
module Window
|
6
|
+
class DefineWindowChain
|
7
|
+
include ::ActiveRecordExtended::Utilities::Support
|
8
|
+
include ::ActiveRecordExtended::Utilities::OrderBy
|
9
|
+
|
10
|
+
def initialize(scope, window_name)
|
11
|
+
@scope = scope
|
12
|
+
@window_name = window_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def partition_by(*partitions, order_by: nil)
|
16
|
+
@scope.window_values! << {
|
17
|
+
window_name: to_arel_sql(@window_name),
|
18
|
+
partition_by: flatten_to_sql(partitions),
|
19
|
+
order_by: order_by_expression(order_by)
|
20
|
+
}
|
21
|
+
|
22
|
+
@scope
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class WindowSelectBuilder
|
27
|
+
include ::ActiveRecordExtended::Utilities::Support
|
28
|
+
|
29
|
+
def initialize(window_function, args, window_name)
|
30
|
+
@window_function = window_function
|
31
|
+
@win_args = to_sql_array(args)
|
32
|
+
@over = to_arel_sql(window_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_select(alias_name = nil)
|
36
|
+
window_arel = generate_named_function(@window_function, *@win_args).over(@over)
|
37
|
+
|
38
|
+
if alias_name.nil?
|
39
|
+
window_arel
|
40
|
+
else
|
41
|
+
nested_alias_escape(window_arel, alias_name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def window_values
|
47
|
+
@values.fetch(:window, [])
|
48
|
+
end
|
49
|
+
|
50
|
+
def window_values!
|
51
|
+
@values[:window] ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
def window_values?
|
55
|
+
!window_values.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
def window_values=(*values)
|
59
|
+
@values[:window] = values.flatten(1)
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_window(name)
|
63
|
+
spawn.define_window!(name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def define_window!(name)
|
67
|
+
DefineWindowChain.new(self, name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def select_window(window_function, *args, over:, as: nil)
|
71
|
+
spawn.select_window!(window_function, args, over: over, as: as)
|
72
|
+
end
|
73
|
+
|
74
|
+
def select_window!(window_function, *args, over:, as: nil)
|
75
|
+
args.flatten!
|
76
|
+
args.compact!
|
77
|
+
|
78
|
+
select_statement = WindowSelectBuilder.new(window_function, args, over).build_select(as)
|
79
|
+
_select!(select_statement)
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_windows(arel)
|
83
|
+
window_values.each do |window_value|
|
84
|
+
window = arel.window(window_value[:window_name])
|
85
|
+
window = window.partition(window_value[:partition_by]) if window_value[:partition_by].present?
|
86
|
+
window.order(window_value[:order_by]) if window_value[:order_by]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
ActiveRecord::Relation.prepend(ActiveRecordExtended::QueryMethods::Window)
|
@@ -3,78 +3,145 @@
|
|
3
3
|
module ActiveRecordExtended
|
4
4
|
module QueryMethods
|
5
5
|
module WithCTE
|
6
|
-
class
|
6
|
+
class WithCTE
|
7
|
+
include ::ActiveRecordExtended::Utilities::Support
|
8
|
+
include Enumerable
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def_delegators :@with_values, :empty?, :blank?, :present?
|
12
|
+
attr_reader :with_values, :with_keys
|
13
|
+
|
14
|
+
# @param [ActiveRecord::Relation] scope
|
7
15
|
def initialize(scope)
|
8
16
|
@scope = scope
|
17
|
+
reset!
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Enumerable] Returns the order for which CTE's were imported as.
|
21
|
+
def each
|
22
|
+
return to_enum(:each) unless block_given?
|
23
|
+
|
24
|
+
with_keys.each do |key|
|
25
|
+
yield(key, with_values[key])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
alias each_pair each
|
29
|
+
|
30
|
+
# @param [Hash, WithCTE] value
|
31
|
+
def with_values=(value)
|
32
|
+
reset!
|
33
|
+
pipe_cte_with!(value)
|
9
34
|
end
|
10
35
|
|
11
|
-
|
36
|
+
# @param [Hash, WithCTE] value
|
37
|
+
def pipe_cte_with!(value)
|
38
|
+
return if value.nil? || value.empty?
|
39
|
+
|
40
|
+
value.each_pair do |name, expression|
|
41
|
+
sym_name = name.to_sym
|
42
|
+
next if with_values.key?(sym_name)
|
43
|
+
|
44
|
+
# Ensure we follow FIFO pattern.
|
45
|
+
# If the parent has similar CTE alias keys, we want to favor the parent's expressions over its children's.
|
46
|
+
if expression.is_a?(ActiveRecord::Relation) && expression.with_values?
|
47
|
+
pipe_cte_with!(expression.cte)
|
48
|
+
expression.cte.reset!
|
49
|
+
end
|
50
|
+
|
51
|
+
@with_keys |= [sym_name]
|
52
|
+
@with_values[sym_name] = expression
|
53
|
+
end
|
54
|
+
|
55
|
+
value.reset! if value.is_a?(WithCTE)
|
56
|
+
end
|
57
|
+
|
58
|
+
def reset!
|
59
|
+
@with_keys = []
|
60
|
+
@with_values = {}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class WithChain
|
65
|
+
# @param [ActiveRecord::Relation] scope
|
66
|
+
def initialize(scope)
|
67
|
+
@scope = scope
|
68
|
+
@scope.cte ||= WithCTE.new(scope)
|
69
|
+
end
|
70
|
+
|
71
|
+
# @param [Hash, WithCTE] args
|
72
|
+
def recursive(args)
|
12
73
|
@scope.tap do |scope|
|
13
|
-
scope.with_values += args
|
14
74
|
scope.recursive_value = true
|
75
|
+
scope.cte.pipe_cte_with!(args)
|
15
76
|
end
|
16
77
|
end
|
17
78
|
end
|
18
79
|
|
19
|
-
|
20
|
-
|
80
|
+
# @return [WithCTE]
|
81
|
+
def cte
|
82
|
+
@values[:cte]
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param [WithCTE] cte
|
86
|
+
def cte=(cte)
|
87
|
+
raise TypeError.new("Must be a WithCTE class type") unless cte.is_a?(WithCTE)
|
88
|
+
|
89
|
+
@values[:cte] = cte
|
21
90
|
end
|
22
91
|
|
92
|
+
# @return [Boolean]
|
23
93
|
def with_values?
|
24
|
-
!(
|
94
|
+
!(cte.nil? || cte.empty?)
|
25
95
|
end
|
26
96
|
|
97
|
+
# @param [Hash, WithCTE] values
|
27
98
|
def with_values=(values)
|
28
|
-
|
99
|
+
cte.with_values = values
|
29
100
|
end
|
30
101
|
|
102
|
+
# @param [Boolean] value
|
31
103
|
def recursive_value=(value)
|
32
104
|
raise ImmutableRelation if @loaded
|
105
|
+
|
33
106
|
@values[:recursive] = value
|
34
107
|
end
|
35
108
|
|
36
|
-
|
37
|
-
|
109
|
+
# @return [Boolean]
|
110
|
+
def recursive_value?
|
111
|
+
!(!@values[:recursive])
|
38
112
|
end
|
39
|
-
alias recursive_value? recursive_value
|
40
113
|
|
114
|
+
# @param [Hash, WithCTE] opts
|
41
115
|
def with(opts = :chain, *rest)
|
42
116
|
return WithChain.new(spawn) if opts == :chain
|
117
|
+
|
43
118
|
opts.blank? ? self : spawn.with!(opts, *rest)
|
44
119
|
end
|
45
120
|
|
46
|
-
|
121
|
+
# @param [Hash, WithCTE] opts
|
122
|
+
def with!(opts = :chain, *_rest)
|
47
123
|
return WithChain.new(self) if opts == :chain
|
48
|
-
self.with_values += [opts] + rest
|
49
|
-
self
|
50
|
-
end
|
51
124
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
case expression
|
56
|
-
when String
|
57
|
-
Arel::Nodes::SqlLiteral.new("(#{expression})")
|
58
|
-
when ActiveRecord::Relation, Arel::SelectManager
|
59
|
-
Arel::Nodes::SqlLiteral.new("(#{expression.to_sql})")
|
60
|
-
end
|
61
|
-
next if select.nil?
|
62
|
-
Arel::Nodes::As.new(Arel::Nodes::SqlLiteral.new(PG::Connection.quote_ident(name.to_s)), select)
|
125
|
+
tap do |scope|
|
126
|
+
scope.cte ||= WithCTE.new(self)
|
127
|
+
scope.cte.pipe_cte_with!(opts)
|
63
128
|
end
|
64
129
|
end
|
65
130
|
|
66
131
|
def build_with(arel)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end.compact
|
132
|
+
return unless with_values?
|
133
|
+
|
134
|
+
cte_statements = cte.map do |name, expression|
|
135
|
+
grouped_expression = cte.generate_grouping(expression)
|
136
|
+
cte_name = cte.to_arel_sql(cte.double_quote(name.to_s))
|
137
|
+
Arel::Nodes::As.new(cte_name, grouped_expression)
|
138
|
+
end
|
75
139
|
|
76
|
-
|
77
|
-
|
140
|
+
if recursive_value?
|
141
|
+
arel.with(:recursive, cte_statements)
|
142
|
+
else
|
143
|
+
arel.with(cte_statements)
|
144
|
+
end
|
78
145
|
end
|
79
146
|
end
|
80
147
|
end
|