active_record_extended 2.0.3 → 3.1.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 +140 -77
- data/lib/active_record_extended/arel/nodes.rb +1 -1
- data/lib/active_record_extended/arel/{sql_literal.rb → sql_literal_patch.rb} +2 -2
- data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +16 -12
- data/lib/active_record_extended/arel.rb +1 -1
- data/lib/active_record_extended/patch/array_handler_patch.rb +22 -0
- data/lib/active_record_extended/patch/relation_patch.rb +82 -0
- data/lib/active_record_extended/patch/where_clause_patch.rb +13 -0
- data/lib/active_record_extended/query_methods/any_of.rb +1 -1
- data/lib/active_record_extended/query_methods/either.rb +5 -7
- data/lib/active_record_extended/query_methods/{select.rb → foster_select.rb} +4 -4
- data/lib/active_record_extended/query_methods/json.rb +2 -2
- data/lib/active_record_extended/query_methods/unionize.rb +5 -5
- data/lib/active_record_extended/query_methods/where_chain.rb +96 -90
- data/lib/active_record_extended/query_methods/window.rb +3 -3
- data/lib/active_record_extended/query_methods/with_cte.rb +61 -4
- data/lib/active_record_extended/utilities/order_by.rb +1 -1
- data/lib/active_record_extended/utilities/support.rb +1 -1
- data/lib/active_record_extended/version.rb +1 -1
- data/lib/active_record_extended.rb +55 -4
- metadata +20 -83
- data/lib/active_record_extended/active_record/relation_patch.rb +0 -50
- data/lib/active_record_extended/active_record.rb +0 -25
- data/lib/active_record_extended/patch/5_1/where_clause.rb +0 -11
- data/lib/active_record_extended/patch/5_2/where_clause.rb +0 -11
- data/lib/active_record_extended/predicate_builder/array_handler_decorator.rb +0 -20
- data/spec/active_record_extended_spec.rb +0 -7
- data/spec/query_methods/any_of_spec.rb +0 -131
- data/spec/query_methods/array_query_spec.rb +0 -64
- data/spec/query_methods/either_spec.rb +0 -70
- data/spec/query_methods/hash_query_spec.rb +0 -45
- data/spec/query_methods/inet_query_spec.rb +0 -112
- data/spec/query_methods/json_spec.rb +0 -157
- data/spec/query_methods/select_spec.rb +0 -115
- data/spec/query_methods/unionize_spec.rb +0 -165
- data/spec/query_methods/window_spec.rb +0 -51
- data/spec/query_methods/with_cte_spec.rb +0 -50
- data/spec/spec_helper.rb +0 -28
- data/spec/sql_inspections/any_of_sql_spec.rb +0 -41
- data/spec/sql_inspections/arel/aggregate_function_name_spec.rb +0 -41
- data/spec/sql_inspections/arel/array_spec.rb +0 -63
- data/spec/sql_inspections/arel/inet_spec.rb +0 -66
- data/spec/sql_inspections/contains_sql_queries_spec.rb +0 -47
- data/spec/sql_inspections/either_sql_spec.rb +0 -71
- data/spec/sql_inspections/json_sql_spec.rb +0 -82
- data/spec/sql_inspections/unionize_sql_spec.rb +0 -124
- data/spec/sql_inspections/window_sql_spec.rb +0 -98
- data/spec/sql_inspections/with_cte_sql_spec.rb +0 -95
- data/spec/support/database_cleaner.rb +0 -15
- data/spec/support/models.rb +0 -80
@@ -29,7 +29,7 @@ module ActiveRecordExtended
|
|
29
29
|
|
30
30
|
def hash_map_queries(queries)
|
31
31
|
if queries.size == 1 && queries.first.is_a?(Hash)
|
32
|
-
queries.first.each_pair.map { |attr, predicate|
|
32
|
+
queries.first.each_pair.map { |attr, predicate| { attr => predicate } }
|
33
33
|
else
|
34
34
|
queries
|
35
35
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "ar_outer_joins"
|
4
|
-
|
5
3
|
module ActiveRecordExtended
|
6
4
|
module QueryMethods
|
7
5
|
module Either
|
@@ -12,21 +10,21 @@ module ActiveRecordExtended
|
|
12
10
|
associations = [initial_association, fallback_association]
|
13
11
|
association_options = xor_field_options_for_associations(associations)
|
14
12
|
condition__query = xor_field_sql(association_options) + "= #{table_name}.#{primary_key}"
|
15
|
-
|
13
|
+
left_outer_joins(associations).where(Arel.sql(condition__query))
|
16
14
|
end
|
17
15
|
alias either_joins either_join
|
18
16
|
|
19
17
|
def either_order(direction, **associations_and_columns)
|
20
18
|
reflected_columns = map_columns_to_tables(associations_and_columns)
|
21
19
|
conditional_query = xor_field_sql(reflected_columns) + sort_order_sql(direction)
|
22
|
-
|
20
|
+
left_outer_joins(associations_and_columns.keys).order(Arel.sql(conditional_query))
|
23
21
|
end
|
24
22
|
alias either_orders either_order
|
25
23
|
|
26
24
|
private
|
27
25
|
|
28
26
|
def xor_field_sql(options)
|
29
|
-
XOR_FIELD_SQL %
|
27
|
+
XOR_FIELD_SQL % xor_field_options(options).to_h
|
30
28
|
end
|
31
29
|
|
32
30
|
def sort_order_sql(dir)
|
@@ -35,7 +33,7 @@ module ActiveRecordExtended
|
|
35
33
|
|
36
34
|
def xor_field_options(options)
|
37
35
|
str_args = options.flatten.take(XOR_FIELD_KEYS.size).map(&:to_s)
|
38
|
-
|
36
|
+
XOR_FIELD_KEYS.zip(str_args).to_h
|
39
37
|
end
|
40
38
|
|
41
39
|
def map_columns_to_tables(associations_and_columns)
|
@@ -60,4 +58,4 @@ module ActiveRecordExtended
|
|
60
58
|
end
|
61
59
|
end
|
62
60
|
|
63
|
-
ActiveRecord::
|
61
|
+
ActiveRecord::Relation.prepend(ActiveRecordExtended::QueryMethods::Either)
|
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
module ActiveRecordExtended
|
4
4
|
module QueryMethods
|
5
|
-
module
|
5
|
+
module FosterSelect
|
6
6
|
class SelectHelper
|
7
|
-
include
|
8
|
-
include
|
7
|
+
include ActiveRecordExtended::Utilities::Support
|
8
|
+
include ActiveRecordExtended::Utilities::OrderBy
|
9
9
|
|
10
10
|
AGGREGATE_ONE_LINERS = /^(exists|sum|max|min|avg|count|jsonb?_agg|(bit|bool)_(and|or)|xmlagg|array_agg)$/.freeze
|
11
11
|
|
@@ -115,4 +115,4 @@ module ActiveRecordExtended
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
-
ActiveRecord::Relation.prepend(ActiveRecordExtended::QueryMethods::
|
118
|
+
ActiveRecord::Relation.prepend(ActiveRecordExtended::QueryMethods::FosterSelect)
|
@@ -12,8 +12,8 @@ module ActiveRecordExtended
|
|
12
12
|
].freeze
|
13
13
|
|
14
14
|
class JsonChain
|
15
|
-
include
|
16
|
-
include
|
15
|
+
include ActiveRecordExtended::Utilities::Support
|
16
|
+
include ActiveRecordExtended::Utilities::OrderBy
|
17
17
|
|
18
18
|
DEFAULT_ALIAS = '"results"'
|
19
19
|
TO_JSONB_OPTIONS = [:array_agg, :distinct, :to_jsonb].to_set.freeze
|
@@ -7,8 +7,8 @@ module ActiveRecordExtended
|
|
7
7
|
UNIONIZE_METHODS = [:union, :union_all, :union_except, :union_intersect].freeze
|
8
8
|
|
9
9
|
class UnionChain
|
10
|
-
include
|
11
|
-
include
|
10
|
+
include ActiveRecordExtended::Utilities::Support
|
11
|
+
include ActiveRecordExtended::Utilities::OrderBy
|
12
12
|
|
13
13
|
def initialize(scope)
|
14
14
|
@scope = scope
|
@@ -107,7 +107,7 @@ module ActiveRecordExtended
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def union(opts = :chain, *args)
|
110
|
-
return UnionChain.new(spawn) if
|
110
|
+
return UnionChain.new(spawn) if opts == :chain
|
111
111
|
|
112
112
|
opts.nil? ? self : spawn.union!(opts, *args, chain_method: __callee__)
|
113
113
|
end
|
@@ -121,7 +121,7 @@ module ActiveRecordExtended
|
|
121
121
|
def union!(opts = :chain, *args, chain_method: :union)
|
122
122
|
union_chain = UnionChain.new(self)
|
123
123
|
chain_method ||= :union
|
124
|
-
return union_chain if
|
124
|
+
return union_chain if opts == :chain
|
125
125
|
|
126
126
|
union_chain.public_send(chain_method, *([opts] + args))
|
127
127
|
end
|
@@ -178,7 +178,7 @@ module ActiveRecordExtended
|
|
178
178
|
def build_union_nodes!(raise_error = true)
|
179
179
|
unionize_error_or_warn!(raise_error)
|
180
180
|
union_values.each_with_index.reduce(nil) do |union_node, (relation_node, index)|
|
181
|
-
next resolve_relation_node(relation_node) if union_node.nil?
|
181
|
+
next resolve_relation_node(relation_node) if union_node.nil? # rubocop:disable Lint/UnmodifiedReduceAccumulator
|
182
182
|
|
183
183
|
operation = union_operations.fetch(index - 1, :union)
|
184
184
|
left = union_node
|
@@ -1,115 +1,121 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecordExtended
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
module QueryMethods
|
5
|
+
module WhereChain
|
6
|
+
# Finds Records that have an array column that contain any a set of values
|
7
|
+
# User.where.overlap(tags: [1,2])
|
8
|
+
# # SELECT * FROM users WHERE tags && {1,2}
|
9
|
+
def overlaps(opts, *rest)
|
10
|
+
substitute_comparisons(opts, rest, Arel::Nodes::Overlaps, "overlap")
|
11
|
+
end
|
12
|
+
alias overlap overlaps
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
# Finds Records that contain an element in an array column
|
15
|
+
# User.where.any(tags: 3)
|
16
|
+
# # SELECT user.* FROM user WHERE 3 = ANY(user.tags)
|
17
|
+
def any(opts, *rest)
|
18
|
+
equality_to_function("ANY", opts, rest)
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
# Finds Records that contain a single matchable array element
|
22
|
+
# User.where.all(tags: 3)
|
23
|
+
# # SELECT user.* FROM user WHERE 3 = ALL(user.tags)
|
24
|
+
def all(opts, *rest)
|
25
|
+
equality_to_function("ALL", opts, rest)
|
26
|
+
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
28
|
+
# Finds Records that contains a nested set elements
|
29
|
+
#
|
30
|
+
# Array Column Type:
|
31
|
+
# User.where.contains(tags: [1, 3])
|
32
|
+
# # SELECT user.* FROM user WHERE user.tags @> {1,3}
|
33
|
+
#
|
34
|
+
# HStore Column Type:
|
35
|
+
# User.where.contains(data: { nickname: 'chainer' })
|
36
|
+
# # SELECT user.* FROM user WHERE user.data @> 'nickname' => 'chainer'
|
37
|
+
#
|
38
|
+
# JSONB Column Type:
|
39
|
+
# User.where.contains(data: { nickname: 'chainer' })
|
40
|
+
# # SELECT user.* FROM user WHERE user.data @> {'nickname': 'chainer'}
|
41
|
+
#
|
42
|
+
# This can also be used along side joined tables
|
43
|
+
#
|
44
|
+
# JSONB Column Type Example:
|
45
|
+
# Tag.joins(:user).where.contains(user: { data: { nickname: 'chainer' } })
|
46
|
+
# # SELECT tags.* FROM tags INNER JOIN user on user.id = tags.user_id WHERE user.data @> { nickname: 'chainer' }
|
47
|
+
#
|
48
|
+
def contains(opts, *rest)
|
49
|
+
if ActiveRecordExtended::AR_VERSION_GTE_6_1
|
50
|
+
return substitute_comparisons(opts, rest, Arel::Nodes::Contains, "contains")
|
51
|
+
end
|
52
|
+
|
53
|
+
build_where_chain(opts, rest) do |arel|
|
54
|
+
case arel
|
55
|
+
when Arel::Nodes::In, Arel::Nodes::Equality
|
56
|
+
column = left_column(arel) || column_from_association(arel)
|
57
|
+
|
58
|
+
if [:hstore, :jsonb].include?(column.type)
|
59
|
+
Arel::Nodes::ContainsHStore.new(arel.left, arel.right)
|
60
|
+
elsif column.try(:array)
|
61
|
+
Arel::Nodes::ContainsArray.new(arel.left, arel.right)
|
62
|
+
else
|
63
|
+
raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
|
64
|
+
end
|
57
65
|
else
|
58
66
|
raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
|
59
67
|
end
|
60
|
-
else
|
61
|
-
raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
|
62
68
|
end
|
63
69
|
end
|
64
|
-
end
|
65
70
|
|
66
|
-
|
71
|
+
private
|
67
72
|
|
68
|
-
|
69
|
-
|
70
|
-
|
73
|
+
def matchable_column?(col, arel)
|
74
|
+
col.name == arel.left.name.to_s || col.name == arel.left.relation.name.to_s
|
75
|
+
end
|
71
76
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
77
|
+
def column_from_association(arel)
|
78
|
+
assoc = assoc_from_related_table(arel)
|
79
|
+
assoc.klass.columns.detect { |col| matchable_column?(col, arel) } if assoc
|
80
|
+
end
|
76
81
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
82
|
+
def assoc_from_related_table(arel)
|
83
|
+
@scope.klass.reflect_on_association(arel.left.relation.name.to_sym) ||
|
84
|
+
@scope.klass.reflect_on_association(arel.left.relation.name.singularize.to_sym)
|
85
|
+
end
|
81
86
|
|
82
|
-
|
83
|
-
|
84
|
-
|
87
|
+
def left_column(arel)
|
88
|
+
@scope.klass.columns_hash[arel.left.name] || @scope.klass.columns_hash[arel.left.relation.name]
|
89
|
+
end
|
85
90
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
91
|
+
def equality_to_function(function_name, opts, rest)
|
92
|
+
build_where_chain(opts, rest) do |arel|
|
93
|
+
case arel
|
94
|
+
when Arel::Nodes::Equality
|
95
|
+
Arel::Nodes::Equality.new(arel.right, Arel::Nodes::NamedFunction.new(function_name, [arel.left]))
|
96
|
+
else
|
97
|
+
raise ArgumentError.new("Invalid argument for .where.#{function_name.downcase}(), got #{arel.class}")
|
98
|
+
end
|
93
99
|
end
|
94
100
|
end
|
95
|
-
end
|
96
101
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
102
|
+
def substitute_comparisons(opts, rest, arel_node_class, method)
|
103
|
+
build_where_chain(opts, rest) do |arel|
|
104
|
+
case arel
|
105
|
+
when Arel::Nodes::In, Arel::Nodes::Equality
|
106
|
+
arel_node_class.new(arel.left, arel.right)
|
107
|
+
else
|
108
|
+
raise ArgumentError.new("Invalid argument for .where.#{method}(), got #{arel.class}")
|
109
|
+
end
|
104
110
|
end
|
105
111
|
end
|
106
|
-
end
|
107
112
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
+
def build_where_clause_for(scope, opts, rest)
|
114
|
+
if ActiveRecordExtended::AR_VERSION_GTE_6_1
|
115
|
+
scope.send(:build_where_clause, opts, rest)
|
116
|
+
else
|
117
|
+
scope.send(:where_clause_factory).build(opts, rest)
|
118
|
+
end
|
113
119
|
end
|
114
120
|
end
|
115
121
|
end
|
@@ -118,7 +124,7 @@ end
|
|
118
124
|
module ActiveRecord
|
119
125
|
module QueryMethods
|
120
126
|
class WhereChain
|
121
|
-
prepend ActiveRecordExtended::WhereChain
|
127
|
+
prepend ActiveRecordExtended::QueryMethods::WhereChain
|
122
128
|
|
123
129
|
def build_where_chain(opts, rest, &block)
|
124
130
|
where_clause = build_where_clause_for(@scope, opts, rest)
|
@@ -4,8 +4,8 @@ module ActiveRecordExtended
|
|
4
4
|
module QueryMethods
|
5
5
|
module Window
|
6
6
|
class DefineWindowChain
|
7
|
-
include
|
8
|
-
include
|
7
|
+
include ActiveRecordExtended::Utilities::Support
|
8
|
+
include ActiveRecordExtended::Utilities::OrderBy
|
9
9
|
|
10
10
|
def initialize(scope, window_name)
|
11
11
|
@scope = scope
|
@@ -24,7 +24,7 @@ module ActiveRecordExtended
|
|
24
24
|
end
|
25
25
|
|
26
26
|
class WindowSelectBuilder
|
27
|
-
include
|
27
|
+
include ActiveRecordExtended::Utilities::Support
|
28
28
|
|
29
29
|
def initialize(window_function, args, window_name)
|
30
30
|
@window_function = window_function
|
@@ -4,12 +4,12 @@ module ActiveRecordExtended
|
|
4
4
|
module QueryMethods
|
5
5
|
module WithCTE
|
6
6
|
class WithCTE
|
7
|
-
include
|
7
|
+
include ActiveRecordExtended::Utilities::Support
|
8
8
|
include Enumerable
|
9
9
|
extend Forwardable
|
10
10
|
|
11
11
|
def_delegators :@with_values, :empty?, :blank?, :present?
|
12
|
-
attr_reader :with_values, :with_keys
|
12
|
+
attr_reader :with_values, :with_keys, :materialized_keys, :not_materialized_keys
|
13
13
|
|
14
14
|
# @param [ActiveRecord::Relation] scope
|
15
15
|
def initialize(scope)
|
@@ -33,6 +33,16 @@ module ActiveRecordExtended
|
|
33
33
|
pipe_cte_with!(value)
|
34
34
|
end
|
35
35
|
|
36
|
+
# @return [Boolean]
|
37
|
+
def materialized_key?(key)
|
38
|
+
materialized_keys.include?(key.to_sym)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Boolean]
|
42
|
+
def not_materialized_key?(key)
|
43
|
+
not_materialized_keys.include?(key.to_sym)
|
44
|
+
end
|
45
|
+
|
36
46
|
# @param [Hash, WithCTE] value
|
37
47
|
def pipe_cte_with!(value)
|
38
48
|
return if value.nil? || value.empty?
|
@@ -44,6 +54,10 @@ module ActiveRecordExtended
|
|
44
54
|
# Ensure we follow FIFO pattern.
|
45
55
|
# If the parent has similar CTE alias keys, we want to favor the parent's expressions over its children's.
|
46
56
|
if expression.is_a?(ActiveRecord::Relation) && expression.with_values?
|
57
|
+
# Add child's materialized keys to the parent
|
58
|
+
@materialized_keys += expression.cte.materialized_keys
|
59
|
+
@not_materialized_keys += expression.cte.not_materialized_keys
|
60
|
+
|
47
61
|
pipe_cte_with!(expression.cte)
|
48
62
|
expression.cte.reset!
|
49
63
|
end
|
@@ -58,6 +72,8 @@ module ActiveRecordExtended
|
|
58
72
|
def reset!
|
59
73
|
@with_keys = []
|
60
74
|
@with_values = {}
|
75
|
+
@materialized_keys = Set.new
|
76
|
+
@not_materialized_keys = Set.new
|
61
77
|
end
|
62
78
|
end
|
63
79
|
|
@@ -75,6 +91,32 @@ module ActiveRecordExtended
|
|
75
91
|
scope.cte.pipe_cte_with!(args)
|
76
92
|
end
|
77
93
|
end
|
94
|
+
|
95
|
+
# @param [Hash, WithCTE] args
|
96
|
+
def materialized(args)
|
97
|
+
@scope.tap do |scope|
|
98
|
+
args.each_pair do |name, _expression|
|
99
|
+
sym_name = name.to_sym
|
100
|
+
raise ArgumentError.new("CTE already set as not_materialized") if scope.cte.not_materialized_key?(sym_name)
|
101
|
+
|
102
|
+
scope.cte.materialized_keys << sym_name
|
103
|
+
end
|
104
|
+
scope.cte.pipe_cte_with!(args)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# @param [Hash, WithCTE] args
|
109
|
+
def not_materialized(args)
|
110
|
+
@scope.tap do |scope|
|
111
|
+
args.each_pair do |name, _expression|
|
112
|
+
sym_name = name.to_sym
|
113
|
+
raise ArgumentError.new("CTE already set as materialized") if scope.cte.materialized_key?(sym_name)
|
114
|
+
|
115
|
+
scope.cte.not_materialized_keys << sym_name
|
116
|
+
end
|
117
|
+
scope.cte.pipe_cte_with!(args)
|
118
|
+
end
|
119
|
+
end
|
78
120
|
end
|
79
121
|
|
80
122
|
# @return [WithCTE]
|
@@ -113,14 +155,14 @@ module ActiveRecordExtended
|
|
113
155
|
|
114
156
|
# @param [Hash, WithCTE] opts
|
115
157
|
def with(opts = :chain, *rest)
|
116
|
-
return WithChain.new(spawn) if
|
158
|
+
return WithChain.new(spawn) if opts == :chain
|
117
159
|
|
118
160
|
opts.blank? ? self : spawn.with!(opts, *rest)
|
119
161
|
end
|
120
162
|
|
121
163
|
# @param [Hash, WithCTE] opts
|
122
164
|
def with!(opts = :chain, *_rest)
|
123
|
-
return WithChain.new(self) if
|
165
|
+
return WithChain.new(self) if opts == :chain
|
124
166
|
|
125
167
|
tap do |scope|
|
126
168
|
scope.cte ||= WithCTE.new(self)
|
@@ -134,6 +176,9 @@ module ActiveRecordExtended
|
|
134
176
|
cte_statements = cte.map do |name, expression|
|
135
177
|
grouped_expression = cte.generate_grouping(expression)
|
136
178
|
cte_name = cte.to_arel_sql(cte.double_quote(name.to_s))
|
179
|
+
|
180
|
+
grouped_expression = add_materialized_modifier(grouped_expression, cte, name)
|
181
|
+
|
137
182
|
Arel::Nodes::As.new(cte_name, grouped_expression)
|
138
183
|
end
|
139
184
|
|
@@ -143,6 +188,18 @@ module ActiveRecordExtended
|
|
143
188
|
arel.with(cte_statements)
|
144
189
|
end
|
145
190
|
end
|
191
|
+
|
192
|
+
private
|
193
|
+
|
194
|
+
def add_materialized_modifier(expression, cte, name)
|
195
|
+
if cte.materialized_key?(name)
|
196
|
+
Arel::Nodes::SqlLiteral.new("MATERIALIZED #{expression.to_sql}")
|
197
|
+
elsif cte.not_materialized_key?(name)
|
198
|
+
Arel::Nodes::SqlLiteral.new("NOT MATERIALIZED #{expression.to_sql}")
|
199
|
+
else
|
200
|
+
expression
|
201
|
+
end
|
202
|
+
end
|
146
203
|
end
|
147
204
|
end
|
148
205
|
end
|
@@ -49,7 +49,7 @@ module ActiveRecordExtended
|
|
49
49
|
obj.each_pair do |o_key, o_value|
|
50
50
|
new_hash["#{tbl_or_col}.#{o_key}"] = o_value
|
51
51
|
end
|
52
|
-
elsif
|
52
|
+
elsif ActiveRecord::QueryMethods::VALID_DIRECTIONS.include?(obj)
|
53
53
|
new_hash[tbl_or_col] = obj
|
54
54
|
elsif obj.nil?
|
55
55
|
new_hash[tbl_or_col.to_s] = :asc
|
@@ -113,7 +113,7 @@ module ActiveRecordExtended
|
|
113
113
|
case value.to_s
|
114
114
|
# Ignore keys that contain double quotes or a Arel.star (*)[all columns]
|
115
115
|
# or if a table has already been explicitly declared (ex: users.id)
|
116
|
-
when "*", /((^".+"$)|(^[[:alpha:]]+\.[[:alnum:]]+))/
|
116
|
+
when "*", /((^".+"$)|(^[[:alpha:]]+\.[[:alnum:]]+)|\(.+\))/
|
117
117
|
value
|
118
118
|
else
|
119
119
|
PG::Connection.quote_ident(value.to_s)
|
@@ -1,10 +1,61 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_record_extended/version"
|
4
|
-
|
5
|
-
require "
|
6
|
-
require "
|
7
|
-
require "
|
4
|
+
|
5
|
+
require "active_record"
|
6
|
+
require "active_record/relation"
|
7
|
+
require "active_record/relation/merger"
|
8
|
+
require "active_record/relation/query_methods"
|
8
9
|
|
9
10
|
module ActiveRecordExtended
|
11
|
+
extend ActiveSupport::Autoload
|
12
|
+
|
13
|
+
AR_VERSION_GTE_6_1 = Gem::Requirement.new(">= 6.1").satisfied_by?(ActiveRecord.gem_version)
|
14
|
+
|
15
|
+
module Utilities
|
16
|
+
extend ActiveSupport::Autoload
|
17
|
+
|
18
|
+
eager_autoload do
|
19
|
+
autoload :OrderBy
|
20
|
+
autoload :Support
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module Patch
|
25
|
+
extend ActiveSupport::Autoload
|
26
|
+
|
27
|
+
eager_autoload do
|
28
|
+
autoload :ArrayHandlerPatch
|
29
|
+
autoload :RelationPatch
|
30
|
+
autoload :WhereClausePatch
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module QueryMethods
|
35
|
+
extend ActiveSupport::Autoload
|
36
|
+
|
37
|
+
eager_autoload do
|
38
|
+
autoload :AnyOf
|
39
|
+
autoload :Either
|
40
|
+
autoload :FosterSelect
|
41
|
+
autoload :Inet
|
42
|
+
autoload :Json
|
43
|
+
autoload :Unionize
|
44
|
+
autoload :WhereChain
|
45
|
+
autoload :Window
|
46
|
+
autoload :WithCTE
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.eager_load!
|
51
|
+
super
|
52
|
+
ActiveRecordExtended::Utilities.eager_load!
|
53
|
+
ActiveRecordExtended::Patch.eager_load!
|
54
|
+
ActiveRecordExtended::QueryMethods.eager_load!
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
ActiveSupport.on_load(:active_record) do
|
59
|
+
require "active_record_extended/arel"
|
60
|
+
ActiveRecordExtended.eager_load!
|
10
61
|
end
|