From b3b65dddd86d42ff1fbe69370ec704a988c1d886 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Tue, 18 Aug 2026 19:44:36 +0530 Subject: [PATCH] fix(db): stamp the dialect into generated query files The same generate command emitted ? one run and $1 the next, which looked like non-determinism. It is not: postgres uses $1 placeholders where sqlite and mysql use ?, and the driver comes from the active profile, so building under a different profile rewrites this committed file. The header now records the dialect it was generated for, making the flip visible in the diff and explaining check:generated-types failures instead of leaving them looking like random churn. Worth deciding separately: a committed artifact whose contents depend on the active profile will keep drifting. Either generate per dialect, or stop committing it. Co-Authored-By: Claude Opus 5 --- examples/basic-app/app/db/queries.gen.ts | 2 +- packages/db/src/generate.ts | 9 ++++++++- packages/db/test/generate-dialect.test.ts | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 packages/db/test/generate-dialect.test.ts diff --git a/examples/basic-app/app/db/queries.gen.ts b/examples/basic-app/app/db/queries.gen.ts index da6ca380..73f0994a 100644 --- a/examples/basic-app/app/db/queries.gen.ts +++ b/examples/basic-app/app/db/queries.gen.ts @@ -1,4 +1,4 @@ -// AUTO-GENERATED by `wrnexus db generate` — do not edit. +// AUTO-GENERATED by `wrnexus db generate` (dialect: sqlite) — do not edit. import type { Db, ExecResult } from "@wrnexus/db"; import { users } from "./schema.ts"; diff --git a/packages/db/src/generate.ts b/packages/db/src/generate.ts index 5634e85e..6bcb9c63 100644 --- a/packages/db/src/generate.ts +++ b/packages/db/src/generate.ts @@ -230,5 +230,12 @@ export function generateQueriesFile( if (usedModels.size > 0) { imports.push(`import { ${[...usedModels].sort().join(", ")} } from "./schema.ts";`); } - return `// AUTO-GENERATED by \`wrnexus db generate\` — do not edit.\n${imports.join("\n")}\n\n${blocks.join("\n\n")}\n`; + // The dialect is stamped into the header because it changes the emitted SQL: + // postgres uses $1 placeholders where sqlite and mysql use ?. Regenerating + // under a different profile therefore rewrites this committed file, and + // without the stamp the diff looks like unexplained churn. + return ( + `// AUTO-GENERATED by \`wrnexus db generate\` (dialect: ${dialect}) — do not edit.\n` + + `${imports.join("\n")}\n\n${blocks.join("\n\n")}\n` + ); } diff --git a/packages/db/test/generate-dialect.test.ts b/packages/db/test/generate-dialect.test.ts new file mode 100644 index 00000000..aab7a888 --- /dev/null +++ b/packages/db/test/generate-dialect.test.ts @@ -0,0 +1,23 @@ +import { expect, test } from "bun:test"; +import { generateQueriesFile, parseQueries } from "../src/generate.ts"; + +const queries = parseQueries(`-- name: GetUser :one\nSELECT * FROM users WHERE email = :email;\n`); + +test("the generated header records the dialect it was built for", () => { + // The same command emits different SQL per dialect, so a build under another + // profile rewrites the committed file. The stamp makes that visible in the + // diff instead of looking like unexplained churn. + expect(generateQueriesFile(queries, [], "sqlite")).toContain("(dialect: sqlite)"); + expect(generateQueriesFile(queries, [], "postgres")).toContain("(dialect: postgres)"); +}); + +test("placeholder style follows the dialect", () => { + expect(generateQueriesFile(queries, [], "sqlite")).toContain("email = ?"); + expect(generateQueriesFile(queries, [], "postgres")).toContain("email = $1"); +}); + +test("generation is deterministic for a fixed dialect", () => { + const first = generateQueriesFile(queries, [], "postgres"); + const second = generateQueriesFile(queries, [], "postgres"); + expect(first).toBe(second); +});