Skip to content

Commit 235d325

Browse files
authored
fix(wrangler): prevent SQLite users error from being reported to Sentry (#11467)
* fix(wrangler): prevent SQLite users error from being reported to Sentry * add SQLITE_AUTH to common error codes
1 parent 78480d7 commit 235d325

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

.changeset/gold-donuts-leave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
fix: prevent reporting SQLite error from `wrangler d1 execute` to Sentry

packages/wrangler/src/__tests__/d1/execute.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fs from "node:fs";
22
import { join } from "node:path";
3+
import { UserError } from "@cloudflare/workers-utils";
34
import { http, HttpResponse } from "msw";
45
import { afterEach, beforeEach, describe, expect, it } from "vitest";
56
import { mockAccountId, mockApiToken } from "../helpers/mock-account-id";
@@ -161,6 +162,31 @@ describe("execute", () => {
161162
`);
162163
});
163164

165+
it("should treat SQLite constraint errors as UserErrors", async () => {
166+
setIsTTY(false);
167+
writeWranglerConfig({
168+
d1_databases: [
169+
{ binding: "DATABASE", database_name: "db", database_id: "xxxx" },
170+
],
171+
});
172+
173+
// First create a table with a foreign key constraint
174+
const setupSQL = `
175+
CREATE TABLE users (id INTEGER PRIMARY KEY);
176+
CREATE TABLE posts (id INTEGER PRIMARY KEY, user_id INTEGER, FOREIGN KEY(user_id) REFERENCES users(id));
177+
`;
178+
fs.writeFileSync("setup.sql", setupSQL);
179+
await runWrangler("d1 execute db --file setup.sql --local");
180+
181+
// Now try to violate the foreign key constraint
182+
const violationSQL = `INSERT INTO posts (id, user_id) VALUES (1, 999);`;
183+
fs.writeFileSync("violation.sql", violationSQL);
184+
185+
await expect(
186+
runWrangler("d1 execute db --file violation.sql --local")
187+
).rejects.toThrow(UserError);
188+
});
189+
164190
it("should show banner by default", async () => {
165191
setIsTTY(false);
166192
writeWranglerConfig({

packages/wrangler/src/d1/execute.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,31 @@ export type QueryResult = {
4141
query?: string;
4242
};
4343

44+
// Common SQLite Codes
45+
// See https://www.sqlite.org/rescode.html
46+
const SQLITE_RESULT_CODES = [
47+
"SQLITE_ERROR",
48+
"SQLITE_CONSTRAINT",
49+
"SQLITE_MISMATCH",
50+
"SQLITE_AUTH",
51+
];
52+
53+
function isSqliteUserError(error: unknown): error is Error {
54+
if (!(error instanceof Error)) {
55+
return false;
56+
}
57+
58+
const message = error.message.toUpperCase();
59+
60+
for (const code of SQLITE_RESULT_CODES) {
61+
if (message.includes(code)) {
62+
return true;
63+
}
64+
}
65+
66+
return false;
67+
}
68+
4469
export const d1ExecuteCommand = createCommand({
4570
metadata: {
4671
description: "Execute a command or SQL file",
@@ -313,7 +338,13 @@ async function executeLocally({
313338
try {
314339
results = await db.batch(queries.map((query) => db.prepare(query)));
315340
} catch (e: unknown) {
316-
throw (e as { cause?: unknown })?.cause ?? e;
341+
const cause = (e as { cause?: unknown })?.cause ?? e;
342+
343+
if (isSqliteUserError(cause)) {
344+
throw new UserError(cause.message);
345+
}
346+
347+
throw cause;
317348
} finally {
318349
await mf.dispose();
319350
}

0 commit comments

Comments
 (0)