57 lines
2.4 KiB
TypeScript
57 lines
2.4 KiB
TypeScript
// A result column whose name is all digits with an interior underscore (e.g.
|
|
// `2024_01`) must stay a NAMED key. The shared ColumnIdentifier classifier was
|
|
// rewritten from Zig to Rust; the Zig version hand-parsed only `'0'..'9'` (any
|
|
// other byte meant "this is a name"), but the Rust port routed the name through
|
|
// `parse_unsigned`, which — mirroring `std.fmt.parseInt` — skips embedded `_`
|
|
// digit separators. So `2024_01` parsed to the integer 202401 and was
|
|
// misclassified as a positional array index.
|
|
//
|
|
// On release 1.3.14 the row decodes as `{ product, "2024_01", "2024_02", "8" }`;
|
|
// on the Rust build (before this fix) `2024_01`/`2024_02` collapse to indices
|
|
// 202401/202402, so those keys vanish — and a debug build aborts on the
|
|
// `cell.index < count` assertion in the object-building slow path.
|
|
//
|
|
// Runs against a real MySQL/MariaDB server. The classifier is shared with
|
|
// Postgres, so this also covers that decoder.
|
|
|
|
import { expect, test } from "bun:test";
|
|
import { bunEnv, bunExe, describeWithContainer } from "harness";
|
|
import path from "path";
|
|
|
|
const fixture = path.join(import.meta.dir, "sql-mysql-column-name-digits.fixture.ts");
|
|
|
|
async function runFixture(url: string) {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), fixture],
|
|
env: { ...bunEnv, MYSQL_URL: url },
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
timeout: 60_000,
|
|
});
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
return { stdout, stderr, exitCode };
|
|
}
|
|
|
|
describeWithContainer("mysql", { image: "mysql_plain" }, container => {
|
|
test("a digits-with-interior-underscore column stays a named key", async () => {
|
|
await container.ready;
|
|
const url = `mysql://root@${container.host}:${container.port}/bun_sql_test`;
|
|
const { stdout, stderr, exitCode } = await runFixture(url);
|
|
|
|
const filteredStderr = stderr
|
|
.split(/\r?\n/)
|
|
.filter(l => l && !l.startsWith("WARNING: ASAN interferes"))
|
|
.join("\n");
|
|
expect(filteredStderr).toBe("");
|
|
const lines = stdout.trim().split(/\r?\n/);
|
|
expect(lines[0]).toBe("CONNECTED");
|
|
// `2024_01`/`2024_02` must be NAMED keys (not indices 202401/202402), `8`
|
|
// round-trips, and nothing is dropped.
|
|
expect(JSON.parse(lines[1] ?? "null")).toEqual({
|
|
row: { product: "widget", "2024_01": 10, "2024_02": 20, "8": 42 },
|
|
keys: ["2024_01", "2024_02", "8", "product"],
|
|
});
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
});
|