Class: PgCanary::Rules::CorrelatedSubqueryInSelect

Inherits:
Base
  • Object
show all
Defined in:
lib/pg_canary/rules/definitions/correlated_subquery_in_select.rb

Overview

A scalar subquery in the SELECT list that references the outer table runs once per result row (N+1 inside a single query).

Constant Summary

Constants included from PgQuerySupport

PgQuerySupport::COMPARISON_OPS

Instance Method Summary collapse

Methods inherited from Base

all, #enabled?, options, rule_name

Instance Method Details

#check(query) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/pg_canary/rules/definitions/correlated_subquery_in_select.rb', line 12

def check(query)
  detections = []
  query.each_scope do |scope|
    scope.stmt.target_list.each do |target|
      res_target = unwrap_node(target)
      next unless res_target.is_a?(PgQuery::ResTarget)

      walk_within_scope(res_target.val) do |node|
        next unless node.is_a?(PgQuery::SubLink) && node.sub_link_type == :EXPR_SUBLINK

        table, column = correlated_reference(node, scope)
        next unless table && column
        next unless applicable_table?(query, table)

        detections << detection(
          query,
          table: table,
          columns: column,
          message: "Scalar subquery in the SELECT list references #{table}.#{column} from the " \
                   "outer query, so it executes once per returned row.",
          suggestion: "Rewrite as a JOIN + GROUP BY (or a LATERAL join) so the lookup runs " \
                      "once as a set operation."
        )
      end
    end
  end
  detections
end

#default_enabledObject



8
9
10
# File 'lib/pg_canary/rules/definitions/correlated_subquery_in_select.rb', line 8

def default_enabled
  true
end