Skip to content

0.21.0 Correct or Error: two P0s (join NULLs, DDL data loss) and a client-reachable DoS class - #198

Merged
zvndev merged 1 commit into
mainfrom
sprint/0.21.0-correct-or-error
Jul 27, 2026
Merged

0.21.0 Correct or Error: two P0s (join NULLs, DDL data loss) and a client-reachable DoS class#198
zvndev merged 1 commit into
mainfrom
sprint/0.21.0-correct-or-error

Conversation

@zvndev

@zvndev zvndev commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

First release of the road-to-1.0 plan (#197). Every known silent-wrong-answer and client-reachable availability bug in the tree is now a fix or a typed error.

Two P0s, both reproduced against the shipped 0.20.0 binary first

Unqualified columns in a join returned a row of NULLs.

User join Order on User.id = Order.user_id { .name, .amount }
 name | amount          ->  ada  | 50
 NULL | NULL                ada  | 70

Validation accepted a bare field name on the strength of a runtime suffix match that evaluation never implemented. The 0.20.0 round that was meant to end silent wrong answers did not reach join scope. One resolver now serves projection, filter, join-key and sort, replacing three that disagreed; an ambiguous bare name is a typed error, not a silent pick.

DDL inside a transaction destroyed data while reporting success.

begin; drop Keep; rollback   ->  "transaction rolled back"
count(Keep)                  ->  Error: table 'Keep' not found   # gone, across a restart

Now refused at every DDL entry point including drop view, which additionally orphaned the backing table and poisoned the name permanently.

Also fixed

  • Unbounded per-transaction dirty page set could OOM the process under panic = "abort". Now budgeted, configurable via POWDB_DIRTY_PAGE_BUDGET
  • Parse failures and unauthorized writes took a writer permit, letting a read-only principal hold all 1024
  • Window sum reported a false overflow where scalar sum succeeded
  • Aggregates over non-numeric values answered 0; integer overflow clamped to i64::MAX
  • Arithmetic on non-numeric operands yielded NULL; date_add/date_diff/abs used unchecked arithmetic
  • Errors reached drivers as Internal, the class meaning "server bug, not your fault"
  • The miri job had been passing without running: it filtered on a module that does not exist, and a cargo filter matching nothing exits 0

Known limitation, documented not hidden

An update can durably store NULL in a required column while insert refuses the same value. PowDB has no statement-level atomicity, so a per-row refusal mid-statement produces a torn write. That refusal was implemented, measured to tear a multi-row update across a reopen, and deliberately reverted. Statement atomicity is the real fix and belongs in the assurance release. docs/STABILITY.md carries the reproduction.

Process

Three rounds. Adversarial verifiers rejected 3 of 4 lanes in round 1, including a data-corruption regression whose own test hid it behind a one-row fixture, and caught a newly introduced silent wrong answer in round 2 where a projection alias suppressed the ambiguity guard for a sibling field. Every fix was mutation-tested by reverting it and confirming the test fails.

Suite 1837 passed, 0 failed. Clippy and fmt clean.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Every known silent-wrong-answer and client-reachable availability bug in the
tree is now a fix or a typed error.

Two P0s, both reproduced against the shipped 0.20.0 binary before being fixed:

Unqualified columns in a join returned a row of NULLs. Validation accepted a
bare field name on the strength of a runtime suffix match that evaluation never
implemented, so the query returned NULL for every column while the qualified
form was correct. The 0.20.0 round that was supposed to end silent wrong
answers did not reach join scope. One resolver now serves the projection,
filter, join-key and sort paths, replacing three that disagreed, and a name two
tables both expose is a typed ambiguity error rather than a silent pick.

DDL inside a transaction destroyed data while reporting success. `begin; drop
T; rollback` said "transaction rolled back" and the table was permanently gone
across a restart. DDL is refused inside a transaction at every entry point,
including `drop view`, which additionally left the backing table orphaned and
the name unusable forever.

Also fixed: an unbounded per-transaction dirty page set that could OOM the
process under panic = abort; parse failures and unauthorized writes taking a
writer admission permit, letting a read-only principal hold all 1024; window
sum reporting a false overflow where scalar sum succeeded; aggregates over
non-numeric values answering 0; integer overflow clamping to i64::MAX;
arithmetic on non-numeric operands yielding NULL; unchecked arithmetic in
date_add, date_diff and abs; and errors that reached drivers as Internal, the
class meaning "server bug, not your fault".

The miri job had been passing without running: it filtered on a module that
does not exist, and a cargo filter matching nothing exits 0. Fixed, with a
guard that fails the job when a filter matches no tests.

Known limitation, documented rather than hidden: an update can durably store
NULL in a required column while insert refuses the same value. PowDB has no
statement-level atomicity, so refusing a per-row value mid-statement produces a
torn write. That refusal was implemented, measured to tear a multi-row update
across a reopen, and deliberately reverted. Statement atomicity is the real fix
and belongs in the assurance release, not bolted onto a bug fix.
docs/STABILITY.md carries the reproduction.

Process: three rounds. Adversarial verifiers rejected 3 of 4 lanes in round 1,
including a data-corruption regression whose own test hid it behind a one-row
fixture, and found a newly introduced silent wrong answer in round 2 where a
projection alias suppressed the ambiguity guard for a sibling field. Every fix
was mutation-tested by reverting it and confirming the test fails.

Suite 1837 passed, 0 failed. Clippy and fmt clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zvndev
zvndev merged commit c8723c7 into main Jul 27, 2026
23 checks passed
@zvndev zvndev mentioned this pull request Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant