active_record_query_fixer 0.0.8 → 0.0.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 748952394d906b747d5be0d64efd08b2c45b37015f0d3ce928622b63770c1eb9
4
- data.tar.gz: f9ac5f0a73434c61e06058fad5dbad01167238caba974876135e3f13514e594b
3
+ metadata.gz: 95ad037bd6f75eeb6769488d667a077f6607414ff3c18e38b75dfe68ef783f3d
4
+ data.tar.gz: b1ad2dc43c9eb7fe1ccf671f5f184754329b964d66c1021f6bee2e7e25ae3507
5
5
  SHA512:
6
- metadata.gz: fe806b25d8a1176f108450ba2e0d86cd5ff3b7e4a612cee1415b054bb3ffd33661e068eb1b9ecc4ce18f38714d083afd0405e9d4910ddf55db34baa593730d59
7
- data.tar.gz: 027b8b485cf08c9d62a2ddcbaa33acf05ae3338da4f24d5454cfa266ef8939f9f79a880f66069048807e9333f374f54f00de285ef7a8d28a1ddc8daf609cb136
6
+ metadata.gz: 9312bb60d8dcc8c8d239e3dda2c6d6a5ef428751270bf86a19a99b0af8099ddd93c39444a93511f5e6aaf0daf499c2cd023b7632c5d3782349a276f6ac764a5f
7
+ data.tar.gz: bab25343f025ce024634c8403f7eccc08b4f966c7463658cbc72ba4ec44822ad2579715c8586ba4588e27fb78153c7938a548b4cbd7721f7c647dd679a34b830
@@ -1,3 +1,5 @@
1
+ require "dig_bang"
2
+
1
3
  class ActiveRecordQueryFixer
2
4
  autoload :RelationExtentions, "#{__dir__}/active_record_query_fixer/relation_extentions"
3
5
 
@@ -16,31 +18,25 @@ class ActiveRecordQueryFixer
16
18
  fix_reference_group if fix_reference_group?
17
19
  fix_order_group if fix_order_group?
18
20
  fix_order_select_distinct if fix_order_select_distinct?
19
- fix_distinct_group_select if @query.values[:distinct] && @query.values[:group] && @query.values[:select]
21
+ fix_select_group if query.values[:select] && query.values[:group]
20
22
 
21
23
  self
22
24
  end
23
25
 
24
- def fix_distinct_group_select
25
- require "dig_bang"
26
- require "pg_query"
27
-
28
- parsed_query = PgQuery.parse(@query.to_sql)
29
- select_targets = parsed_query.tree.dig!(0, "RawStmt", "stmt", "SelectStmt", "targetList")
30
-
26
+ def fix_select_group
31
27
  select_targets.each do |select_target|
32
- fields = select_target.dig!("ResTarget", "val", "ColumnRef", "fields")
33
- next if fields.length != 2
28
+ fields = select_target.dig!("ResTarget", "val").dig("ColumnRef", "fields")
29
+ next if !fields || fields.length != 2
34
30
 
35
31
  table = fields[0].dig("String", "str")
36
32
  column = fields[1].dig("String", "str")
37
33
 
38
34
  if column
39
35
  # A table and a column has been selected - make sure to group by that
40
- @query = @query.group("#{table}.#{column}")
36
+ @query = query.group("#{table}.#{column}")
41
37
  elsif fields[1].key?("A_Star")
42
38
  # A table and a star has been selected - assume the primary key is called "id" and group by that
43
- @query = @query.group("#{table}.id")
39
+ @query = query.group("#{table}.id")
44
40
  end
45
41
  end
46
42
 
@@ -48,33 +44,52 @@ class ActiveRecordQueryFixer
48
44
  end
49
45
 
50
46
  def fix_order_group
51
- @query = @query.group(@query.model.arel_table[@query.model.primary_key])
47
+ @query = query.group(query.model.arel_table[query.model.primary_key])
48
+
49
+ sort_targets.each do |sort_target|
50
+ fields = sort_target.dig("SortBy", "node", "ColumnRef", "fields")
51
+ next if !fields || fields.length != 2
52
52
 
53
- @query.values[:order]&.each do |order|
54
- @query = @query.group(extract_table_and_column_from_expression(order)) if group_by_order?(order)
53
+ table = fields.dig(0, "String", "str")
54
+ column = fields.dig(1, "String", "str")
55
+
56
+ @query = query.group("#{table}.#{column}") if table && column
55
57
  end
56
58
 
57
59
  self
58
60
  end
59
61
 
60
62
  def fix_order_select_distinct
61
- changed = false
62
- @query.values[:order]&.each do |order|
63
- @query = @query.select("#{extract_table_and_column_from_expression(order)} AS active_record_query_fixer_#{@count_select}")
64
- changed = true
63
+ select_appends = []
64
+
65
+ sort_targets.each do |sort_target|
66
+ fields = sort_target.dig("SortBy", "node", "ColumnRef", "fields")
67
+ next if !fields || fields.length != 2
68
+
69
+ table = fields.dig(0, "String", "str")
70
+ column = fields.dig(1, "String", "str")
71
+
72
+ next if !table || !column
73
+
74
+ select_appends << "#{table}.#{column} AS active_record_query_fixer_#{@count_select}"
65
75
  @count_select += 1
