Skip to content

post-#609 regression: pool.query() returns an empty object — is undefined #639

Description

@proggeramlug

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions