active_record_query_fixer 0.0.8 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
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: