Summary
Following up on #609 (closed in v0.5.739): the native server now boots through migrations + the crypto KID step (the parameterized INSERT with a Buffer parameter no longer segfaults — thank you), but every subsequent read path fails because pool.query() returns an empty object instead of a { rows, rowCount, lastInsertId, ... } payload.
Concretely: Object.keys(await pool.query("SELECT 1")) is an empty array. JSON.stringify yields the empty string. .rows is undefined.
This was working in 0.5.706 (where I tested an earlier INSERT-then-SELECT repro for #609 and r.rows.length === 0 evaluated correctly).
Reproducer
import { Pool } from "@perryts/mysql";
async function main() {
const pool = new Pool({ url: "mysql://root:@127.0.0.1:3306/test" });
const r: unknown = await pool.query("SELECT 1 AS n");
console.log("typeof r:", typeof r);
console.log("JSON.stringify(r):", JSON.stringify(r));
console.log("Object.keys(r):", Object.keys(r as object).join(","));
await pool.end();
}
main().catch((e) => { console.error(e); process.exit(2); });
$ perry compile probe.ts -o probe && ./probe
typeof r: object
JSON.stringify(r): ""
Object.keys(r):
Same code under tsx probe.ts returns the expected shape.
A second repro showing the downstream impact:
const id = crypto.randomBytes(16);
await pool.query("CREATE TABLE probe (id BINARY(16) PRIMARY KEY, label VARCHAR(64))");
await pool.query("INSERT INTO probe (id, label) VALUES (?, ?)", [id, "hello"]);
const r1 = await pool.query("SELECT COUNT(*) AS n FROM probe");
console.log("count rows:", JSON.stringify(r1.rows)); // undefined
const r2 = await pool.query("SELECT label FROM probe WHERE id = ?", [id]);
console.log("by-id rows:", JSON.stringify(r2.rows)); // undefined
Both r1.rows and r2.rows are undefined. The INSERT appears to succeed (no error, no segfault) but I can't tell whether it actually persisted because the SELECT-back returns nothing recognizable.
Discovery context
The native server boots cleanly post-#609 fix:
[1/6] init MySQL pool
[2/6] run migrations
applied: 0001_init.sql ... 0005_shop_timezone.sql
[3/6] ensure active crypto KID
[crypto] WARNING: Perry stdlib lacks AES; ...
active kid: dek-moyi9zr3-055c8a4d ← #609 fix worked
[4/7] start fastify
Server listening on http://0.0.0.0:18080
Then on the first POST /v1/auth/signup:
Error: createUser: row not found after insert
createUser is:
await exec("INSERT INTO users (id, email, passwordHash, ...) VALUES (?, ?, ?, ...)", [...]);
const row = await getUserById(id);
if (!row) throw new Error("createUser: row not found after insert");
getUserById calls queryOne which reads result.rows[0]. With the new return shape, result.rows is undefined → queryOne returns null → createUser throws. Same shape would break every other read path (signin's getUserByEmail, every cache lookup, every CRUD list endpoint).
Environment
Why we filed
This is the only thing now blocking the native server binary. With #609's INSERT segfault fixed and this query-result regression resolved, we can flip our entire 24-suite e2e from tsx server/main.ts to ./build/server and ship a fully native production binary.
The fix likely needs to live alongside #609 — possibly the same NATIVE_MODULE_TABLE entry change that fixed the INSERT crash also altered how pool.query packages its return value to JS-land.
Summary
Following up on #609 (closed in v0.5.739): the native server now boots through migrations + the crypto KID step (the parameterized INSERT with a Buffer parameter no longer segfaults — thank you), but every subsequent read path fails because
pool.query()returns an empty object instead of a{ rows, rowCount, lastInsertId, ... }payload.Concretely:
Object.keys(await pool.query("SELECT 1"))is an empty array.JSON.stringifyyields the empty string..rowsisundefined.This was working in 0.5.706 (where I tested an earlier
INSERT-then-SELECTrepro for #609 andr.rows.length === 0evaluated correctly).Reproducer
Same code under
tsx probe.tsreturns the expected shape.A second repro showing the downstream impact:
Both
r1.rowsandr2.rowsareundefined. The INSERT appears to succeed (no error, no segfault) but I can't tell whether it actually persisted because the SELECT-back returns nothing recognizable.Discovery context
The native server boots cleanly post-#609 fix:
Then on the first
POST /v1/auth/signup:createUseris:getUserByIdcallsqueryOnewhich readsresult.rows[0]. With the new return shape,result.rowsis undefined →queryOnereturns null →createUserthrows. Same shape would break every other read path (signin'sgetUserByEmail, every cache lookup, every CRUD list endpoint).Environment
perry 0.5.739(the version that closed post-#591 regression: exec(INSERT, [str, Buffer]) after SELECT segfaults the runtime #609)@perryts/mysql 0.1.3--target macos, arm64Why we filed
This is the only thing now blocking the native server binary. With #609's INSERT segfault fixed and this query-result regression resolved, we can flip our entire 24-suite e2e from
tsx server/main.tsto./build/serverand ship a fully native production binary.The fix likely needs to live alongside #609 — possibly the same NATIVE_MODULE_TABLE entry change that fixed the INSERT crash also altered how
pool.querypackages its return value to JS-land.