Repository: cloudflare/workers-sdk (2025-07-07T05:48:32Z)

Issue:
🐛 BUG: `wrangler d1 export` writes `ERROR near...` to output file
### Which Cloudflare product(s) does this pertain to?

D1

### What version(s) of the tool(s) are you using?

3.57.1 [Wrangler]

### What version of Node are you using?

v21.6.1

### What operating system and version are you using?

macOS Sonoma 14.4.1

### Describe the Bug

### Observed behavior
When running `npx wrangler d1 export my-db --remote --output myfile.sql`, I get some tables, but then `ERROR near "default": syntax error at offset 39`. Here's the end of the file:
```sql
[ here are some CREATE TABLE IF NOT EXISTS and INSERT INTO queries ]
CREATE TABLE IF NOT EXISTS "Layout" (
    "id" TEXT NOT NULL PRIMARY KEY,
    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" DATETIME NOT NULL,
    "name" TEXT NOT NULL,
    "default" BOOLEAN NOT NULL DEFAULT false,
    "active" BOOLEAN NOT NULL DEFAULT false,
    "media" TEXT NOT NULL,
    "objects" TEXT NOT NULL,
    "a4Options" TEXT NOT NULL,
    "ticketOptions" TEXT NOT NULL,
    "walletOptions" TEXT NOT NULL,
    "plasticCardOptions" TEXT NOT NULL,
    "receiptOptions" TEXT NOT NULL,
    "organizationId" TEXT,
    CONSTRAINT "Layout_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
ERROR near "default": syntax error at offset 39
```

### Expected behavior
It exports correctly.

### Steps to reproduce
To be honest, I don't know. The `Layout` table contains some entries with JSON strings, but those are just `{}`. Maybe the column called `default` is the problem?

### Please provide a link to a minimal reproduction

_No response_

### Please provide any relevant error logs

_No response_
🐛 BUG: SQLite error on wrangler d1 export of tables with hyphen in column identifier
### Which Cloudflare product(s) does this pertain to?

D1

### What version(s) of the tool(s) are you using?

3.67.1 [wrangler]

### What version of Node are you using?

22.0.0

### What operating system and version are you using?

MacOS Sonoma 14.4.1 (23E224)

### Describe the Bug

### Observed behavior

When I do `npx wrangler d1 export dbname --remote --output=./database.sql`, I received the following error:

    Sentry: Capturing exception Error: no such column: step01 at offset 23: SQLITE_ERROR

### Expected behavior

The database should be saved as database.sql. 

### Steps to reproduce

I believe this might be related to the hyphen I use in column names. Below is an abridged schema.sql I used to create the database in D1: 

```sql
DROP TABLE IF EXISTS tablename;
CREATE TABLE IF NOT EXISTS tablename ('id' INTEGER PRIMARY KEY, 'step01-q01' VARCHAR(255));
```


### Please provide a link to a minimal reproduction

_No response_

### Please provide any relevant error logs

_No response_
🐛 BUG: d1 reserved key words fail on export but not migrations
### Which Cloudflare product(s) does this pertain to?

D1

### What versions are you using?

3.113.0

### What operating system and version are you using?

ubuntu

### Please provide a link to a minimal reproduction

_No response_

### Describe the Bug

If the data table has reserved words such as group and order, there was no problem when I created the application to d1 before, but an error would be reported when exporting, and the db would be loaded in the console all the time, and other operations could not be performed.

wrangler d1 export mydb --remote --output ./backup/20250306.sql

 ⛅️ wrangler 3.112.0 (update available 3.113.0)
---------------------------------------------------------

🌀 Executing on remote database mydb  (1bad90ef-990b-4ad1-908b-4bddd1f3ffd9):

✘ [ERROR] near "group": syntax error at offset 74: SQLITE_ERROR


If you think this is a bug then please create an issue at https://github.com/cloudflare/workers-sdk/issues/new/choose
Note that there is a newer version of Wrangler available (3.113.0). Consider checking whether upgrading resolves this error.
✖ Would you like to report this error to Cloudflare? Wrangler's output and the error details will be shared with the Wrangler team to help us diagnose and fix the issue. … yes

