active_record_query_fixer 0.0.9 → 0.0.14

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: 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: