Driver-layer interception
Wraps database/sql at the driver, so you get a real *sql.DB back and every query is analyzed — including the ones your ORM writes
Catch SELECT *, missing WHERE clauses, N+1 loops and slow queries — at runtime through a database/sql driver wrapper, statically in CI, or from the query plan. Think golangci-lint for the SQL your application actually runs.
go get github.com/KARTIKrocks/sqlguardWraps database/sql at the driver, so you get a real *sql.DB back and every query is analyzed — including the ones your ORM writes
SELECT *, DELETE/UPDATE without WHERE, leading wildcards, non-sargable predicates, cartesian joins, deep OFFSETs, and more
Flags the same query fingerprint repeating inside a window; scope it per request with ResetN1()
Latency measured at the driver, reported against a threshold you set
Literals become ? before a finding leaves the process; every finding carries a PII-free fingerprint safe as a metric label
Per-finding de-duplication and an exact-query LRU cache — a hot query is parsed once and reported once per window
sqlguard scan ./... walks Go source, resolves constants and fmt.Sprintf formats, and exits 1 for CI
Plans a query against live Postgres, MySQL or MariaDB inside a rolled-back read-only transaction — never executes it
GORM, sqlx, native pgx/pgxpool, bun, xorm and ent, all built on the same Guard core with the same options
Zero-dependency fallback by default; add pgparser or mysqlparser for AST-exact facts without touching the core
A .sqlguard.yml drives the middleware, the scanner and the CLI — disable rules, override severities, tune thresholds
-- sqlguard:ignore in SQL or // sqlguard:ignore in Go, per rule or wholesale, honored at runtime and statically
Query logging tells you what ran. It does not tell you that the query was a full-table DELETE, that the same lookup just ran 400 times in a loop, or that a LIKE '%…' can never use the index. sqlguard sits at the one place every query passes through — the database/sql driver — and turns those into findings with a rule name, a redacted query and a fix.
| Capability | sqlguard | Hand-rolled query logging |
|---|---|---|
| Every query analyzed, ORM or raw, no wrapper type | Included | You build it |
| 21 SQL anti-pattern rules with tunable severities | Included | You build it |
| N+1 and slow-query detection at the driver | Included | You build it |
| Literal redaction + stable fingerprints | Included | You build it |
| De-duplication and per-query analysis cache | Included | You build it |
| Static scan of Go source for CI | Included | You build it |
| EXPLAIN plan analysis that never executes | Included | You build it |
| GORM / sqlx / pgx / bun / xorm / ent adapters | Included | You build it |
Analysis runs on every intercepted query, so it has to be cheap. Rule configuration is resolved once at construction, repeated queries hit an exact-string LRU, and a hit is a map lookup with zero allocations. Measured on an Intel i5-11400H (12 cores), Go 1.27, Linux.
20 ns/op
0 allocs — analysis cache hit
22 µs/op
full parse + every static rule
~1000×
default 1024-entry LRU
0 third-party
analyzer, middleware, reporter
Reproduce them yourself: go test -bench GuardCheck -benchmem ./middleware/. The repeated-query figure is BenchmarkGuardCheck_Cached, the first-sighting figure BenchmarkGuardCheck_Uncached (cache disabled). Source in middleware/cache_test.go.