middleware

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func OpenDB

func OpenDB(c driver.Connector, opts ...Option) *sql.DB

OpenDB wraps a driver.Connector and returns an analyzed *sql.DB. Use this when you already hold a connector — for example pgx's stdlib.GetConnector or a driver-specific Connector — and don't want a global registration.

connector := stdlib.GetConnector(*pgxConfig)
db := middleware.OpenDB(connector)

func Register

func Register(name, baseDriver string, opts ...Option) (err error)

Register wraps the database/sql driver currently registered under baseDriver and registers the analyzed result under name. Afterwards sql.Open(name, dsn) yields a *sql.DB whose every query is analyzed.

middleware.Register("sqlguard-sqlite", "sqlite3")
db, _ := sql.Open("sqlguard-sqlite", ":memory:")

It returns an error if name is already registered or baseDriver is not a known driver.

func WrapConnector

func WrapConnector(base driver.Connector, opts ...Option) driver.Connector

WrapConnector returns a driver.Connector that analyzes every query executed through connections it produces.

func WrapDriver

func WrapDriver(base driver.Driver, opts ...Option) driver.Driver

WrapDriver returns a driver.Driver that analyzes every query executed through it. The returned driver also implements driver.DriverContext so connector-based pooling is preserved.

Types

type Guard

type Guard struct {
	// contains filtered or unexported fields
}

Guard is the single shared analysis core. It runs the configured analyzer and reporter against every executed query, measures latency, and feeds the N+1 tracker. Every interception point — the database/sql driver chain and every out-of-tree integration (pgxguard, …) — drives the same Guard so analysis logic, redaction, fingerprinting, N+1, the parser seam and config live here exactly once. Integrations must build on Guard rather than re-implementing check/latency by hand (that path silently loses redaction-by-default and fingerprints).

A Guard is safe for concurrent use.

func NewGuard

func NewGuard(opts ...Option) *Guard

NewGuard builds a Guard from the given options.

func (*Guard) Analyzer

func (g *Guard) Analyzer() *analyzer.Analyzer

Analyzer returns the configured analyzer. Useful for integrations that need the canonical redact/fingerprint helpers without re-deriving policy.

func (*Guard) Check

func (g *Guard) Check(query string)

Check runs the static rules against the query and feeds the N+1 tracker.

func (*Guard) CheckLatency

func (g *Guard) CheckLatency(query string, elapsed time.Duration)

CheckLatency reports a slow-query finding if elapsed exceeds the threshold.

func (*Guard) Observe

func (g *Guard) Observe(query string) func(err error)

Observe analyzes a query and times its execution. The returned function must be called once the underlying operation completes; it records latency only when err is nil (a failed query's latency is meaningless). It is designed for split start/end interception points such as pgx tracers: call Observe in the start hook, stash the closure, invoke it in the end hook with the operation error.

func (*Guard) ResetN1

func (g *Guard) ResetN1()

ResetN1 clears the N+1 tracker's accumulated state. Call this at a per-request boundary (e.g. end of an HTTP handler) so N+1 detection is scoped to a single logical unit of work rather than process-global. It is a no-op when N+1 detection is not enabled.

type Option

type Option func(*options)

Option configures the runtime guard.

func WithAnalysisCacheSize

func WithAnalysisCacheSize(n int) Option

WithAnalysisCacheSize sets the maximum number of distinct query strings whose analysis results are memoized, so a recurring query is parsed and rule-checked once instead of on every execution. The cache is an LRU keyed on the exact query string (correct even for the literal-sensitive rules). Default is 1024. Pass 0 to disable the cache and analyze every query.

func WithAnalyzer

func WithAnalyzer(a *analyzer.Analyzer) Option

WithAnalyzer sets a custom analyzer. Default is analyzer.Default().

func WithFindingDedup

func WithFindingDedup(window time.Duration) Option

WithFindingDedup sets the window within which a repeated static finding — the same rule firing on the same canonical query shape — is reported at most once. This keeps a recurring query (or a prepared statement run in a loop) from flooding the log sink with the same warning on every execution. The default is one minute. Pass 0 to disable dedup and report every occurrence (the legacy behavior). Slow-query and N+1 findings have their own emission policy and are unaffected.

func WithN1Detection

func WithN1Detection(threshold int, window time.Duration) Option

WithN1Detection enables N+1 query detection with the given threshold and window. When the same query pattern is executed threshold times within window, a warning is reported.

func WithParser

func WithParser(p analyzer.Parser) Option

WithParser sets the SQL parser the analyzer uses. Default is the zero-dependency analyzer.FallbackParser. Pass a real dialect parser (e.g. from sqlguard/parsers/pgparser) for exact, structural analysis.

func WithReporter

func WithReporter(r reporter.Reporter) Option

WithReporter sets a custom reporter. Default is ConsoleReporter.

func WithSlowQueryThreshold

func WithSlowQueryThreshold(d time.Duration) Option

WithSlowQueryThreshold sets the duration above which a query is flagged as slow. Default is 200ms.

type QueryTracker

type QueryTracker struct {
	// contains filtered or unexported fields
}

QueryTracker detects N+1 query patterns at runtime. It tracks normalized query patterns and flags when the same pattern is executed more than a threshold number of times within a time window.

func NewQueryTracker

func NewQueryTracker(threshold int, window time.Duration, reportFn func([]analyzer.Result)) *QueryTracker

NewQueryTracker creates a tracker that flags when the same query pattern appears more than threshold times within the given window.

func (*QueryTracker) Reset

func (qt *QueryTracker) Reset()

Reset clears all tracked queries. Call this between requests.

func (*QueryTracker) Track

func (qt *QueryTracker) Track(query string)

Track records a query execution and reports if N+1 pattern is detected.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL