chore(api): v0.3.0 follow-ups (A: typed I/O sources, B: InvalidOperation, C: structured SQLSTATE)#76
Merged
StefanSteiner merged 4 commits intoMay 28, 2026
Conversation
Migrates 10 io::Error sites in HyperProcess startup/shutdown from
Error::internal(format!("...: {e}")) to Error::connection_with_io.
This preserves the typed std::io::Error in Error::Connection { source:
Option<io::Error> } so downstream callers can introspect the cause via
std::error::Error::source() instead of parsing the message string.
Sites migrated:
- TcpListener::bind (callback listener creation)
- local_addr / set_nonblocking on the listener
- create_dir_all (UDS socket directory)
- cmd.spawn (hyperd process start)
- listener.accept (callback wait)
- set_nonblocking on the callback stream
- read_exact (length and descriptor reads)
- child.wait (3 sites in shutdown)
Sites left as Error::internal (non-IO):
- timeout waiting for callback (state assertion)
- empty endpoint descriptor (protocol invariant)
- UTF-8 decode failure on descriptor (FromUtf8Error, not io::Error)
- invalid descriptor format (parse failure)
- require_endpoint / require_grpc_endpoint mode mismatch (state)
Separates true library invariant violations (Error::Internal) from caller-API misuse (Error::InvalidOperation). The five ArrowInserter state-machine errors that detect mode mixing or schema-already-sent conditions are caller bugs, not internal invariant violations — they move to Error::InvalidOperation so callers can match on them distinctly. Variant + constructor follow the established String-tuple pattern (Error::invalid_operation(impl Into<String>)). Sites migrated (all in arrow_inserter.rs): - insert_data() / insert_batch() mode mix - insert_data() schema-already-sent - insert_record_batches() / insert_batch() mode mix - insert_record_batches() before insert_data() - insert_raw() / insert_batch() mode mix - insert_batch() / raw-IPC mode mix Sites left as Error::Internal: - inserter.rs:709 (Failed to initialize COPY connection — wire-level) - inserter.rs:1952/2038 (ChunkSender mutex poisoned — actual invariant violation indicating an upstream panic) Also clarifies Error::Internal rustdoc to point at Error::InvalidOperation for caller misuse, and updates MIGRATING-0.3.md with a new "Follow-up B" section + adds the variant to the enum diagram + constructor list.
Reverses the post-tableau#70 caveat that non-Server SQLSTATE codes were folded into the message string. Connection-class (08*), close-class (57P0*), and cancellation (57014) SQLSTATEs are now exposed structurally via the variant's sqlstate field, and Error::sqlstate() returns them too. Variant shape changes (breaking — Cancelled and Closed go from tuple to struct variants): Error::Cancelled(String) -> Error::Cancelled { message: String, sqlstate: Option<String> } Error::Closed(String) -> Error::Closed { message: String, sqlstate: Option<String> } Error::Connection { message, source } -> Error::Connection { message, source, sqlstate: Option<String> } New constructors carry SQLSTATE explicitly; existing constructors stay source-compatible (default sqlstate: None): - Error::cancelled_with_sqlstate(msg, code) - Error::closed_with_sqlstate(msg, code) - Error::connection_with_sqlstate(msg, code) The From<client::Error> mapping populates sqlstate for these kinds. Updates Error::sqlstate() rustdoc; updates the existing sqlstate_returns_some_only_for_server unit test (renamed) to assert the new structural behavior; updates the one MCP match site that destructured Closed/Cancelled as tuples. MIGRATING-0.3.md: adds a Follow-up C section with destructuring + Error::sqlstate() recipes, updates the enum diagram, and reverses the prior caveat about non-Server SQLSTATE.
6 tasks
StefanSteiner
added a commit
that referenced
this pull request
May 28, 2026
* feat: stabilize v0.3.0 public API bundle This commit aggregates the breaking and additive API changes that ship together as v0.3.0. The individual PRs landed under chore: prefixes to defer release-please from cutting an early version; this single feat: commit with a BREAKING CHANGE: footer is the trigger for the v0.3.0 release PR. Bundle contents (all merged to main): - #70 (PR #71) — Flat public Error enum + ergonomic constructors workspace-wide - #69 (PR #73) — Transaction API consolidation (RAII guard recommended; raw trio deprecated and #[doc(hidden)]) - #61 + #62 (PR #74) — FromRow modernization: #[derive(FromRow)] in new hyperdb-api-derive crate, RowAccessor with cached name->index lookup, breaking trait signature change, blanket tuple impls deleted, #[hyperdb(rename)] and #[hyperdb(index)] attributes - #76 — Follow-ups A/B/C: typed io::Error sources in process.rs, Error::InvalidOperation variant for caller misuse, structured SQLSTATE on Cancelled/Closed/Connection Follow-up D (flatten internal client::Error) deferred to v0.3.x as issue #75 — internal-only, zero external consumers, larger than originally scoped. The code change in this commit is a small documentation refresh on the crate-level rustdoc to (a) include hyperdb-api-derive in the companion crates list and (b) fix a stale crate name (sea-query-hyper -> sea-query-hyperdb). The body of the commit is the BREAKING CHANGE: footer below; release-please uses it to generate the v0.3.0 entry in CHANGELOG.md. See MIGRATING-0.3.md for full migration recipes covering every breaking change in the bundle. BREAKING CHANGE: v0.3.0 reshapes the public hyperdb_api::Error enum into a flat canonical structure (no Box<dyn StdError> cause channel, no kind() method, no Other catch-all variant), and its constructor surface (Error::new and Error::with_cause are deleted in favor of domain-specific snake_case constructors). It also changes the FromRow trait signature from fn from_row(row: &Row) to fn from_row(row: RowAccessor<'_>), deletes the blanket 1/2/3/4-tuple FromRow impls, deprecates Connection::begin_transaction/commit/rollback (use the RAII guard at Connection::transaction() instead), introduces a new Error::InvalidOperation variant, and changes Error::Cancelled and Error::Closed from tuple to struct variants carrying structured sqlstate. Every variant has a snake_case constructor; the FromRow derive lives in a re-exported hyperdb-api-derive crate. See MIGRATING-0.3.md for migration recipes. * chore(ci): publish hyperdb-api-derive in release.yml + dry-run in ci.yml Pre-release adversarial review of the v0.3.0 rollup CI/CD config caught that hyperdb-api-derive (added in PR #74) was missing from release.yml's publish-in-dependency-order step. hyperdb-api/Cargo.toml strictly pins hyperdb-api-derive = "=0.X.Y", so cargo publish -p hyperdb-api would fail at release time when crates.io can't resolve the strict version of derive (because release.yml never published it). Verified topologically: - hyperdb-api-derive has zero workspace deps (only syn/quote/proc-macro2 from the registry), so it can publish before any workspace crate. - It's a runtime dep of hyperdb-api only. - Inserted right after hyperdb-api-salesforce; existing order otherwise unchanged. Added a dependency-order comment to the publish step explaining the topology so future contributors don't break it. Also adds hyperdb-api-derive to ci.yml's publish dry-run job. The dry-run job exists exactly to catch this class of bug before release time. Without this addition, the same blocker could re-emerge after a future major-version refactor of derive. Updates the stale "7 workspace-member version rows" comment in release-please.yml to reflect the current 8-member workspace (hyperdb-api-derive added in #74). The lockfile-sync sentinel enumerates members at runtime via cargo metadata, so the count is informational; correctness is unchanged. Verified locally: - cargo publish -p hyperdb-api-derive --dry-run: succeeds - cargo publish -p sea-query-hyperdb --dry-run: succeeds - cargo publish -p hyperdb-bootstrap --dry-run: succeeds - cargo metadata workspace topology check: order in release.yml is consistent with non-dev deps across all 7 publishable crates.
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The four #70 follow-ups for the v0.3.0 bundle. 3 of 4 land here; D defers to v0.3.x as #75.
process.rs. 12 io::Error sites inHyperProcessstartup/shutdown move fromError::internal(format!("...: {e}"))toError::connection_with_io(message, e), preserving the typedstd::io::Errorso callers can introspect viastd::error::Error::source().Error::InvalidOperationvariant. Separates caller-API misuse from library invariant violations. 5 ArrowInserter state-machine errors (mode mixing, schema-already-sent) move fromError::InternaltoError::InvalidOperation.Error::Internalrustdoc clarified to point atInvalidOperationfor caller misuse.Cancelled/Closed/Connection. Reverses the post-Flatten Error type to canonical M-ERRORS shape (drop Client wrapper, Box<dyn> source, Option<ErrorKind>) #70 caveat. SQLSTATE codes that arrive via cancellation (57014), connection-class (08*), or close-class (57P0*) wire errors are now exposed via the variant'ssqlstatefield andError::sqlstate()returns them. Breaking:Error::CancelledandError::Closedchange from tuple to struct variants.Error::Connectiongains asqlstatefield.client::Error. 51 producers + 15 kinds, larger than originally scoped, internal-only (zero external consumers), no user-facing benefit. Filed as #75 for v0.3.x.Bundle policy:
chore:prefix to defer release-please version bump until the v0.3.0 rollupfeat:commit. Each follow-up is breaking on its own; landing under the bundle umbrella.Commits
13f6942— docs typo fix (feat!:→feat:) inMIGRATING-0.3.mdrollup note.9dde324— Follow-up A. 12 io::Error sites migrated; 6 non-IO sites left asError::internal(timeout, descriptor parse, state assertions).33f73e4— Follow-up B. New variant + constructor; 5 inserter state-machine sites migrated; mutex-poisoned sites correctly stayInternal(upstream panic, not caller misuse).666aa4f— Follow-up C. Variant restructuring + 3 new*_with_sqlstateconstructors; broadenedError::sqlstate(); updatedFrom<client::Error>mapping; one MCP match site updated for the tuple→struct transition.Migration
Highlights from
MIGRATING-0.3.md:Verification
cargo build --workspace --all-targets✅cargo clippy --workspace --all-targets -- -D warnings✅cargo test --workspace(full, including doctests) ✅ — 81 test groups passcargo fmt --check✅cargo doc --workspace --no-deps✅ — 6 warnings (= post-Flatten Error type to canonical M-ERRORS shape (drop Client wrapper, Box<dyn> source, Option<ErrorKind>) #70 baseline)cargo run --example async_parity_smoke✅ — 10 stages passWhy Follow-up D defers
Scope grew on second look. Original plan estimated "6 producers, mechanical." Actual workspace state: 51 producer call sites in
hyperdb-api-core/src/client/{client,async_client,connection,grpc/*,tls}.rs, with 15ErrorKindvariants (3 more than #70's public type —InvalidData,InvalidInput,UnexpectedEof).No user-facing benefit.
client::Erroris internal — not re-exported fromhyperdb-api, zero external consumers (verified via grep). The benefit of flattening it is purely codebase-internal hygiene.v0.3.0 has all user-facing improvements without it. Shipping the bundle on time matters more than internal cleanup that downstream callers can't observe.
Filed as #75 for v0.3.x. Approach mirrors #70's pattern.
Test plan
Error::InvalidOperationconstructor +Error::sqlstate()broadened behaviorasync_parity_smokeend-to-end against local hyperdThis closes the v0.3.0 follow-up work. Next step in the bundle: the rollup
feat:commit with aBREAKING CHANGE:footer aggregating #70, #69, #61, #62, and follow-ups A/B/C.