active_record_extended 1.2.0 → 2.0.3
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 +85 -7
- data/lib/active_record_extended/active_record.rb +2 -11
- data/lib/active_record_extended/active_record/relation_patch.rb +21 -4
- data/lib/active_record_extended/arel.rb +1 -0
- data/lib/active_record_extended/arel/nodes.rb +24 -21
- data/lib/active_record_extended/arel/predications.rb +3 -2
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +1 -1
- data/lib/active_record_extended/query_methods/any_of.rb +5 -4
- data/lib/active_record_extended/query_methods/either.rb +2 -1
- data/lib/active_record_extended/query_methods/inet.rb +6 -2
- data/lib/active_record_extended/query_methods/json.rb +14 -17
- data/lib/active_record_extended/query_methods/select.rb +13 -12
- data/lib/active_record_extended/query_methods/unionize.rb +13 -7
- data/lib/active_record_extended/query_methods/where_chain.rb +17 -8
- data/lib/active_record_extended/query_methods/window.rb +93 -0
- data/lib/active_record_extended/query_methods/with_cte.rb +104 -37
- data/lib/active_record_extended/utilities/order_by.rb +11 -30
- data/lib/active_record_extended/utilities/support.rb +21 -18
- data/lib/active_record_extended/version.rb +1 -1
- data/spec/query_methods/any_of_spec.rb +2 -2
- data/spec/query_methods/either_spec.rb +11 -0
- data/spec/query_methods/json_spec.rb +5 -5
- data/spec/query_methods/select_spec.rb +13 -13
- data/spec/query_methods/unionize_spec.rb +5 -5
- data/spec/query_methods/window_spec.rb +51 -0
- data/spec/query_methods/with_cte_spec.rb +12 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/sql_inspections/any_of_sql_spec.rb +2 -2
- data/spec/sql_inspections/contains_sql_queries_spec.rb +8 -8
- data/spec/sql_inspections/either_sql_spec.rb +19 -3
- data/spec/sql_inspections/json_sql_spec.rb +7 -1
- data/spec/sql_inspections/unionize_sql_spec.rb +2 -2
- data/spec/sql_inspections/window_sql_spec.rb +98 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +30 -1
- data/spec/support/models.rb +18 -0
- metadata +23 -20
- data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +0 -87
- data/lib/active_record_extended/patch/5_0/regex_match.rb +0 -10
@@ -52,12 +52,12 @@ module ActiveRecordExtended
|
|
52
52
|
# #=> SELECT (ARRAY_AGG(DISTINCT members.price)) AS past_purchases, ...
|
53
53
|
def process_hash!(hash_of_options, alias_name)
|
54
54
|
enforced_options = {
|
55
|
-
cast_with: hash_of_options
|
56
|
-
order_by: hash_of_options
|
57
|
-
distinct: !(!hash_of_options
|
55
|
+
cast_with: hash_of_options[:cast_with],
|
56
|
+
order_by: hash_of_options[:order_by],
|
57
|
+
distinct: !(!hash_of_options[:distinct])
|
58
58
|
}
|
59
|
-
query_statement = hash_to_dot_notation(hash_of_options
|
60
|
-
select!(query_statement, alias_name, enforced_options)
|
59
|
+
query_statement = hash_to_dot_notation(hash_of_options[:__select_statement] || hash_of_options.first)
|
60
|
+
select!(query_statement, alias_name, **enforced_options)
|
61
61
|
end
|
62
62
|
|
63
63
|
# Turn a hash chain into a query statement:
|
@@ -65,7 +65,7 @@ module ActiveRecordExtended
|
|
65
65
|
def hash_to_dot_notation(column)
|
66
66
|
case column
|
67
67
|
when Hash, Array
|
68
|
-
column.to_a.flat_map(
|
68
|
+
column.to_a.flat_map { |col| hash_to_dot_notation(col) }.join(".")
|
69
69
|
when String, Symbol
|
70
70
|
/^([[:alpha:]]+)$/.match?(column.to_s) ? double_quote(column) : column
|
71
71
|
else
|
@@ -76,7 +76,7 @@ module ActiveRecordExtended
|
|
76
76
|
# Add's select statement values to the current relation, select statement lists
|
77
77
|
def select!(query, alias_name = nil, **options)
|
78
78
|
pipe_cte_with!(query)
|
79
|
-
@scope._select!(to_casted_query(query, alias_name, options))
|
79
|
+
@scope._select!(to_casted_query(query, alias_name, **options))
|
80
80
|
end
|
81
81
|
|
82
82
|
# Wraps the query with the requested query method
|
@@ -84,15 +84,15 @@ module ActiveRecordExtended
|
|
84
84
|
# to_casted_query("memberships.cost", :total_revenue, :sum)
|
85
85
|
# #=> SELECT (SUM(memberships.cost)) AS total_revenue
|
86
86
|
def to_casted_query(query, alias_name, **options)
|
87
|
-
cast_with = options
|
88
|
-
order_expr = order_by_expression(options
|
89
|
-
distinct = cast_with.chomp!("_distinct") || options
|
87
|
+
cast_with = options[:cast_with].to_s.downcase
|
88
|
+
order_expr = order_by_expression(options[:order_by])
|
89
|
+
distinct = cast_with.chomp!("_distinct") || options[:distinct] # account for [:agg_name:]_distinct
|
90
90
|
|
91
91
|
case cast_with
|
92
92
|
when "array", "true"
|
93
93
|
wrap_with_array(query, alias_name)
|
94
94
|
when AGGREGATE_ONE_LINERS
|
95
|
-
expr = to_sql_array(query
|
95
|
+
expr = to_sql_array(query) { |value| group_when_needed(value) }
|
96
96
|
casted_query = ::Arel::Nodes::AggregateFunctionName.new(cast_with, expr, distinct).order_by(order_expr)
|
97
97
|
nested_alias_escape(casted_query, alias_name)
|
98
98
|
else
|
@@ -102,7 +102,8 @@ module ActiveRecordExtended
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def foster_select(*args)
|
105
|
-
raise ArgumentError
|
105
|
+
raise ArgumentError.new("Call `.forster_select' with at least one field") if args.empty?
|
106
|
+
|
106
107
|
spawn._foster_select!(*args)
|
107
108
|
end
|
108
109
|
|
@@ -59,7 +59,7 @@ module ActiveRecordExtended
|
|
59
59
|
protected
|
60
60
|
|
61
61
|
def append_union_order!(union_type, args)
|
62
|
-
args.each
|
62
|
+
args.each { |arg| pipe_cte_with!(arg) }
|
63
63
|
flatten_scopes = flatten_to_sql(args)
|
64
64
|
@scope.union_values += flatten_scopes
|
65
65
|
calculate_union_operation!(union_type, flatten_scopes.size)
|
@@ -81,7 +81,7 @@ module ActiveRecordExtended
|
|
81
81
|
union_values: [],
|
82
82
|
union_operations: [],
|
83
83
|
union_ordering_values: [],
|
84
|
-
unionized_name: nil
|
84
|
+
unionized_name: nil
|
85
85
|
}
|
86
86
|
end
|
87
87
|
|
@@ -89,10 +89,11 @@ module ActiveRecordExtended
|
|
89
89
|
union_values: Array,
|
90
90
|
union_operations: Array,
|
91
91
|
union_ordering_values: Array,
|
92
|
-
unionized_name: lambda { |klass| klass.arel_table.name }
|
92
|
+
unionized_name: lambda { |klass| klass.arel_table.name }
|
93
93
|
}.each_pair do |method_name, default|
|
94
94
|
define_method(method_name) do
|
95
95
|
return unionize_storage[method_name] if send("#{method_name}?")
|
96
|
+
|
96
97
|
(default.is_a?(Proc) ? default.call(@klass) : default.new)
|
97
98
|
end
|
98
99
|
|
@@ -106,19 +107,21 @@ module ActiveRecordExtended
|
|
106
107
|
end
|
107
108
|
|
108
109
|
def union(opts = :chain, *args)
|
109
|
-
return UnionChain.new(spawn) if
|
110
|
+
return UnionChain.new(spawn) if :chain == opts
|
111
|
+
|
110
112
|
opts.nil? ? self : spawn.union!(opts, *args, chain_method: __callee__)
|
111
113
|
end
|
112
114
|
|
113
115
|
(UNIONIZE_METHODS + UNION_RELATION_METHODS).each do |union_method|
|
114
116
|
next if union_method == :union
|
117
|
+
|
115
118
|
alias_method union_method, :union
|
116
119
|
end
|
117
120
|
|
118
121
|
def union!(opts = :chain, *args, chain_method: :union)
|
119
122
|
union_chain = UnionChain.new(self)
|
120
123
|
chain_method ||= :union
|
121
|
-
return union_chain if
|
124
|
+
return union_chain if :chain == opts
|
122
125
|
|
123
126
|
union_chain.public_send(chain_method, *([opts] + args))
|
124
127
|
end
|
@@ -126,11 +129,13 @@ module ActiveRecordExtended
|
|
126
129
|
# Will construct *Just* the union SQL statement that was been built thus far
|
127
130
|
def to_union_sql
|
128
131
|
return unless union_values?
|
132
|
+
|
129
133
|
apply_union_ordering(build_union_nodes!(false)).to_sql
|
130
134
|
end
|
131
135
|
|
132
136
|
def to_nice_union_sql(color = true)
|
133
137
|
return to_union_sql unless defined?(::Niceql)
|
138
|
+
|
134
139
|
::Niceql::Prettifier.prettify_sql(to_union_sql, color)
|
135
140
|
end
|
136
141
|
|
@@ -172,7 +177,7 @@ module ActiveRecordExtended
|
|
172
177
|
|
173
178
|
def build_union_nodes!(raise_error = true)
|
174
179
|
unionize_error_or_warn!(raise_error)
|
175
|
-
union_values.each_with_index.
|
180
|
+
union_values.each_with_index.reduce(nil) do |union_node, (relation_node, index)|
|
176
181
|
next resolve_relation_node(relation_node) if union_node.nil?
|
177
182
|
|
178
183
|
operation = union_operations.fetch(index - 1, :union)
|
@@ -215,6 +220,7 @@ module ActiveRecordExtended
|
|
215
220
|
#
|
216
221
|
def apply_union_ordering(union_nodes)
|
217
222
|
return union_nodes unless union_ordering_values?
|
223
|
+
|
218
224
|
UnionChain.new(self).inline_order_by(union_nodes, union_ordering_values)
|
219
225
|
end
|
220
226
|
|
@@ -222,7 +228,7 @@ module ActiveRecordExtended
|
|
222
228
|
|
223
229
|
def unionize_error_or_warn!(raise_error = true)
|
224
230
|
if raise_error && union_values.size <= 1
|
225
|
-
raise ArgumentError
|
231
|
+
raise ArgumentError.new("You are required to provide 2 or more unions to join!")
|
226
232
|
elsif !raise_error && union_values.size <= 1
|
227
233
|
warn("Warning: You are required to provide 2 or more unions to join.")
|
228
234
|
end
|
@@ -5,9 +5,10 @@ module ActiveRecordExtended
|
|
5
5
|
# Finds Records that have an array column that contain any a set of values
|
6
6
|
# User.where.overlap(tags: [1,2])
|
7
7
|
# # SELECT * FROM users WHERE tags && {1,2}
|
8
|
-
def
|
9
|
-
substitute_comparisons(opts, rest, Arel::Nodes::
|
8
|
+
def overlaps(opts, *rest)
|
9
|
+
substitute_comparisons(opts, rest, Arel::Nodes::Overlaps, "overlap")
|
10
10
|
end
|
11
|
+
alias overlap overlaps
|
11
12
|
|
12
13
|
# Finds Records that contain an element in an array column
|
13
14
|
# User.where.any(tags: 3)
|
@@ -54,10 +55,10 @@ module ActiveRecordExtended
|
|
54
55
|
elsif column.try(:array)
|
55
56
|
Arel::Nodes::ContainsArray.new(arel.left, arel.right)
|
56
57
|
else
|
57
|
-
raise ArgumentError
|
58
|
+
raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
|
58
59
|
end
|
59
60
|
else
|
60
|
-
raise ArgumentError
|
61
|
+
raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
|
61
62
|
end
|
62
63
|
end
|
63
64
|
end
|
@@ -88,7 +89,7 @@ module ActiveRecordExtended
|
|
88
89
|
when Arel::Nodes::Equality
|
89
90
|
Arel::Nodes::Equality.new(arel.right, Arel::Nodes::NamedFunction.new(function_name, [arel.left]))
|
90
91
|
else
|
91
|
-
raise ArgumentError
|
92
|
+
raise ArgumentError.new("Invalid argument for .where.#{function_name.downcase}(), got #{arel.class}")
|
92
93
|
end
|
93
94
|
end
|
94
95
|
end
|
@@ -99,10 +100,18 @@ module ActiveRecordExtended
|
|
99
100
|
when Arel::Nodes::In, Arel::Nodes::Equality
|
100
101
|
arel_node_class.new(arel.left, arel.right)
|
101
102
|
else
|
102
|
-
raise ArgumentError
|
103
|
+
raise ArgumentError.new("Invalid argument for .where.#{method}(), got #{arel.class}")
|
103
104
|
end
|
104
105
|
end
|
105
106
|
end
|
107
|
+
|
108
|
+
def build_where_clause_for(scope, opts, rest)
|
109
|
+
if ActiveRecord::VERSION::MAJOR == 6 && ActiveRecord::VERSION::MINOR == 1
|
110
|
+
scope.send(:build_where_clause, opts, rest)
|
111
|
+
else
|
112
|
+
scope.send(:where_clause_factory).build(opts, rest)
|
113
|
+
end
|
114
|
+
end
|
106
115
|
end
|
107
116
|
end
|
108
117
|
|
@@ -112,9 +121,9 @@ module ActiveRecord
|
|
112
121
|
prepend ActiveRecordExtended::WhereChain
|
113
122
|
|
114
123
|
def build_where_chain(opts, rest, &block)
|
115
|
-
where_clause = @scope
|
124
|
+
where_clause = build_where_clause_for(@scope, opts, rest)
|
116
125
|
@scope.tap do |scope|
|
117
|
-
scope.references!(PredicateBuilder.references(opts)) if opts.is_a?(Hash)
|
126
|
+
scope.references!(PredicateBuilder.references(opts.stringify_keys)) if opts.is_a?(Hash)
|
118
127
|
scope.where_clause += where_clause.modified_predicates(&block)
|
119
128
|
end
|
120
129
|
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
|
-
return WithChain.new(spawn) if
|
116
|
+
return WithChain.new(spawn) if :chain == opts
|
117
|
+
|
43
118
|
opts.blank? ? self : spawn.with!(opts, *rest)
|
44
119
|
end
|
45
120
|
|
46
|
-
|
47
|
-
|
48
|
-
self
|
49
|
-
self
|
50
|
-
end
|
121
|
+
# @param [Hash, WithCTE] opts
|
122
|
+
def with!(opts = :chain, *_rest)
|
123
|
+
return WithChain.new(self) if :chain == opts
|
51
124
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
case expression
|
56
|
-
when String
|
57
|
-
Arel.sql("(#{expression})")
|
58
|
-
when ActiveRecord::Relation, Arel::SelectManager
|
59
|
-
Arel.sql("(#{expression.to_sql})")
|
60
|
-
end
|
61
|
-
next if select.nil?
|
62
|
-
Arel::Nodes::As.new(Arel.sql(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
|