Class: PgCanary::Rules::DeepOffset
- Inherits:
-
Base
- Object
- Base
- PgCanary::Rules::DeepOffset
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).
Constant Summary
PgQuerySupport::COMPARISON_OPS
Class Method Summary
collapse
Instance Method Summary
collapse
Methods inherited from Base
all, #enabled?, rule_name
Class Method Details
.options ⇒ Object
14
15
16
|
# File 'lib/pg_canary/rules/definitions/deep_offset.rb', line 14
def self.options
{ threshold: 1000 }
end
|
Instance Method Details
#check(query) ⇒ Object
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/deep_offset.rb', line 18
def check(query)
threshold = rule_config(query).threshold
detections = []
query.each_scope do |scope|
next unless scope.stmt.limit_offset
value = numeric_value(query, scope.stmt.limit_offset)
next unless value && value >= threshold
detections << detection(
query,
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
|
#default_enabled ⇒ Object
10
11
12
|
# File 'lib/pg_canary/rules/definitions/deep_offset.rb', line 10
def default_enabled
true
end
|