active_record_query_fixer 0.0.9 → 0.0.14

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: 211d95cbf9d6f076875db25b572d387ccae0bab79966e5956b407fa585054114
4
- data.tar.gz: 74dd84ce0e7573bd2a52864f02a2b0856d8c11caa45716946880476760a813d0
3
+ metadata.gz: d5997cfbf763c93ff62479b46f02fa52c769723be216b7c2f60e57847e90a41e
4
+ data.tar.gz: d7fc6189ee2f6ad8c8af6244ab1f9e1e8ab8f67b9ba5142f3465460efcf589cd
5
5
  SHA512:
6
- metadata.gz: 497005381f08f135df4b672560b3a200ab9486252a8e1079d5e2f23fd11c6f22479296d9ff2eeada9f0b9d98b82a952ade57fa71f80c568cd43fcd0242a9f505
7
- data.tar.gz: cc59eea2a2f6b2842d8d1583632780b91811171b69f50d3c0fe9fca8d7e78c80453bbc8dfd011c148f6527e2374aaa35729db7ce8c9e70887abfe4e9a36940ff
6
+ metadata.gz: 942395bf5a468635a435503793793c80e914b2b37036033b0ab7fd9fc714a6c8e16d46be0167088ad9eaf4b308cc0c1274db45b4668ec2aa96e45808e55d4215
7
+ data.tar.gz: 155a52f9bcc3720b1bff57fae6ccafe5167b3ae9b515fedb5e257a109e702f2f5227a7813b743aad256389e45fd4d6e475ec08e8d32611def21cabecf396727d
@@ -1,17 +1,19 @@
1
1
  require "dig_bang"
2
- require "pg_query"
3
2
 
4
3
  class ActiveRecordQueryFixer
5
4
  autoload :RelationExtentions, "#{__dir__}/active_record_query_fixer/relation_extentions"
6
5
 
7
- attr_reader :query
6
+ attr_reader :count_select, :query
7
+
8
+ delegate :connection, to: :query
9
+ delegate :quote_column_name, :quote_table_name, to: :connection
8
10
 
9
11
  def self.fix(query)
10
12
  new(query: query).fix.query
11
13
  end
12
14
 
13
- def initialize(args)
14
- @query = args.fetch(:query)
15
+ def initialize(query:)
16
+ @query = query
15
17
  @count_select = 0
16
18
  end
17
19
 
@@ -19,74 +21,80 @@ class ActiveRecordQueryFixer
19
21
  fix_reference_group if fix_reference_group?
20
22
  fix_order_group if fix_order_group?
21
23
  fix_order_select_distinct if fix_order_select_distinct?
22
- fix_select_group if @query.values[:select] && @query.values[:group]
24
+ fix_select_group if query.values[:select] && query.values[:group]
23
25
 
24
26
  self
25
27
  end
26
28
 
27
- def fix_select_group
29
+ def fix_select_group # rubocop:disable Metrics/AbcSize
28
30
  select_targets.each do |select_target|
29
- fields = select_target.dig!("ResTarget", "val", "ColumnRef", "fields")
30
- next if fields.length != 2
31
+ fields = select_target.res_target.val.column_ref.fields
32
+ next if !fields || fields.length != 2
31
33
 
32
- table = fields[0].dig("String", "str")
33
- column = fields[1].dig("String", "str")
34
+ table = fields[0].string.str
35
+ column = fields[1].string&.str
34
36
 
35
37
  if column
36
38
  # A table and a column has been selected - make sure to group by that
37
- @query = @query.group("#{table}.#{column}")
38
- elsif fields[1].key?("A_Star")
39
+ @query = query.group("#{quote_table_name(table)}.#{quote_column_name(column)}")
40
+ elsif fields[1].a_star
39
41
  # A table and a star has been selected - assume the primary key is called "id" and group by that
40
- @query = @query.group("#{table}.id")
42
+ @query = query.group("#{quote_table_name(table)}.#{quote_column_name("id")}")
41
43
  end
42
44
  end
43
45
 
44
46
  self
45
47
  end
46
48
 
47
- def fix_order_group
48
- @query = @query.group(@query.model.arel_table[@query.model.primary_key])
49
+ def fix_order_group # rubocop:disable Metrics/AbcSize
50
+ @query = query.group(query.model.arel_table[query.model.primary_key])
49
51
 
50
52
  sort_targets.each do |sort_target|
51
- fields = sort_target.dig("SortBy", "node", "ColumnRef", "fields")
53
+ fields = sort_target.sort_by.node.column_ref&.fields
54
+
52
55
  next if !fields || fields.length != 2
53
56
 
54
- table = fields.dig(0, "String", "str")
55
- column = fields.dig(1, "String", "str")
57
+ table = fields[0].string.str
58
+ column = fields[1].string.str
56
59
 
57
- @query = @query.group("#{table}.#{column}") if table && column
60
+ @query = query.group("#{quote_table_name(table)}.#{quote_column_name(column)}") if table && column
58
61
  end
