MCPcopy Create free account
hub / github.com/PostHog/duckgres / TestIsUserQueryError

Function TestIsUserQueryError

server/transient_test.go:379–430  ·  view source on GitHub ↗

TestIsUserQueryError pins the SQLSTATE-class-based discriminator that splits Query execution log lines between Info ("user wrote something that doesn't make sense") and Error ("the system itself failed"). The logger uses this to keep the Error level meaningful for alerting; a regression here would e

(t *testing.T)

Source from the content-addressed store, hash-verified

377// for alerting; a regression here would either drown alerts in user-
378// typo noise or silently downgrade real infra failures.
379func TestIsUserQueryError(t *testing.T) {
380 tests := []struct {
381 name string
382 err error
383 want bool // true == user error (Info), false == infra error (Error)
384 }{
385 // Class 42 — by far the most common user errors (table/column not
386 // found, syntax errors, access rule violations).
387 {"missing table (42P01)", errors.New("Catalog Error: Table with name users does not exist!"), true},
388 {"missing column (42703)", errors.New("Binder Error: Referenced column \"missing_col\" not found in FROM clause!"), true},
389 {"syntax error (42601)", errors.New("Parser Error: syntax error at or near \"FORM\""), true},
390 {"missing function (42883)", errors.New("Catalog Error: Scalar Function with name no_such_func does not exist!"), true},
391 {"permission denied (42501)", errors.New("Permission Error: not allowed to write here"), true},
392 {"duplicate table (42P07)", errors.New("Catalog Error: Table with name \"t\" already exists!"), true},
393
394 // Other user classes — bad input, integrity, transaction misuse.
395 {"data exception conversion (22P02)", errors.New("Conversion Error: Could not convert string 'abc' to INT32"), true},
396 {"data exception out of range (22003)", errors.New("Out of Range Error: Overflow in multiplication of INT32"), true},
397 {"unique violation (23505)", errors.New("Constraint Error: Duplicate key \"id: 1\" violates primary key constraint"), true},
398 {"not null violation (23502)", errors.New("Constraint Error: NOT NULL constraint failed: t.col"), true},
399 {"invalid transaction state (25000)", errors.New("Transaction Error: cannot begin within an existing transaction"), true},
400 {"missing schema (3F000)", errors.New("Catalog Error: Schema with name \"missing\" does not exist!"), true},
401 {"dependent objects (2BP01)", errors.New("Dependency Error: Cannot drop entry because there are other entries that depend on it"), true},
402
403 // 57014 cancellation — class 57 is "operator intervention". Caller-
404 // driven cancellation (Ctrl-C, deadline, client disconnect) is filtered
405 // at the call site via clientConn.isCallerCancellation, so any
406 // cancellation reaching isUserQueryError is infra (gRPC client closed
407 // because the worker died, takeover, etc.) and must surface at Error.
408 {"infra cancellation (57014)", errors.New("context canceled"), false},
409
410 // Infra classes — must NOT be treated as user errors.
411 {"unknown error → XX000", errors.New("something went wrong"), false},
412 {"SSL closed → infra", errors.New("SSL connection has been closed unexpectedly"), false},
413 {"nil error", nil, false},
414
415 // 40001 retryable conflicts are a special case handled before the
416 // SQLSTATE check fires (logQueryError emits its own Warn for them),
417 // so they never reach this function in production. But verify the
418 // classification is unambiguously infra-side here so a future
419 // caller doesn't accidentally bucket retries as user errors.
420 {"transaction conflict 40001 is not user", errors.New("Transaction conflict on commit"), false},
421 }
422 for _, tt := range tests {
423 t.Run(tt.name, func(t *testing.T) {
424 if got := isUserQueryError(tt.err); got != tt.want {
425 t.Errorf("isUserQueryError(%v) = %v, want %v (SQLSTATE=%s)",
426 tt.err, got, tt.want, classifyErrorCodeOrEmpty(tt.err))
427 }
428 })
429 }
430}
431
432// classifyErrorCodeOrEmpty is a test helper to surface the computed
433// SQLSTATE in failure messages without crashing on nil errors.

Callers

nothing calls this directly

Calls 3

isUserQueryErrorFunction · 0.85
classifyErrorCodeOrEmptyFunction · 0.85
RunMethod · 0.65

Tested by

no test coverage detected