Class: PgCanary::Rules::DeepOffset

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

Overview

OFFSET-based pagination reads and throws away every skipped row, so deep pages degrade linearly. The offset value is read from the runtime bind ($n), which static SQL linters cannot do. Threshold: config.rules.deep_offset.threshold (default 1000).

Instance Method Summary collapse

Methods inherited from Base

all, check, default_enabled, enabled?, #initialize, option, options, rule_name

Constructor Details

This class inherits a constructor from PgCanary::Rules::Base

Instance Method Details

#checkObject



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/pg_canary/rules/definitions/deep_offset.rb', line 15

def check
  threshold = rule_config.threshold
  detections = []
  each_scope do |scope|
    next unless scope.stmt.limit_offset

    value = numeric_value(scope.stmt.limit_offset)
    next unless value && value >= threshold

    detections << detection(
      table: scope.tables.length == 1 ? scope.tables.first : nil,
      message: "OFFSET #{value} reads and discards #{value} rows before returning anything — " \
               "offset pagination degrades linearly with page depth.",
      suggestion: <<~SUGGESTION.chomp
        Consider keyset pagination instead:
          WHERE (created_at, id) < (:last_seen_created_at, :last_seen_id) ORDER BY created_at DESC, id DESC LIMIT n
      SUGGESTION
    )
  end
  detections
end