fix(tdengine): display timezone timestamps and tags correctly

This commit is contained in:
zipg authored and GitHub committed 2026-08-22 19:17:05 +08:00
1 parent 2fee435b33
commit 36c9500a4e
9 files changed
+177 -40

No files matched your search

@@ -5577,6 +5577,7 @@ function cellDetailFor(rowIndex: number, columnIndex: number): DataGridCellDetai
canEditCell: canEditCellItem(item, columnIndex),
isDraft: !!item.isDraft,
}),
databaseType: resolvedDatabaseType.value,
isValuePreviewTruncated: isLargeValuePreview(item, columnIndex),
});
}
@@ -5706,7 +5707,7 @@ const activeCellDetailTabs = computed(() => {
const activeBinaryHexBytes = computed(() => {
if (activeCellDetailTab.value !== "hexViewer") return null;
const detail = activeCellDetail.value;
return detail ? parseBinaryCellBytes(detail.value, detail.type) : null;
return detail ? parseBinaryCellBytes(detail.value, detail.type, resolvedDatabaseType.value) : null;
});
const activeBinaryHexRows = computed(() => (activeBinaryHexBytes.value ? buildBinaryHexViewRows(activeBinaryHexBytes.value) : []));
@@ -6326,7 +6327,7 @@ function formatCell(value: CellValue, columnIndex?: number, originalBytes?: numb
columnInfo: displayColumnInfo,
});
if (arrayDisplay !== undefined) return arrayDisplay;
const binaryDisplay = formatter ? null : binaryCellDisplayText(value, columnIndex === undefined ? undefined : allColumnTypes.value[columnIndex], originalBytes);
const binaryDisplay = formatter ? null : binaryCellDisplayText(value, columnIndex === undefined ? undefined : allColumnTypes.value[columnIndex], originalBytes, resolvedDatabaseType.value);
if (binaryDisplay !== null) return binaryDisplay;
const s = applyColumnFormatter(value, formatter);
return limitDisplay ? limitDataGridCellDisplay(s, resolvedDatabaseType.value === "sqlserver" ? SQLSERVER_DATA_GRID_CELL_DISPLAY_MAX_LENGTH : undefined) : s;
@@ -8922,7 +8923,7 @@ function copyDetailColumnName() {
function canDownloadDetailBinaryValue(detail: DataGridCellDetail | null): boolean {
if (!detail) return false;
const item = getRowItem(detail.rowId);
return (isLargeValuePreview(item, detail.colIndex) && isBinaryCellColumnType(detail.type)) || canDownloadBinaryCellValue(detail.value, detail.type);
return (resolvedDatabaseType.value !== "tdengine" && isLargeValuePreview(item, detail.colIndex) && isBinaryCellColumnType(detail.type)) || canDownloadBinaryCellValue(detail.value, detail.type, resolvedDatabaseType.value);
}
function canImportDetailBinaryValue(detail: DataGridCellDetail | null): boolean {
@@ -8972,7 +8973,7 @@ async function downloadDetailBinaryValue(detail: DataGridCellDetail | null, mode
if (!(await hydrateLargeValueCell(detail.rowId, detail.colIndex))) return;
const resolvedDetail = cellDetailFor(detail.rowNumber - 1, detail.colIndex);
if (!resolvedDetail) return;
const payload = binaryCellDownloadPayload(resolvedDetail.value, mode, resolvedDetail.type);
const payload = binaryCellDownloadPayload(resolvedDetail.value, mode, resolvedDetail.type, resolvedDatabaseType.value);
const fileName = binaryCellDownloadFileName({
column: resolvedDetail.column,
rowNumber: resolvedDetail.rowNumber,
@@ -159,7 +159,7 @@ function bytesFromBufferLikeObject(value: unknown): Uint8Array | null {
return bytesFromByteArray(data);
}
export function parseBinaryCellBytes(value: unknown, columnType?: string): Uint8Array | null {
export function parseBinaryCellBytes(value: unknown, columnType?: string, databaseType?: DatabaseType): Uint8Array | null {
if (typeof value === "string") {
const prefixed = parseBinaryCellHexValue(value);
if (prefixed) return prefixed;
@@ -169,7 +169,7 @@ export function parseBinaryCellBytes(value: unknown, columnType?: string): Uint8
return bytesFromHex(trimmed.replace(/\\x/gi, ""));
}
if (isBinaryCellColumnType(columnType) && BARE_HEX_RE.test(trimmed)) {
if (databaseType !== "tdengine" && isBinaryCellColumnType(columnType) && BARE_HEX_RE.test(trimmed)) {
return bytesFromHex(trimmed);
}
}
@@ -189,16 +189,16 @@ export function canImportBinaryCellFile(databaseType?: DatabaseType, columnType?
return false;
}
export function canDownloadBinaryCellValue(value: unknown, columnType?: string): boolean {
return !!parseBinaryCellBytes(value, columnType);
export function canDownloadBinaryCellValue(value: unknown, columnType?: string, databaseType?: DatabaseType): boolean {
return !!parseBinaryCellBytes(value, columnType, databaseType);
}
export function binaryCellDisplayText(value: unknown, columnType?: string, originalBytes?: number): string | null {
export function binaryCellDisplayText(value: unknown, columnType?: string, originalBytes?: number, databaseType?: DatabaseType): string | null {
if (typeof value === "string" && isBinaryCellColumnType(columnType) && TRUNCATED_HEX_VALUE_RE.test(value.trim())) {
const size = originalBytes === undefined ? "..." : formatBinaryCellByteSize(originalBytes);
return `${binaryCellDisplayLabel(columnType)} [${size}]`;
}
const bytes = parseBinaryCellBytes(value, columnType);
const bytes = parseBinaryCellBytes(value, columnType, databaseType);
if (!bytes || !isBinaryCellColumnType(columnType)) return null;
if (BINARY_STRING_TYPE_RE.test((columnType ?? "").trim())) {
const previewBytes = FIXED_BINARY_TYPE_RE.test((columnType ?? "").trim()) ? trimTrailingNullBytes(bytes) : bytes;
@@ -245,8 +245,8 @@ export function formatBinaryCellByteSize(bytes: number): string {
return `${(bytes / (1024 * 1024)).toFixed(bytes < 10 * 1024 * 1024 ? 1 : 0)} MB`;
}
export function binaryCellDownloadPayload(value: unknown, mode: BinaryCellDownloadMode, columnType?: string): BinaryCellDownloadPayload {
const bytes = parseBinaryCellBytes(value, columnType);
export function binaryCellDownloadPayload(value: unknown, mode: BinaryCellDownloadMode, columnType?: string, databaseType?: DatabaseType): BinaryCellDownloadPayload {
const bytes = parseBinaryCellBytes(value, columnType, databaseType);
if (!bytes) {
throw new Error("Cell value is not a downloadable binary value.");
}
+10 -6
View File
@@ -1,5 +1,6 @@
import type { CellValue } from "@/lib/dataGrid/cellValue";
import { isBinaryCellColumnType, parseBinaryCellBytes } from "@/lib/dataGrid/binaryCellDownload";
import type { DatabaseType } from "@/types/database";
const IMAGE_PATH_RE = /\.(?:png|jpe?g|webp|gif|avif|bmp|svg)$/i;
const SAFE_DATA_IMAGE_RE = /^data:image\/(?:png|jpe?g|webp|gif|avif|bmp);base64,[a-z0-9+/=\s]+$/i;
@@ -14,10 +15,11 @@ function isLocalHttpHost(hostname: string): boolean {
export interface CellImagePreviewOptions {
binary?: boolean;
databaseType?: DatabaseType;
}
export function cellImagePreviewUrl(value: CellValue | unknown, columnType?: string, options: CellImagePreviewOptions = {}): string | null {
const binaryPreviewUrl = options.binary === false ? null : binaryImagePreviewUrl(value, columnType);
const binaryPreviewUrl = options.binary === false ? null : binaryImagePreviewUrl(value, columnType, options.databaseType);
if (binaryPreviewUrl) return binaryPreviewUrl;
if (typeof value !== "string") return null;
@@ -38,16 +40,16 @@ export function cellImagePreviewUrl(value: CellValue | unknown, columnType?: str
return text;
}
function binaryImagePreviewUrl(value: unknown, columnType?: string): string | null {
if (estimatedBinaryByteLength(value, columnType) > MAX_BINARY_IMAGE_PREVIEW_BYTES) return null;
const bytes = parseBinaryCellBytes(value, columnType);
function binaryImagePreviewUrl(value: unknown, columnType?: string, databaseType?: DatabaseType): string | null {
if (estimatedBinaryByteLength(value, columnType, databaseType) > MAX_BINARY_IMAGE_PREVIEW_BYTES) return null;
const bytes = parseBinaryCellBytes(value, columnType, databaseType);
if (!bytes || bytes.length > MAX_BINARY_IMAGE_PREVIEW_BYTES) return null;
const mimeType = detectImageMimeType(bytes);
if (!mimeType) return null;
return `data:${mimeType};base64,${bytesToBase64(bytes)}`;
}
function estimatedBinaryByteLength(value: unknown, columnType?: string): number {
function estimatedBinaryByteLength(value: unknown, columnType?: string, databaseType?: DatabaseType): number {
if (Array.isArray(value)) return value.length;
if (value && typeof value === "object" && Array.isArray((value as { data?: unknown }).data)) {
return (value as { data: unknown[] }).data.length;
@@ -58,7 +60,9 @@ function estimatedBinaryByteLength(value: unknown, columnType?: string): number
const prefixed = trimmed.match(HEX_PREFIX_RE);
if (prefixed) return prefixed[1].replace(/\s+/g, "").length / 2;
if (HEX_ESCAPE_RE.test(trimmed)) return trimmed.replace(/\s+/g, "").replace(/\\x/gi, "").length / 2;
if (isBinaryCellColumnType(columnType) && BARE_HEX_RE.test(trimmed)) return trimmed.replace(/\s+/g, "").length / 2;
if (databaseType !== "tdengine" && isBinaryCellColumnType(columnType) && BARE_HEX_RE.test(trimmed)) {
return trimmed.replace(/\s+/g, "").length / 2;
}
return 0;
}
@@ -1,6 +1,7 @@
import { cellImagePreviewUrl } from "@/lib/dataGrid/cellImageUrl";
import { displayCellValue, type CellValue } from "@/lib/dataGrid/cellValue";
import { formatJsonText } from "@/lib/dataGrid/cellDetailPresentation";
import type { DatabaseType } from "@/types/database";
export const CELL_DETAIL_VALUE_PREVIEW_MAX_LENGTH = 12_000;
@@ -48,6 +49,7 @@ export interface BuildDataGridCellDetailOptions {
commentByColumn?: ReadonlyMap<string, string>;
displayValue: (value: CellValue, columnIndex: number) => string;
isEditable: boolean;
databaseType?: DatabaseType;
includeBinaryImagePreview?: boolean;
isValuePreviewTruncated?: boolean;
}
@@ -109,7 +111,10 @@ export function buildDataGridCellDetail(options: BuildDataGridCellDetailOptions)
displayValue,
displayValuePreview,
isValuePreviewTruncated: options.isValuePreviewTruncated === true || rawValuePreview.length < rawValue.length || displayValuePreview.length < displayValue.length,
imagePreviewUrl: cellImagePreviewUrl(value, type, { binary: options.includeBinaryImagePreview !== false }),
imagePreviewUrl: cellImagePreviewUrl(value, type, {
binary: options.includeBinaryImagePreview !== false,
databaseType: options.databaseType,
}),
length: value === null ? 0 : String(value).length,
formattedJson,
isEditable: options.isEditable,