66
76
  end
67
77
 
68
- @query = @query.select("#{@query.table_name}.*") if changed
78
+ # Start by prepending a wild-card select before doing the fix-selects to avoid any issues with `DISTINCT COUNT`
79
+ prepend_table_wildcard if !table_wildcard_prepended? && select_appends.any? && query.values[:select].blank?
80
+
81
+ select_appends.each do |select_append|
82
+ @query = query.select(select_append)
83
+ end
69
84
 
70
85
  self
71
86
  end
72
87
 
73
88
  def fix_reference_group
74
- @query = @query.group(@query.model.arel_table[@query.model.primary_key])
89
+ @query = query.group(query.model.arel_table[query.model.primary_key])
75
90
 
76
- @query.values[:references].each do |reference|
77
- @query = @query.group("#{reference}.id")
91
+ query.values[:references].each do |reference|
92
+ @query = query.group("#{reference}.id")
78
93
  end
79
94
 
80
95
  self
@@ -82,35 +97,56 @@ class ActiveRecordQueryFixer
82
97
 
83
98
  private
84
99
 
85
- def extract_table_and_column_from_expression(order)
86
- if order.is_a?(Arel::Nodes::Ascending) || order.is_a?(Arel::Nodes::Descending)
87
- if order.expr.relation.respond_to?(:right)
88
- "#{order.expr.relation.right}.#{order.expr.name}"
89
- else
90
- "#{order.expr.relation.table_name}.#{order.expr.name}"
91
- end
92
- elsif order.is_a?(String)
93
- order
94
- else
95
- raise "Couldn't extract table and column from: #{order}"
96
- end
97
- end
98
-
99
100
  def fix_order_group?
100
- @query.values[:joins].blank? && @query.values[:distinct].present? ||
101
- @query.values[:group].present? && @query.values[:order].present?
101
+ query.values[:joins].blank? && query.values[:distinct].present? && query.values[:order].present? ||
102
+ query.values[:group].present? && query.values[:order].present?
102
103
  end
103
104
 
104
105
  def fix_order_select_distinct?
105
- @query.values[:distinct].present? && @query.values[:order].present?
106
+ query.values[:distinct].present? && query.values[:order].present?
106
107
  end
107
108
 
108
109
  def fix_reference_group?
109
- @query.values[:references].present? && @query.values[:group].present?
110
+ query.values[:references].present? && query.values[:group].present?
110
111
  end
111
112
 
112
- def group_by_order?(order)
113
- order.is_a?(String) && !order.match?(/\A\s*(COUNT|SUM)\(/i)
113
+ def parsed_query
114
+ require "pg_query" unless defined?(PgQuery)
115
+ @parsed_query ||= PgQuery.parse(query.to_sql)
116
+ end
117
+
118
+ # Prepends 'table_name.*' to the query. It needs to be pre-pended in case a `COUNT` or another aggregate function has been added to work with `DISTINCT`.
119
+ def prepend_table_wildcard
120
+ old_select = query.values[:select]&.clone || []
121
+ old_select = old_select.keep_if { |select_statement| select_statement != select_table_wildcard_sql }
122
+
123
+ @query = query.except(:select).select(select_table_wildcard_sql)
124
+
125
+ old_select.each do |select_statement|
126
+ @query = query.select(select_statement)
127
+ end
128
+ end
129
+
130
+ def select_table_wildcard_sql
131
+ @select_table_wildcard_sql ||= "#{query.table_name}.*"
132
+ end
133
+
134
+ def table_wildcard_prepended?
135
+ query.values[:select]&.first == select_table_wildcard_sql
136
+ end
137
+
138
+ def select_statement
139
+ @select_statement ||= parsed_query.tree.dig!(0, "RawStmt", "stmt", "SelectStmt")
140
+ end
141
+
142
+ def select_targets
143
+ @select_targets ||= select_statement.fetch("targetList")
144
+ end
145
+
146
+ def sort_targets
147
+ return [] unless select_statement.key?("sortClause")
148
+
149
+ @sort_targets ||= parsed_query.tree.dig!(0, "RawStmt", "stmt", "SelectStmt", "sortClause")
114
150
  end
115
151
  end
116
152
 
@@ -1,3 +1,3 @@
1
1
  class ActiveRecordQueryFixer
2
- VERSION = "0.0.8".freeze
2
+ VERSION = "0.0.13".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_query_fixer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - kaspernj
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-20 00:00:00.000000000 Z
11
+ date: 2020-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dig_bang
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: pg_query
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
27
  description: A library for automatically added `.select` on a column used for `.distinct`
42
28
  or automatically adding `.group` for a column used for order.
43
29
  email: