Class: PgCanary::Rules::HugeInList

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

Overview

IN (...) / = ANY(...) with a huge number of values: the statement itself becomes expensive to parse/plan and usually signals a missing JOIN. Counts literal list items and runtime array binds, which static SQL linters cannot see. Threshold: config.rules.huge_in_list.threshold (default 500).

Constant Summary

Constants included from PgQuerySupport

PgQuerySupport::COMPARISON_OPS

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

all, #enabled?, rule_name

Class Method Details

.optionsObject



15
16
17
# File 'lib/pg_canary/rules/definitions/huge_in_list.rb', line 15

def self.options
  { threshold: 500 }
end

Instance Method Details

#check(query) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/pg_canary/rules/definitions/huge_in_list.rb', line 19

def check(query)
  threshold = rule_config(query).threshold
  detections = []
  query.each_scope do |scope|
    next unless scope.where_clause

    walk_within_scope(scope.where_clause) do |node|
      next unless node.is_a?(PgQuery::A_Expr)

      count = value_count(query, node)
      next unless count && count > threshold

      table, column = resolve_lexpr(scope, node)
      detections << detection(
        query,
        table: table,
        columns: column,
        message: "IN / ANY list with #{count} values (threshold: #{threshold}). Huge value lists " \
                 "are expensive to parse and plan, and usually mean a JOIN is missing.",
        suggestion: "Rewrite as a JOIN against the source of the values (subquery or VALUES list) " \
                    "instead of materializing the ids in the query."
      )
    end
  end
  detections
end

#default_enabledObject



11
12
13
# File 'lib/pg_canary/rules/definitions/huge_in_list.rb', line 11

def default_enabled
  true
end