Skip to content

Unknown function call and 1/0 return one NULL row instead of erroring (42883 / 22012 suppressed) #216

Description

@emanzx

Version / build tested against

farhansyah/nodedb:0.4.0 (published Docker image; SELECT version() → NodeDB 0.4.0, wire format 1). Not re-tested against current origin/main — close if already addressed.

Deployment mode

Origin — single node (local), observed through a pgwire client (Crystal / nodedb.cr).

Engine(s) involved

SQL semantics (parser/evaluator) — engine-independent.

Summary

A call to an unknown function evaluates as a successful query returning one row with a single NULL column, instead of raising 42883 undefined_function the way Postgres does. SELECT 1/0 behaves the same way — a successful one-row NULL, not a 22012 division_by_zero. Because the failure is reported as success with a NULL payload, a client cannot distinguish "this expression errored" from "this expression legitimately evaluated to NULL". This suppresses a whole class of errors silently — a typo'd function name, a bad cast, or a divide-by-zero all look like a valid NULL result.

Steps to reproduce

-- unknown function: Postgres raises 42883; NodeDB returns one NULL row, no error
SELECT some_function_that_does_not_exist(1, 2);
--  (1 row)
--  NULL

-- division by zero: Postgres raises 22012; NodeDB returns one NULL row, no error
SELECT 1/0;
--  (1 row)
--  NULL

For contrast, a genuinely malformed reference does error correctly — e.g. SELECT * FROM nonexistent_table_xyz returns ERROR 42P01 collection "nonexistent_table_xyz" does not exist — so the error path exists; it is specifically unknown-function and arithmetic-domain evaluation that is swallowed.

Expected behavior

An unknown function should raise 42883 (undefined_function); 1/0 should raise 22012 (division_by_zero). At minimum, an expression that could not be evaluated must not be reported to the client as a successful NULL result — the client has no way to tell the two apart, which is exactly the condition that lets a bug (or an injected/hallucinated query) pass silently.

Actual behavior

Both cases return CommandComplete with one row and a single NULL (text/OID 25) column, no ErrorResponse. The evaluator appears to fold an unresolved/failed scalar expression to NULL rather than surfacing an error.

What actually happened? (check all that are true)

  • Acknowledged/committed data was lost, corrupted, or silently wrong
  • The server crashed, hung, or failed to start
  • A security or isolation boundary was crossed
  • Core functionality is broken with no acceptable workaround
  • A workaround exists (rewrite the query, avoid one path, etc.)

Proposed severity

SEV-3 — Medium (flagging a possible security angle for triage): silently-wrong results rather than data loss, but the error-suppression class is a real footgun — it hides typos, bad casts, and injected/malformed expressions from any client that relies on an error to detect them. Maintainers may weigh whether the suppression warrants higher at triage, given the security-hardening context.

Reproducibility

Always — every attempt.

Environment & logs

Linux x86_64, farhansyah/nodedb:0.4.0 Docker image, single local node. Observed via a Crystal pgwire client while establishing a wire-facts baseline for nodedb.cr — this behavior is why the ORM asserts result rows / server state in every dialect probe rather than trusting absence-of-error (a SELECT'd expression "not erroring" is not evidence it worked).

Activity

  1. added
    type:bugA defect — broken, incorrect, or lost data
    status:needs-triageAwaiting maintainer triage (severity + priority)
    area:sqlParser, planner, SQL semantics
    sev:3-mediumFeature wrong, but operational and a workaround exists
    and removed
    status:needs-triageAwaiting maintainer triage (severity + priority)
    on Jul 25, 2026
  2. emanzx commented on Jul 26, 2026

    @emanzx
    ContributorAuthor

    Re-verified on origin/main @ f7fcc7718 (release build): still reproduces. SELECT some_function_that_does_not_exist(1, 2) and SELECT 1/0 both return one row, one NULL column, no error; the 42P01 control (SELECT * FROM nonexistent_table_xyz) errors correctly. The staleness caveat in the report body no longer applies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:sqlParser, planner, SQL semanticspriority:P2Scheduled, not urgentsev:3-mediumFeature wrong, but operational and a workaround existstype:bugA defect — broken, incorrect, or lost data

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions