Files
rea/src/sqlite/SqliteDatabaseWorker.ts
T
Kaoru0822 bcfcd8b6e2 fix(sqlite): attribute generated-column evaluation failures to the selection (#5879)
* fix(sqlite): attribute generated-column evaluation failures to the selection

A selected virtual generated column whose expression needs a function the
read-only connection lacks (or raises an SQL error for a sampled row) was
classified by the worker's catch-all as a malformed database and projected as
an invalid_format issue on `path`.

Row reads now attribute SQLITE_ERROR from selected cells by probing each
selected virtual generated column over the sampled rows, and report a
typed capability_unavailable refusal naming the table, each failing column
with SQLite's message, and the remaining columns to select. Other causes,
including corruption, TOOBIG and NOMEM, keep their classification.

Fixes #5719

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(sqlite): recommend only probed-readable columns after evaluation failures

Record every per-column probe failure with its own reason, discover stored
columns outside an inventory-scope selection without decoding unrelated
malformed names, and present recovery as the structured columns selection
instead of shell-quoted command text built from database-supplied names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 04:08:49 -07:00

68 lines
2.1 KiB
TypeScript

import { readFile, writeFile } from "node:fs/promises";
import { z } from "zod";
import { inspectSqliteDatabaseInputSchema } from "../domain/sqlite/sqliteDatabase.js";
import { inspectSqliteDatabaseSnapshot } from "./SqliteDatabaseInspection.js";
import {
SQLITE_DATABASE_LIMITS,
SqliteColumnEvaluationFailure,
SqliteInspectionFailure,
sqlitePrimaryResultCode,
} from "./SqliteDatabaseLimits.js";
const requestSchema = z.strictObject({
snapshot_path: z.string(),
reply_path: z.string(),
input: inspectSqliteDatabaseInputSchema,
});
const main = async (): Promise<void> => {
const requestPath = process.argv[2];
if (requestPath === undefined)
throw new Error("Owned SQLite worker request path missing.");
const request = requestSchema.parse(
JSON.parse(await readFile(requestPath, "utf8")),
);
let reply: unknown;
try {
reply = {
ok: true,
inspection: inspectSqliteDatabaseSnapshot(
request.snapshot_path,
request.input,
),
};
} catch (cause: unknown) {
const code = sqlitePrimaryResultCode(cause);
const message = cause instanceof Error ? cause.message : String(cause);
reply =
cause instanceof SqliteColumnEvaluationFailure
? {
ok: false,
reason: cause.reason,
message,
readable_columns: cause.readableColumns,
}
: {
ok: false,
reason:
cause instanceof SqliteInspectionFailure
? cause.reason
: code === 7
? "memory"
: code === 18
? "output-limit"
: "format",
message,
};
}
let encoded = JSON.stringify(reply);
if (Buffer.byteLength(encoded) > SQLITE_DATABASE_LIMITS.outputBytes)
encoded = JSON.stringify({
ok: false,
reason: "output-limit",
message:
"Complete SQLite evidence exceeds the 16 MiB JSON reply budget; no partial success was returned.",
});
await writeFile(request.reply_path, encoded, { flag: "wx", mode: 0o600 });
};
await main();