Skip to content

S5 language-lane spec — hardening

Engine/tooling already on main: ormgen validate --dsn, ormgen import --dsn, ormgen errors --lang (docs/errors.yaml → constants), docs/config.md (orm.toml), .github/workflows/ci.yml. Each lane delivers the following for its language. Rules: docs/lanes/s3.md §4 (plus: you may add files under clients/<lang>/** freely; keep generated files generated).

1. Deliverables

itemwhat
error constantscheck in the generated constants file (ormgen errors --lang <lang> --out …: Go clients/go/orm/codes.go, PHP clients/php/src/Code.php (+ autoload map line), Rust clients/rust/orm/src/codes.rs + pub mod codes;) and use the constants instead of string literals in the runtime; map driver errors: MySQL 1213/SQLSTATE 40001 → DEADLOCK, 1062/23000 → DUPLICATE_KEY (the original driver message stays in the error text)
schema_hash boot checkat client init (Go orm.Open / PHP Orm::init / Rust Db::connect), compare the generated code's schema hash with the engine's loaded manifest exactly once; mismatch → SCHEMA_HASH_MISMATCH error, no watching, no reload. PHP asks ormd ({"op":"hash"}), Rust reads the wasm engine's hash, Go compares gen vs engine.M.SchemaHash
runtime connectionThe application injects one DSN URI and runtime options. The URI scheme selects the database. Runtime configuration files are not part of the public connection API.
on_query hookunify the signature: (sql, binds, duration, plan_id, err) where plan_id is the plan cache key (Go uint64 → hex string; PHP the local cache key suffix; Rust the u64 hash) so logs can group by statement shape; keep secrets in binds masked as $SECRET in the hook payload
DSN from the environment for testsintegration tests and the conformance runner read the DSN from ORM_MYSQL_DSN_GO / ORM_MYSQL_DSN_PHP / ORM_MYSQL_URL_RUST when set, else the local /tmp/mysql.sock default (so .github/workflows/ci.yml works unchanged)
fixed cost (T5.3b)measure with examples/thin-slice (3-row query, 500 iterations) and reduce the per-statement overhead: Go — cache the IR shape bytes per call site is impossible, so make shape() cheaper (encode once into a pooled buffer, FNV over bytes without allocation) and avoid the []any row → struct double pass where possible; PHP — avoid re-encoding the IR when the local (per-request) plan cache already has the shape; Rust — decode typed values directly from MySqlRow into the generated struct (skip Vec<Val> for the main step) and hash without serde_json::to_vec allocations. Target: ≤ +5% vs the native line of the demo. Record before/after numbers in your report
PHP onlydecide PDO::ATTR_EMULATE_PREPARES by measurement (PK, 100 rows, IN of 8) and fix one value; make sure ip and JSON columns come back as expected under it
packaging metadataGo: module path; PHP: clients/php/composer.json; Rust: clients/rust/orm/Cargo.toml and generated client assets. Secrets and DSNs are injected at runtime.

2. Verification

Existing integration tests and your runner still pass; add integration coverage for the boot check (a wrong hash → SCHEMA_HASH_MISMATCH), for orm.toml loading (a relative path → CONFIG), for the driver error mapping (insert a duplicate uuid → DUPLICATE_KEY). No new conformance vectors this round (the harness records per-language runner outputs, not config).

MIT License · Go / PHP / Rust / TypeScript