fix(grid): copy text-like oracle blobs as decoded text

This commit is contained in:
ordinary being
2026-10-19 13:30:18 +08:00
committed by GitHub
parent 9944e1e04c
commit f3b163a29e
3 changed files with 30 additions and 6 deletions
@@ -2966,4 +2966,15 @@ describe("useDataGridExport MySQL BLOB 文本复制 (#12413)", () => {
expect(extractDataGridSelection).toHaveBeenLastCalledWith(expect.objectContaining({ extractor: "raw", rows: [["0x616263"]] }));
});
it("copies text-like Oracle-family blobs as text, matching the grid preview", async () => {
const matrix: CellSelectionMatrix = { rowIndexes: [0], columnIndexes: [1], columns: ["secret"], rows: [["0x616263"]] };
vi.mocked(extractDataGridSelection).mockResolvedValueOnce({ text: "abc", mimeType: "text/plain", fileExtension: "txt", rowCount: 1, columnCount: 1 });
const state = createExportState(blobTable, ["id", "secret"], matrix, [1, "0x616263"], undefined, undefined, [], DEFAULT_DATA_GRID_EXTRACTOR_OPTIONS, false, undefined, false, 1, 1, "oracle");
await expect(state.copyWithPreference("smart")).resolves.toBe(true);
expect(extractDataGridSelection).toHaveBeenCalledWith(expect.objectContaining({ extractor: "raw", rows: [["abc"]] }));
expect(copyToClipboard).toHaveBeenCalledWith("abc");
});
});
@@ -291,13 +291,17 @@ export function binaryCellTextPreview(value: unknown, encoding: BinaryCellTextEn
// VARBINARY,避免改变 MySQL BINARY/BLOB、SQL Server VARBINARY、PostgreSQL BYTEA、
// Oracle RAW 等现有语义。
export function binaryCellClipboardText(value: unknown, columnType?: string, databaseType?: DatabaseType): string | null {
// 覆盖上方注释里「严格限于 VARBINARY」的旧范围(#12413):网格已经对文本型 MySQL
// BLOB 显示文本预览(AES_DECRYPT 这类表达式的结果列常报成 BLOB),显示 `secret`
// 却复制出 hex 与预览自相矛盾,因此 BLOB 家族并入同一闸门;严格往返校验与 hex
// 回退保持不变,MySQL BINARY、非 MySQL 的 blob/bytea 等现有语义不受影响。
// 覆盖上方注释里「严格限于 VARBINARY」的旧范围(#12413):网格已经对文本型 BLOB
// 显示文本预览(MySQL 与 Oracle 家族;AES_DECRYPT 这类表达式的结果列常报成 BLOB),
// 显示 `secret` 却复制出 hex 与预览自相矛盾,因此并入同一闸门;严格往返校验与 hex
// 回退保持不变,MySQL BINARY、Oracle RAW、非预览库的 blob/bytea 等现有语义不受影响。
const blobColumn = isBlobCellColumnType(columnType);
const decodableType = VARBINARY_TYPE_RE.test((columnType ?? "").trim()) || blobColumn;
if (databaseType !== "mysql" || !decodableType) return null;
// Blob columns follow the grid's inline text-preview gate: MySQL and the
// Oracle family preview text-like blobs as text, so copying hex from such a
// cell contradicts what the cell shows (#12413). MySQL VARBINARY keeps its
// #7471 contract; every other column stays lossless hex.
const decodableType = (databaseType === "mysql" && VARBINARY_TYPE_RE.test((columnType ?? "").trim())) || (blobColumn && isBlobTextPreviewDatabase(databaseType));
if (!decodableType) return null;
// MySQL QueryResult 的 binary canonical form 是 `0x<hex>`;不要把普通的偶数长度
// 文本(例如 "abcd")猜测成 hex bytes。
const bytes = typeof value === "string" ? parseBinaryCellHexValue(value) : null;
+9
View File
@@ -240,6 +240,15 @@ test("binaryCellClipboardText decodes textual MySQL varbinary and preserves arbi
assert.equal(binaryCellClipboardText("0x616263", "BLOB", "mysql"), "abc");
assert.equal(binaryCellClipboardText("0x616263", "LONGBLOB", "mysql"), "abc");
assert.equal(binaryCellClipboardText("0xd6d0cec4", "LONGBLOB", "mysql"), null); // BLOB 不回退 GBK(与显示一致)
// Oracle 家族 BLOB 与内联显示预览共用同一闸门:文本型 BLOB 显示为文本,复制也必须
// 还原成同一份文本;只解严格 UTF-8(不回退 GBK),不可解码的 bytes 仍为 hex,
// 没有文本预览的库(如 sqlite)与 Oracle RAW 不受影响。
assert.equal(binaryCellClipboardText("0x616263", "BLOB", "oracle"), "abc");
assert.equal(binaryCellClipboardText("0x616263", "BLOB", "oceanbase-oracle"), "abc");
assert.equal(binaryCellClipboardText("0xd6d0cec4", "BLOB", "oracle"), null);
assert.equal(binaryCellClipboardText("0x00fffe", "BLOB", "oracle"), null);
assert.equal(binaryCellClipboardText("0x616263", "BLOB", "sqlite"), null);
assert.equal(binaryCellClipboardText("0x616263", "RAW", "oracle"), null);
assert.equal(binaryCellClipboardText("0x616263", "BINARY(8)", "mysql"), null); // MySQL BINARY 不跟随
assert.equal(binaryCellClipboardText("0x616263", "VARBINARY(128)", "sqlserver"), null); // SQL Server varbinary 不改
assert.equal(binaryCellClipboardText("0x616263", "bytea", "postgres"), null); // Postgres bytea 不改