From 63ea5f1fd88aa284c7730e73afd0184a25dd0f65 Mon Sep 17 00:00:00 2001 From: LRcoding <38545827+LwClick@users.noreply.github.com> Date: Fri, 17 Jul 2026 00:10:46 +0800 Subject: [PATCH] feat(table-info): show column default values --- apps/desktop/src/components/grid/DataGrid.vue | 5 +++ .../src/components/objects/ObjectBrowser.vue | 5 +++ .../table/tableColumnDefaultPresentation.ts | 4 +++ .../app-tests/tableInfoDefaultValue.test.ts | 31 +++++++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 apps/desktop/src/lib/table/tableColumnDefaultPresentation.ts create mode 100644 packages/app-tests/tableInfoDefaultValue.test.ts diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index ecdb633e64..c68b1d9848 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -62,6 +62,7 @@ import TemporalCellEditor from "@/components/grid/TemporalCellEditor.vue"; import EnumCellEditor from "@/components/grid/EnumCellEditor.vue"; import type { QueryResult, ColumnInfo, DatabaseType, ForeignKeyInfo, IndexInfo, TriggerInfo, TableInfoTab } from "@/types/database"; import { tableObjectSourceKind } from "@/lib/table/tableObjectSourceKind"; +import { tableColumnDefaultDisplayValue } from "@/lib/table/tableColumnDefaultPresentation"; import * as api from "@/lib/backend/api"; import { formatElapsedSeconds } from "@/lib/common/elapsedTime"; import { dataGridCellDisplayText, dataGridCellEditorText } from "@/lib/dataGrid/dataGridCellCoercion"; @@ -8477,6 +8478,7 @@ const gridContextMenuItems = computed(() => { {{ t("grid.columnName") }} {{ t("grid.columnType") }} {{ t("grid.tableInfoNullable") }} + {{ t("structureEditor.defaultValue") }} @@ -8503,6 +8505,9 @@ const gridContextMenuItems = computed(() => { {{ column.data_type }} {{ column.is_nullable ? "YES" : "NO" }} + + {{ tableColumnDefaultDisplayValue(column.column_default) }} + diff --git a/apps/desktop/src/components/objects/ObjectBrowser.vue b/apps/desktop/src/components/objects/ObjectBrowser.vue index dd612b24a8..40a145a794 100644 --- a/apps/desktop/src/components/objects/ObjectBrowser.vue +++ b/apps/desktop/src/components/objects/ObjectBrowser.vue @@ -96,6 +96,7 @@ import { import { resolveRowClickAction, shouldDeferSingleClick, type ObjectBrowserRowAction } from "@/lib/table/objectBrowserRowAction"; import { createSidePanelRequestGuard } from "@/lib/table/sidePanelRequestGuard"; import { runBatchTableTruncate } from "@/lib/table/batchTableTruncate"; +import { tableColumnDefaultDisplayValue } from "@/lib/table/tableColumnDefaultPresentation"; type ObjectFilter = "all" | "tables" | "views" | "materializedViews" | "procedures" | "functions" | "sequences" | "packages"; type ObjectBrowserColumnKey = "select" | "name" | "type" | "estimatedRows" | "totalBytes" | "created_at" | "updated_at" | "comment"; @@ -2726,6 +2727,7 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] { {{ t("grid.columnName") }} {{ t("grid.columnType") }} {{ t("grid.tableInfoNullable") }} + {{ t("structureEditor.defaultValue") }} @@ -2742,6 +2744,9 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] { {{ column.data_type }} {{ column.is_nullable ? "YES" : "NO" }} + + {{ tableColumnDefaultDisplayValue(column.column_default) }} + diff --git a/apps/desktop/src/lib/table/tableColumnDefaultPresentation.ts b/apps/desktop/src/lib/table/tableColumnDefaultPresentation.ts new file mode 100644 index 0000000000..6374ae7263 --- /dev/null +++ b/apps/desktop/src/lib/table/tableColumnDefaultPresentation.ts @@ -0,0 +1,4 @@ +/** Preserve database-specific default expressions exactly; only absent metadata gets a placeholder. */ +export function tableColumnDefaultDisplayValue(value: string | null | undefined): string { + return value ?? "—"; +} diff --git a/packages/app-tests/tableInfoDefaultValue.test.ts b/packages/app-tests/tableInfoDefaultValue.test.ts new file mode 100644 index 0000000000..11eea598ea --- /dev/null +++ b/packages/app-tests/tableInfoDefaultValue.test.ts @@ -0,0 +1,31 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { test } from "vitest"; +import { compileTemplate, parse } from "vue/compiler-sfc"; +import { tableColumnDefaultDisplayValue } from "@/lib/table/tableColumnDefaultPresentation"; + +const tableInfoSurfaces = ["apps/desktop/src/components/objects/ObjectBrowser.vue", "apps/desktop/src/components/grid/DataGrid.vue"]; + +test("table column defaults preserve raw database expressions", () => { + const values = [null, "''", "0", "CURRENT_TIMESTAMP", "((1))", "('prefix (internal)')", "x".repeat(512)] as const; + + assert.deepEqual(values.map(tableColumnDefaultDisplayValue), ["—", ...values.slice(1)]); + assert.equal(tableColumnDefaultDisplayValue(undefined), "—"); + assert.equal(tableColumnDefaultDisplayValue(""), ""); +}); + +test("both table information surfaces expose the same default-value column", () => { + for (const path of tableInfoSurfaces) { + const source = readFileSync(path, "utf8"); + const { descriptor, errors } = parse(source, { filename: path }); + assert.deepEqual(errors, []); + assert.ok(descriptor.template); + const result = compileTemplate({ id: path, filename: path, source: descriptor.template.content }); + assert.deepEqual(result.errors, []); + + assert.match(descriptor.template.content, /t\("structureEditor\.defaultValue"\)/); + assert.match(descriptor.template.content, /data-table-info-column-default/); + assert.match(descriptor.template.content, /:title="column\.column_default \?\? undefined"/); + assert.match(descriptor.template.content, /tableColumnDefaultDisplayValue\(column\.column_default\)/); + } +});