### Please provide any relevant error logs


--- 2025-03-06T14:21:55.124Z debug
🪵  Writing logs to "/root/.wrangler/logs/wrangler-2025-03-06_14-21-54_939.log"
---

--- 2025-03-06T14:21:55.124Z debug
Metrics dispatcher: Dispatching disabled - would have sent {"deviceId":"d44bd0f4-5a37-4f7c-bd6a-c98b02150bdd","event":"wrangler command started","timestamp":1741270915123,"properties":{"amplitude_session_id":1741270915121,"amplitude_event_id":0,"wranglerVersion":"3.112.0","osPlatform":"Linux","osVersion":"#45-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 30 12:02:04 UTC 2024","nodeVersion":22,"isFirstUsage":false,"configFileType":"toml","isCI":false,"isPagesCI":false,"isWorkersCI":false,"isInteractive":true,"hasAssets":true,"argsUsed":["output","remote"],"argsCombination":"output, remote","command":"wrangler d1 export","args":{"remote":true,"output":"<REDACTED>","xJsonConfig":true,"j":true,"schema":true,"data":true,"name":"<REDACTED>"}}}.
---

--- 2025-03-06T14:21:55.126Z debug
.env file not found at ".env". Continuing... For more details, refer to https://developers.cloudflare.com/workers/wrangler/system-environment-variables/
---

--- 2025-03-06T14:21:56.010Z log

 ⛅️ wrangler 3.112.0 (update available [32m3.113.0[39m)
[38;2;255;136;0m---------------------------------------------------------[39m

---

--- 2025-03-06T14:21:56.016Z debug
No experimental flag store instantiated
---

--- 2025-03-06T14:21:56.016Z debug
Attempted to use flag "RESOURCES_PROVISION" which has not been instantiated
---

--- 2025-03-06T14:21:56.016Z debug
No experimental flag store instantiated
---

--- 2025-03-06T14:21:56.016Z debug
Attempted to use flag "RESOURCES_PROVISION" which has not been instantiated
---

--- 2025-03-06T14:21:56.016Z debug
No experimental flag store instantiated
---

--- 2025-03-06T14:21:56.017Z debug
Attempted to use flag "RESOURCES_PROVISION" which has not been instantiated
---

--- 2025-03-06T14:21:56.017Z debug
No experimental flag store instantiated
---

--- 2025-03-06T14:21:56.017Z debug
Attempted to use flag "RESOURCES_PROVISION" which has not been instantiated
---

--- 2025-03-06T14:21:56.017Z debug
No experimental flag store instantiated
---

--- 2025-03-06T14:21:56.017Z debug
Attempted to use flag "RESOURCES_PROVISION" which has not been instantiated
---

--- 2025-03-06T14:21:56.020Z debug
Checking if domain has Access enabled: dash.cloudflare.com
---

--- 2025-03-06T14:21:56.020Z debug
Access switch not cached for: dash.cloudflare.com
---

--- 2025-03-06T14:21:56.112Z debug
Caching access switch for: dash.cloudflare.com
---

--- 2025-03-06T14:21:56.273Z debug
Retrieving cached values for account from node_modules/.cache/wrangler
---

--- 2025-03-06T14:21:56.273Z log
🌀 Executing on remote database mydb (1bad90ef-990b-4ad1-908b-4bddd1f3ffd9):
---

--- 2025-03-06T14:21:56.274Z debug
-- START CF API REQUEST: POST https://api.cloudflare.com/client/v4/accounts/8863216c46bc0b22436dac7ad793a08f/d1/database/1bad90ef-990b-4ad1-908b-4bddd1f3ffd9/export
---

--- 2025-03-06T14:21:56.275Z debug
HEADERS: omitted; set WRANGLER_LOG_SANITIZE=false to include sanitized data
---

--- 2025-03-06T14:21:56.275Z debug
INIT: omitted; set WRANGLER_LOG_SANITIZE=false to include sanitized data
---

--- 2025-03-06T14:21:56.275Z debug
-- END CF API REQUEST
---

--- 2025-03-06T14:21:59.246Z debug
-- START CF API RESPONSE: OK 200
---

--- 2025-03-06T14:21:59.247Z debug
HEADERS: omitted; set WRANGLER_LOG_SANITIZE=false to include sanitized data
---

--- 2025-03-06T14:21:59.247Z debug
RESPONSE: omitted; set WRANGLER_LOG_SANITIZE=false to include sanitized data
---

--- 2025-03-06T14:21:59.247Z debug
-- END CF API RESPONSE
---

--- 2025-03-06T14:21:59.250Z log

---

--- 2025-03-06T14:21:59.309Z error
[31m✘ [41;31m[[41;97mERROR[41;31m][0m [1mnear "group": syntax error at offset 74: SQLITE_ERROR[0m


---

--- 2025-03-06T14:21:59.310Z debug
Error: near "group": syntax error at offset 74: SQLITE_ERROR
    at pollExport (/root/.local/share/fnm/node-versions/v22.7.0/installation/lib/node_modules/wrangler/wrangler-dist/cli.js:118438:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async spinnerWhile (/root/.local/share/fnm/node-versions/v22.7.0/installation/lib/node_modules/wrangler/wrangler-dist/cli.js:90155:20)
    at async exportRemotely (/root/.local/share/fnm/node-versions/v22.7.0/installation/lib/node_modules/wrangler/wrangler-dist/cli.js:118393:25)
    at async Object.Handler [as handler] (/root/.local/share/fnm/node-versions/v22.7.0/installation/lib/node_modules/wrangler/wrangler-dist/cli.js:118337:12)
---

--- 2025-03-06T14:21:59.310Z log
[32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/workers-sdk/issues/new/choose[0m
---

--- 2025-03-06T14:21:59.310Z log
Note that there is a newer version of Wrangler available (3.113.0). Consider checking whether upgrading resolves this error.
---



Discussion:
Yes, the `default` column is indeed the culprit. I renamed it to `isDefault` and it worked just fine.

@geelen - can you take a look?
Just to clarify... This seems to only affect exports. A dash is allowed as a column identifier in SQLite. 

Hey @jiangsi, from what I understand from reading the error log, it looks like you've used a reserved keyword `GROUP` in your query. Would you be able to send through the query string that's resulting in the error here?
If you define a table's field as a reserved word such as "order" or "group," the table can be created when applying migrations with wrangler d1 migrations apply. However, it cannot be exported.
hi @jiangsi , are you able to provide a reproduction or the query string in use (per @FlareLine 's comment) so that the d1 team can investigate further? thanks!

cc @lambrospetrou 
> hi [@jiangsi](https://github.com/jiangsi) , are you able to provide a reproduction or the query string in use (per [@FlareLine](https://github.com/FlareLine) 's comment) so that the d1 team can investigate further? thanks!
> 
> cc [@lambrospetrou](https://github.com/lambrospetrou)

If the data table fields contain reserved words, such as order and group, then you can apply but cannot export
/drizzle/test.sql

`CREATE TABLE `book` (
	`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
	`name` text NOT NULL,
	`order` integer DEFAULT 100,
	`created_at` text DEFAULT CURRENT_TIMESTAMP,
	`updated_at` text DEFAULT CURRENT_TIMESTAMP
);`

`wrangler config
[[d1_databases]]
binding = "MY_DB"
database_name = "summer-lake-7cca-db"
database_id = "c81bb4ae-11cd-4cad-9881-570705f78a35"
migrations_dir = "./drizzle/" `

it is success to apply to remote .

`npx wrangler d1 migrations apply summer-lake-7cca-db --remote


Migrations to be applied:
┌──────────┐
│ name     │
├──────────┤
│ test.sql │
└──────────┘
√ About to apply 1 migration(s)
Your database may not be available to serve requests during the migration, continue? ... yes
🌀 Executing on remote database summer-lake-7cca-db (c81bb4ae-11cd-4cad-9881-570705f78a35):
🌀 To execute on your local development database, remove the --remote flag from your wrangler command.
🚣 Executed 2 commands in 0.6242000000000001ms
┌──────────┬────────┐
│ name     │ status │
├──────────┼────────┤
│ test.sql │ ✅       │
└──────────┴────────┘`


but ,it can not export 

`wrangler d1 export summer-lake-7cca-db  --remote --output ./output.sql

🌀 Executing on remote database summer-lake-7cca-db (c81bb4ae-11cd-4cad-9881-570705f78a35):

X [ERROR] near "order": syntax error at offset 17: SQLITE_ERROR


If you think this is a bug then please create an issue at https://github.com/cloudflare/workers-sdk/issues/new/choose
Note that there is a newer version of Wrangler available (4.1.0). Consider checking whether upgrading resolves this error.
× Would you like to report this error to Cloudflare? Wrangler's output and the error details will be shared with the Wrangler team to help us diagnose and fix the issue. ... yes
🪵  Logs were written to "C:\Users\jiang\.wrangler\logs\wrangler-2025-03-18_04-28-16_223.log"`
I got the same issue: 
```
pnpm wrangler d1 export mydb --remote --no-schema --output db-export.sql

 ⛅️ wrangler 4.10.0
-------------------

🌀 Executing on remote database xxx-db (xxxx):

✘ [ERROR] near "order": syntax error at offset 79: SQLITE_ERROR
```

I can't export my d1 data anymore... I can export the schema at least
> it looks like you've used a reserved keyword `GROUP` in your query. Would you be able to send through the query string that's resulting in the error here?

The query is generated by Cluodflare API, we only executed the `export` command

log:

```text
--- 2025-04-22T05:20:39.546Z debug
Error: near "order": syntax error at offset 65: SQLITE_ERROR
    at pollExport (D:\dev\yume-chan\cloudflare-test\node_modules\.pnpm\wrangler@4.12.0\node_modules\wrangler\wrangler-dist\cli.js:115613:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async pollExport (D:\dev\yume-chan\cloudflare-test\node_modules\.pnpm\wrangler@4.12.0\node_modules\wrangler\wrangler-dist\cli.js:115630:12)
    at async pollExport (D:\dev\yume-chan\cloudflare-test\node_modules\.pnpm\wrangler@4.12.0\node_modules\wrangler\wrangler-dist\cli.js:115630:12)
    at async pollExport (D:\dev\yume-chan\cloudflare-test\node_modules\.pnpm\wrangler@4.12.0\node_modules\wrangler\wrangler-dist\cli.js:115630:12)
    at async spinnerWhile (D:\dev\yume-chan\cloudflare-test\node_modules\.pnpm\wrangler@4.12.0\node_modules\wrangler\wrangler-dist\cli.js:89655:20)
    at async exportRemotely (D:\dev\yume-chan\cloudflare-test\node_modules\.pnpm\wrangler@4.12.0\node_modules\wrangler\wrangler-dist\cli.js:115568:25)
    at async Object.Handler [as handler] (D:\dev\yume-chan\cloudflare-test\node_modules\.pnpm\wrangler@4.12.0\node_modules\wrangler\wrangler-dist\cli.js:115512:12)
---
```

offending code:

https://github.com/cloudflare/workers-sdk/blob/41f095b0dd35411adbca3398966b5cfe8c39d433/packages/wrangler/src/d1/export.ts#L219-L237
ref 

* #5920
* #6357
* #6675



Patch:
diff --git a/.changeset/mean-schools-see.md b/.changeset/mean-schools-see.md
new file mode 100644
index 000000000000..597113ecdccd
--- /dev/null
+++ b/.changeset/mean-schools-see.md
@@ -0,0 +1,8 @@
+---
+"miniflare": patch
+---
+
+Fix D1 SQL dump generation: escape identifiers and handle SQLite's dynamic typing
+
+Escape column and table names to prevent SQL syntax errors.
+Escape values based on their runtime type to support SQLite's flexible typing.
diff --git a/packages/miniflare/src/workers/d1/dumpSql.ts b/packages/miniflare/src/workers/d1/dumpSql.ts
index b3cfed757fce..42b90687dac7 100644
--- a/packages/miniflare/src/workers/d1/dumpSql.ts
+++ b/packages/miniflare/src/workers/d1/dumpSql.ts
@@ -19,13 +19,14 @@ export function* dumpSql(
 	// Taken from SQLite shell.c.in https://github.com/sqlite/sqlite/blob/105c20648e1b05839fd0638686b95f2e3998abcb/src/shell.c.in#L8463-L8469
 	// @ts-ignore (SqlStorageStatement needs to be callable)
 	const tables_cursor = db.prepare(`
-    SELECT name, type, sql 
-      FROM sqlite_schema AS o 
-    WHERE (true) AND type=='table' 
-      AND sql NOT NULL 
+    SELECT name, type, sql
+      FROM sqlite_schema AS o
+    WHERE (true) AND type=='table'
+      AND sql NOT NULL
     ORDER BY tbl_name='sqlite_sequence', rowid;
   `)();
-	const tables: any[] = Array.from(tables_cursor);
+	const tables: { name: string; type: string; sql: string }[] =
+		Array.from(tables_cursor);
 
 	for (const { name: table, sql } of tables) {
 		if (filterTables.size > 0 && !filterTables.has(table)) continue;
@@ -54,10 +55,18 @@ export function* dumpSql(
 		}
 
 		if (noData) continue;
-		const columns_cursor = db.exec(`PRAGMA table_info="${table}"`);
-		const columns = Array.from(columns_cursor);
-		const select = `SELECT ${columns.map((c) => c.name).join(", ")}
-                            FROM "${table}";`;
+		const columns_cursor = db.exec(`PRAGMA table_info=${escapeId(table)}`);
+
+		const columns = Array.from(columns_cursor) as {
+			cid: string;
+			name: string;
+			type: string;
+			notnull: number;
+			dflt_val: string | null;
+			pk: number;
+		}[];
+
+		const select = `SELECT ${columns.map((c) => escapeId(c.name)).join(", ")} FROM ${escapeId(table)};`;
 		const rows_cursor = db.exec(select);
 		for (const dataRow of rows_cursor.raw()) {
 			const formattedCells = dataRow.map((cell: unknown, i: number) => {
@@ -65,9 +74,9 @@ export function* dumpSql(
 				const cellType = typeof cell;
 				if (cell === null) {
 					return "NULL";
-				} else if (colType === "INTEGER" || cellType === "number") {
+				} else if (cellType === "number") {
 					return cell;
-				} else if (colType === "TEXT" || cellType === "string") {
+				} else if (cellType === "string") {
 					return outputQuotedEscapedString(cell);
 				} else if (cell instanceof ArrayBuffer) {
 					return `X'${Array.prototype.map
@@ -79,7 +88,7 @@ export function* dumpSql(
 				}
 			});
 
-			yield `INSERT INTO ${sqliteQuote(table)} VALUES(${formattedCells.join(",")});`;
+			yield `INSERT INTO ${escapeId(table)} VALUES(${formattedCells.join(",")});`;
 		}
 	}
 
@@ -138,6 +147,15 @@ export function sqliteQuote(token: string) {
 		: token;
 }
 
+/**
+ * Escape an identifier for use in SQL statements.
+ * @param id - The identifier to escape.
+ * @returns
+ */
+function escapeId(id: string) {
+	return `"${id.replace(/"/g, '""')}"`;
+}
+
 // List taken from `aKeywordTable` inhttps://github.com/sqlite/sqlite/blob/378bf82e2bc09734b8c5869f9b148efe37d29527/tool/mkkeywordhash.c#L172
 // prettier-ignore
 export const SQLITE_KEYWORDS = new Set([


Test patch:
diff --git a/packages/miniflare/test/plugins/d1/suite.ts b/packages/miniflare/test/plugins/d1/suite.ts
index 28d33c6cf8e5..ab68f8f2f8b7 100644
--- a/packages/miniflare/test/plugins/d1/suite.ts
+++ b/packages/miniflare/test/plugins/d1/suite.ts
@@ -1,8 +1,11 @@
 import assert from "assert";
 import fs from "fs/promises";
+import { type D1Database } from "@cloudflare/workers-types/experimental";
+import { ExecutionContext } from "ava";
 import { Miniflare, MiniflareOptions } from "miniflare";
 import { useTmp, utf8Encode } from "../../test-shared";
 import { binding, getDatabase, opts, test } from "./test";
+import type { Context } from "./test";
 
 export const SCHEMA = (
 	tableColours: string,
@@ -580,3 +583,149 @@ test("it properly handles ROWS_AND_COLUMNS results format", async (t) => {
 	}
 	t.deepEqual(results, expectedResults);
 });
+
+/**
+ * Test that the `dumpSql` method returns a valid SQL dump of the database.
+ * This test creates a new D1 database, fills it with dummy data, and then
+ * exports the SQL dump using the `PRAGMA miniflare_d1_export` command.
+ * It then executes the dump in a new D1 database and checks if both databases
+ * are equal in terms of schema and data.
+ */
+test("dumpSql exports and imports complete database structure and content correctly", async (t) => {
+	// Create a new Miniflare instance with D1 database
+	const originalMF = new Miniflare({
+		...opts,
+		d1Databases: { test: "test" },
+	});
+	const mirrorMF = new Miniflare({
+		...opts,
+		d1Databases: { test: "test" },
+	});
+
+	t.teardown(() => originalMF.dispose());
+	t.teardown(() => mirrorMF.dispose());
+
+	const originalDb = await originalMF.getD1Database("test");
+	const mirrorDb = await mirrorMF.getD1Database("test");
+
+	// Fill the original database with dummy data
+	await fillDummyData(originalDb);
+
+	// Export the database schema and data
+	const result = await originalDb
+		.prepare("PRAGMA miniflare_d1_export(?,?,?);")
+		.bind(0, 0)
+		.raw();
+
+	const [dumpStatements] = result as [string[]];
+	const dump = dumpStatements.join("\n");
+
+	await mirrorDb.exec(dump);
+
+	// Verify that the schema and data in both databases are equal
+	await isDatabaseEqual(t, originalDb, mirrorDb);
+});
+
+/**
+ * Populates a D1 database with test data for schema export testing.
+ * Creates tables with various schema features (foreign keys, special characters, etc.)
+ * and inserts sample data including edge cases like NULL values and type mismatches.
+ */
+async function fillDummyData(db: D1Database) {
+	// Create schema with various SQL features to test export compatibility
+	// Each table must have an ID column as primary key so that we can use it for ordering in equality tests
+
+	const schemas = [
+		// Create basic table with text primary key
+		`CREATE TABLE "classrooms"(id TEXT PRIMARY KEY, capacity INTEGER, test_blob BLOB)`,
+
+		// Create table with foreign key constraint
+		`CREATE TABLE "students" (id INTEGER PRIMARY KEY, name TEXT NOT NULL, classroom TEXT NOT NULL, FOREIGN KEY (classroom) REFERENCES "classrooms" (id) ON DELETE CASCADE)`,
+
+		// Create table with spaces in name to test quoting
+		`CREATE TABLE "test space table" (id INTEGER PRIMARY KEY, name TEXT NOT NULL)`,
+
+		// Create table with escaped quotes and SQL reserved keywords
+		`CREATE TABLE "test""name" (id INTEGER PRIMARY KEY, "escaped""column" TEXT, "order" INTEGER)`,
+	];
+
+	await db.exec(schemas.join(";"));
+
+	// Prepare sample data
+	const classroomData = [
+		// Standard numeric data
+		...Array.from({ length: 10 }, (_, i) => ({
+			id: `classroom_${i + 1}`,
+			capacity: (i + 1) * 10,
+			test_blob: utf8Encode(`Blob data for classroom ${i + 1}`),
+		})),
+
+		// Edge case: type mismatch (string where number expected)
+		{ id: "different_type_classroom", capacity: "not_a_number" },
+
+		// Edge case: NULL value
+		{ id: "null_classroom", capacity: null },
+	];
+
+	// Insert classroom data
+	const classroomStmt = db.prepare(
+		`INSERT INTO classrooms (id, capacity) VALUES (?, ?)`
+	);
+
+	for (const classroom of classroomData) {
+		await classroomStmt.bind(classroom.id, classroom.capacity).run();
+	}
+
+	// Generate and insert student data with classroom references
+	const studentStmt = db.prepare(
+		`INSERT INTO students (id, name, classroom) VALUES (?, ?, ?)`
+	);
+
+	// Create 2 students for each classroom
+	for (let i = 0; i < 10; i++) {
+		for (let j = 1; j <= 2; j++) {
+			const studentId = i * 2 + j;
+			await studentStmt
+				.bind(studentId, `student_${studentId}`, `classroom_${i + 1}`)
+				.run();
+		}
+	}
+}
+
+/**
+ * Compares two D1 databases to check if they are equal in terms of schema and data.
+ * It retrieves the schema of both databases, compares the tables, and then
+ * checks if the data in each table is identical.
+ */
+async function isDatabaseEqual(
+	t: ExecutionContext<Context>,
+	db: D1Database,
+	db2: D1Database
+) {
+	// SQL to select schema excluding internal tables
+	const selectSchemaSQL =
+		"SELECT * FROM sqlite_master WHERE type = 'table' AND (name NOT LIKE 'sqlite_%' AND name NOT LIKE '_cf_%')";
+
+	// Check if schema (tables) in both databases is equal
+	const tablesFromMirror = (await db2.prepare(selectSchemaSQL).all()).results;
+	const tablesFromOriginal = (await db.prepare(selectSchemaSQL).all()).results;
+	t.deepEqual(tablesFromMirror, tablesFromOriginal);
+
+	// Check if data in each table is equal
+	// We will use a simple SELECT * FROM table ORDER BY id to ensure consistent ordering
+	for (const table of tablesFromMirror) {
+		const tableName = table.name as string;
+
+		// Escape and ORDER BY to ensure consistent ordering
+		const selectTableSQL = `SELECT * FROM "${tableName.replace(/"/g, '""')}" ORDER BY id ASC`;
+
+		const originalData = (await db.prepare(selectTableSQL).all()).results;
+		const mirrorData = (await db2.prepare(selectTableSQL).all()).results;
+
+		t.deepEqual(
+			originalData,
+			mirrorData,
+			`Data mismatch in table: ${tableName}`
+		);
+	}
+}
diff --git a/packages/wrangler/src/__tests__/d1/export.test.ts b/packages/wrangler/src/__tests__/d1/export.test.ts
index 334bd2df6ec6..01364e8cd78a 100644
--- a/packages/wrangler/src/__tests__/d1/export.test.ts
+++ b/packages/wrangler/src/__tests__/d1/export.test.ts
@@ -59,14 +59,14 @@ describe("export", () => {
 		const create_foo = "CREATE TABLE foo(id INTEGER PRIMARY KEY, value TEXT);";
 		const create_bar = "CREATE TABLE bar(id INTEGER PRIMARY KEY, value TEXT);";
 		const insert_foo = [
-			"INSERT INTO foo VALUES(1,'xxx');",
-			"INSERT INTO foo VALUES(2,'yyy');",
-			"INSERT INTO foo VALUES(3,'zzz');",
+			`INSERT INTO "foo" VALUES(1,'xxx');`,
+			`INSERT INTO "foo" VALUES(2,'yyy');`,
+			`INSERT INTO "foo" VALUES(3,'zzz');`,
 		];
 		const insert_bar = [
-			"INSERT INTO bar VALUES(1,'aaa');",
-			"INSERT INTO bar VALUES(2,'bbb');",
-			"INSERT INTO bar VALUES(3,'ccc');",
+			`INSERT INTO "bar" VALUES(1,'aaa');`,
+			`INSERT INTO "bar" VALUES(2,'bbb');`,
+			`INSERT INTO "bar" VALUES(3,'ccc');`,
 		];
 
 		// Full export