From f4524f058cd6345325f5a08b1d0fd980f8d0f66e Mon Sep 17 00:00:00 2001 From: manthaaaaan Date: Sat, 29 Aug 2026 21:18:22 +0530 Subject: [PATCH 1/8] [miniflare] fix: emit valid SQL literal for Infinity/-Infinity in D1 export --- packages/miniflare/src/workers/d1/dumpSql.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/miniflare/src/workers/d1/dumpSql.ts b/packages/miniflare/src/workers/d1/dumpSql.ts index da163b3061a..752926ad53f 100644 --- a/packages/miniflare/src/workers/d1/dumpSql.ts +++ b/packages/miniflare/src/workers/d1/dumpSql.ts @@ -110,6 +110,9 @@ export function* dumpSql( if (cell === null) { return "NULL"; } else if (cellType === "number") { + if (!Number.isFinite(cell)) { + return cell > 0 ? "9e999" : "-9e999"; + } return cell; } else if (cellType === "string") { return outputQuotedEscapedString(cell); From 73c22adf3f6f03a2c023aa02ad272e6d13f70451 Mon Sep 17 00:00:00 2001 From: manthaaaaan Date: Sat, 29 Aug 2026 21:27:02 +0530 Subject: [PATCH 2/8] [miniflare] fix: emit valid SQL literal for Infinity/-Infinity in D1 export --- packages/miniflare/src/workers/d1/dumpSql.ts | 2 +- packages/miniflare/test/plugins/d1/suite.ts | 62 ++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/packages/miniflare/src/workers/d1/dumpSql.ts b/packages/miniflare/src/workers/d1/dumpSql.ts index 752926ad53f..b4420e56fd5 100644 --- a/packages/miniflare/src/workers/d1/dumpSql.ts +++ b/packages/miniflare/src/workers/d1/dumpSql.ts @@ -111,7 +111,7 @@ export function* dumpSql( return "NULL"; } else if (cellType === "number") { if (!Number.isFinite(cell)) { - return cell > 0 ? "9e999" : "-9e999"; + return (cell as number) > 0 ? "9e999" : "-9e999"; } return cell; } else if (cellType === "string") { diff --git a/packages/miniflare/test/plugins/d1/suite.ts b/packages/miniflare/test/plugins/d1/suite.ts index bbd9e4e08fb..0ad7f6a9d38 100644 --- a/packages/miniflare/test/plugins/d1/suite.ts +++ b/packages/miniflare/test/plugins/d1/suite.ts @@ -632,6 +632,68 @@ test("dumpSql exports and imports complete database structure and content correc await isDatabaseEqual(expect, originalDb, mirrorDb); }); +/** + * Test that dumpSql emits a valid SQL literal for non-finite REAL values + * (Infinity / -Infinity), instead of the raw JS Infinity/-Infinity string, + * which SQLite parses back as a bare identifier and fails to re-import. + */ +test("dumpSql exports Infinity and -Infinity as valid, re-importable SQL literals", async ({ + expect, +}) => { + const tmp1 = await useTmp(); + const baseConfig = opts.workers[0].config; + const originalMF = new Miniflare({ + resourcePersistencePath: tmp1, + workers: [ + { config: { ...baseConfig, env: { test: { type: "d1", id: "test" } } } }, + ], + }); + useDispose(originalMF); + const originalDb = await originalMF.getD1Database("test"); + + await originalDb.exec( + `CREATE TABLE inf_test (id INTEGER PRIMARY KEY, r REAL)` + ); + await originalDb + .prepare(`INSERT INTO inf_test (id, r) VALUES (?, ?), (?, ?)`) + .bind(1, Infinity, 2, -Infinity) + .run(); + + const result = await originalDb + .prepare("PRAGMA miniflare_d1_export(?,?,?);") + .bind(0, 0) + .raw(); + const [dumpStatements] = result as [string[]]; + const dump = dumpStatements.join("\n"); + + // The dump must not contain the raw JS token as a bare identifier + expect(dump).not.toMatch(/VALUES\(1,Infinity\)/); + expect(dump).not.toMatch(/VALUES\(2,-Infinity\)/); + + // Re-importing the dump must succeed and preserve the values + const tmp2 = await useTmp(); + const mirrorMF = new Miniflare({ + resourcePersistencePath: tmp2, + workers: [ + { config: { ...baseConfig, env: { test: { type: "d1", id: "test" } } } }, + ], + }); + useDispose(mirrorMF); + const mirrorDb = await mirrorMF.getD1Database("test"); + + // exec(dump) must not throw — this is the core guarantee of the fix. + // (If the dump contained bare `Infinity`/`-Infinity` identifiers, SQLite + // would reject them at parse time and exec would throw.) + await mirrorDb.exec(dump); + + // D1's JS binding layer normalises SQLite's internal Infinity representation + // to null, so we assert both rows were inserted (not lost/skipped). + const rows = await mirrorDb + .prepare(`SELECT id FROM inf_test ORDER BY id`) + .raw(); + expect(rows).toEqual([[1], [2]]); +}); + /** * Populates a D1 database with test data for schema export testing. * Creates tables with various schema features (foreign keys, special characters, etc.) From f10b2b1f474058c924a8dd2637075c096f43208e Mon Sep 17 00:00:00 2001 From: manthaaaaan Date: Sat, 29 Aug 2026 22:01:56 +0530 Subject: [PATCH 3/8] Add changeset --- .changeset/fix-d1-export-infinity.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fix-d1-export-infinity.md diff --git a/.changeset/fix-d1-export-infinity.md b/.changeset/fix-d1-export-infinity.md new file mode 100644 index 00000000000..27e84c2856e --- /dev/null +++ b/.changeset/fix-d1-export-infinity.md @@ -0,0 +1,5 @@ +--- +"miniflare": patch +--- + +Fix Infinity/-Infinity being emitted as an invalid bare identifier in D1 export From 5c72c06c905d49e92728b2aec2831d78476d2be5 Mon Sep 17 00:00:00 2001 From: manthaaaaan Date: Sat, 29 Aug 2026 22:08:17 +0530 Subject: [PATCH 4/8] Assert exact signed literal in dump, per review feedback --- packages/miniflare/test/plugins/d1/suite.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/miniflare/test/plugins/d1/suite.ts b/packages/miniflare/test/plugins/d1/suite.ts index 0ad7f6a9d38..096d13a8bc4 100644 --- a/packages/miniflare/test/plugins/d1/suite.ts +++ b/packages/miniflare/test/plugins/d1/suite.ts @@ -654,10 +654,9 @@ test("dumpSql exports Infinity and -Infinity as valid, re-importable SQL literal await originalDb.exec( `CREATE TABLE inf_test (id INTEGER PRIMARY KEY, r REAL)` ); - await originalDb - .prepare(`INSERT INTO inf_test (id, r) VALUES (?, ?), (?, ?)`) - .bind(1, Infinity, 2, -Infinity) - .run(); + await originalDb.exec( + `INSERT INTO inf_test (id, r) VALUES (1, 9e999), (2, -9e999)` + ); const result = await originalDb .prepare("PRAGMA miniflare_d1_export(?,?,?);") @@ -686,8 +685,13 @@ test("dumpSql exports Infinity and -Infinity as valid, re-importable SQL literal // would reject them at parse time and exec would throw.) await mirrorDb.exec(dump); - // D1's JS binding layer normalises SQLite's internal Infinity representation - // to null, so we assert both rows were inserted (not lost/skipped). + // Assert the exported SQL actually contains the correctly-signed literal + // for each row — this is the real guarantee of the fix, checked before + // the D1 JS binding's Infinity->null normalisation can hide it. + expect(dump).toMatch(/INSERT INTO "inf_test" \("id","r"\) VALUES\(1,9e999\);/); + expect(dump).toMatch(/INSERT INTO "inf_test" \("id","r"\) VALUES\(2,-9e999\);/); + + // And confirm the import actually succeeded (rows exist post-import) const rows = await mirrorDb .prepare(`SELECT id FROM inf_test ORDER BY id`) .raw(); From 5a10ac957d738439e8d9f14bbf5fb69bb1bdd00b Mon Sep 17 00:00:00 2001 From: manthaaaaan Date: Sun, 30 Aug 2026 22:30:40 +0530 Subject: [PATCH 5/8] Fix formatting --- packages/miniflare/test/plugins/d1/suite.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/miniflare/test/plugins/d1/suite.ts b/packages/miniflare/test/plugins/d1/suite.ts index 096d13a8bc4..31618bb7a3b 100644 --- a/packages/miniflare/test/plugins/d1/suite.ts +++ b/packages/miniflare/test/plugins/d1/suite.ts @@ -688,8 +688,12 @@ test("dumpSql exports Infinity and -Infinity as valid, re-importable SQL literal // Assert the exported SQL actually contains the correctly-signed literal // for each row — this is the real guarantee of the fix, checked before // the D1 JS binding's Infinity->null normalisation can hide it. - expect(dump).toMatch(/INSERT INTO "inf_test" \("id","r"\) VALUES\(1,9e999\);/); - expect(dump).toMatch(/INSERT INTO "inf_test" \("id","r"\) VALUES\(2,-9e999\);/); + expect(dump).toMatch( + /INSERT INTO "inf_test" \("id","r"\) VALUES\(1,9e999\);/ + ); + expect(dump).toMatch( + /INSERT INTO "inf_test" \("id","r"\) VALUES\(2,-9e999\);/ + ); // And confirm the import actually succeeded (rows exist post-import) const rows = await mirrorDb From 1636e40376bbf920ac95131788cdd28a8e7404af Mon Sep 17 00:00:00 2001 From: Manthan Basavaraj <1nc23cs092@ncetmail.com> Date: Thu, 10 Sep 2026 23:50:04 +0530 Subject: [PATCH 6/8] Apply suggestion from @dario-piotrowicz Co-authored-by: Dario Piotrowicz --- packages/miniflare/test/plugins/d1/suite.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/miniflare/test/plugins/d1/suite.ts b/packages/miniflare/test/plugins/d1/suite.ts index 31618bb7a3b..dc9dbdb7604 100644 --- a/packages/miniflare/test/plugins/d1/suite.ts +++ b/packages/miniflare/test/plugins/d1/suite.ts @@ -685,9 +685,7 @@ test("dumpSql exports Infinity and -Infinity as valid, re-importable SQL literal // would reject them at parse time and exec would throw.) await mirrorDb.exec(dump); - // Assert the exported SQL actually contains the correctly-signed literal - // for each row — this is the real guarantee of the fix, checked before - // the D1 JS binding's Infinity->null normalisation can hide it. + // Assert the exported SQL actually contains the correctly converted value for each row expect(dump).toMatch( /INSERT INTO "inf_test" \("id","r"\) VALUES\(1,9e999\);/ ); From 2510625766d3a89ec77a6926246238ed054c48aa Mon Sep 17 00:00:00 2001 From: Manthan Basavaraj <1nc23cs092@ncetmail.com> Date: Thu, 10 Sep 2026 23:50:16 +0530 Subject: [PATCH 7/8] Apply suggestion from @dario-piotrowicz Co-authored-by: Dario Piotrowicz --- packages/miniflare/test/plugins/d1/suite.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/miniflare/test/plugins/d1/suite.ts b/packages/miniflare/test/plugins/d1/suite.ts index dc9dbdb7604..26483bc2672 100644 --- a/packages/miniflare/test/plugins/d1/suite.ts +++ b/packages/miniflare/test/plugins/d1/suite.ts @@ -680,9 +680,8 @@ test("dumpSql exports Infinity and -Infinity as valid, re-importable SQL literal useDispose(mirrorMF); const mirrorDb = await mirrorMF.getD1Database("test"); - // exec(dump) must not throw — this is the core guarantee of the fix. - // (If the dump contained bare `Infinity`/`-Infinity` identifiers, SQLite - // would reject them at parse time and exec would throw.) + // exec(dump) must not throw, If the dump contained the bare `Infinity`/`-Infinity` + // identifiers, SQLite would reject them at parse time and exec would throw await mirrorDb.exec(dump); // Assert the exported SQL actually contains the correctly converted value for each row From c49530ffbad85d850d9bcad6c665704a04b1de78 Mon Sep 17 00:00:00 2001 From: Manthan Basavaraj <1nc23cs092@ncetmail.com> Date: Thu, 10 Sep 2026 23:50:28 +0530 Subject: [PATCH 8/8] Apply suggestion from @dario-piotrowicz Co-authored-by: Dario Piotrowicz --- packages/miniflare/test/plugins/d1/suite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/miniflare/test/plugins/d1/suite.ts b/packages/miniflare/test/plugins/d1/suite.ts index 26483bc2672..ad594c084dc 100644 --- a/packages/miniflare/test/plugins/d1/suite.ts +++ b/packages/miniflare/test/plugins/d1/suite.ts @@ -665,7 +665,7 @@ test("dumpSql exports Infinity and -Infinity as valid, re-importable SQL literal const [dumpStatements] = result as [string[]]; const dump = dumpStatements.join("\n"); - // The dump must not contain the raw JS token as a bare identifier + // The dump must not contain the raw JS Infinity/-Infinity tokens as bare identifiers expect(dump).not.toMatch(/VALUES\(1,Infinity\)/); expect(dump).not.toMatch(/VALUES\(2,-Infinity\)/);