59
62
 
60
63
  self
61
64
  end
62
65
 
63
- def fix_order_select_distinct
64
- changed = false
66
+ def fix_order_select_distinct # rubocop:disable Metrics/AbcSize
67
+ select_appends = []
65
68
 
66
69
  sort_targets.each do |sort_target|
67
- fields = sort_target.dig("SortBy", "node", "ColumnRef", "fields")
70
+ fields = sort_target.sort_by.node.column_ref.fields
68
71
  next if !fields || fields.length != 2
69
72
 
70
- table = fields.dig(0, "String", "str")
71
- column = fields.dig(1, "String", "str")
73
+ table = fields[0].string.str
74
+ column = fields[1].string.str
72
75
 
73
76
  next if !table || !column
74
77
 
75
- @query = @query.select("#{table}.#{column} AS active_record_query_fixer_#{@count_select}")
76
- changed = true
78
+ select_appends << "#{quote_table_name(table)}.#{quote_column_name(column)} AS active_record_query_fixer_#{count_select}"
79
+
77
80
  @count_select += 1
78
81
  end
79
82
 
80
- @query = @query.select("#{@query.table_name}.*") if changed
83
+ # Start by prepending a wild-card select before doing the fix-selects to avoid any issues with `DISTINCT COUNT`
84
+ prepend_table_wildcard if !table_wildcard_prepended? && select_appends.any? && query.values[:select].blank?
85
+
86
+ select_appends.each do |select_append|
87
+ @query = query.select(select_append)
88
+ end
81
89
 
82
90
  self
83
91
  end
84
92
 
85
93
  def fix_reference_group
86
- @query = @query.group(@query.model.arel_table[@query.model.primary_key])
94
+ @query = query.group(query.model.arel_table[query.model.primary_key])
87
95
 
88
- @query.values[:references].each do |reference|
89
- @query = @query.group("#{reference}.id")
96
+ query.values[:references].each do |reference|
97
+ @query = query.group("#{quote_table_name(reference)}.#{quote_column_name("id")}")
90
98
  end
91
99
 
92
100
  self
@@ -95,28 +103,53 @@ class ActiveRecordQueryFixer
95
103
  private
96
104
 
97
105
  def fix_order_group?
98
- @query.values[:joins].blank? && @query.values[:distinct].present? ||
99
- @query.values[:group].present? && @query.values[:order].present?
106
+ query.values[:joins].blank? && query.values[:distinct].present? && query.values[:order].present? ||
107
+ query.values[:group].present? && query.values[:order].present?
100
108
  end
101
109
 
102
110
  def fix_order_select_distinct?
103
- @query.values[:distinct].present? && @query.values[:order].present?
111
+ query.values[:distinct].present? && query.values[:order].present?
104
112
  end
105
113
 
106
114
  def fix_reference_group?
107
- @query.values[:references].present? && @query.values[:group].present?
115
+ query.values[:references].present? && query.values[:group].present?
108
116
  end
109
117
 
110
118
  def parsed_query
111
- @parsed_query ||= PgQuery.parse(@query.to_sql)
119
+ require "pg_query" unless defined?(PgQuery)
120
+ @parsed_query ||= PgQuery.parse(query.to_sql)
121
+ end
122
+
123
+ # 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`.
124
+ def prepend_table_wildcard
125
+ old_select = query.values[:select]&.clone || []
126
+ old_select = old_select.keep_if { |select_statement| select_statement != select_table_wildcard_sql }
127
+
128
+ @query = query.except(:select).select(select_table_wildcard_sql)
129
+
130
+ old_select.each do |select_statement|
131
+ @query = query.select(select_statement)
132
+ end
133
+ end
134
+
135
+ def select_table_wildcard_sql
136
+ @select_table_wildcard_sql ||= "#{quote_table_name(query.table_name)}.*"
137
+ end
138
+
139
+ def table_wildcard_prepended?
140
+ query.values[:select]&.first == select_table_wildcard_sql
141
+ end
142
+
143
+ def select_statement
144
+ @select_statement ||= parsed_query.tree.stmts.fetch(0).stmt.select_stmt
112
145
  end
113
146
 
114
147
  def select_targets
115
- @select_targets ||= parsed_query.tree.dig!(0, "RawStmt", "stmt", "SelectStmt", "targetList")
148
+ @select_targets ||= select_statement.target_list
116
149
  end
117
150
 
118
151
  def sort_targets
119
- @sort_targets ||= parsed_query.tree.dig!(0, "RawStmt", "stmt", "SelectStmt", "sortClause")
152
+ @sort_targets ||= select_statement.sort_clause
120
153
  end
121
154
  end
122
155
 
@@ -1,3 +1,3 @@
1
1
  class ActiveRecordQueryFixer
2
- VERSION = "0.0.9".freeze
2
+ VERSION = "0.0.14".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.9
4
+ version: 0.0.14
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: 2021-04-06 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: