Skip to content

feat(datagrid): show a table's properties and let its comment be edited - #2578

Open
datlechin wants to merge 1 commit into
mainfrom
feat/table-properties-tab
Open

feat(datagrid): show a table's properties and let its comment be edited#2578
datlechin wants to merge 1 commit into
mainfrom
feat/table-properties-tab

Conversation

@datlechin

Copy link
Copy Markdown
Member

Closes #2555.

What was missing

There was no surface for a table's own properties, and no way to edit a table comment anywhere in the app, on any engine. The right-sidebar inspector showed a read-only subset (size, rows, engine, collation, timestamps, comment) and only while no row was selected. The reporter stores a JSON payload in a table comment and needed to read and edit it without writing COMMENT ON.

What this adds

A Properties tab, first in the structure editor's sub-tab picker.

  • Read, on every engine. fetchTableMetadata through TableStructureLoader, so the tab's own session owns the data rather than the coordinator's latest-wins inspector slot. PluginTableMetadata gains attributes: [PluginObjectAttribute], which lets each driver name its own vocabulary without the app modelling any of it: PostgreSQL sends Owner, Tablespace, Persistence and a Kind for anything that is not an ordinary table; MySQL sends Row Format, Auto Increment and Create Options.
  • Edit, on MySQL, MariaDB, PostgreSQL and PGlite. The comment is the only editable field. It stages as SchemaChange.modifyTableComment into the same StructureChangeManager the Columns grid writes to, so it inherits Safe Mode, the authorization gate, the transaction, query history, undo and redo, and the unsaved-changes prompt on close. The editor is the existing TextValueEditor: a scrolling NSTextView with AppKit's quote and dash substitutions off, which is the answer to "how do I open the comment bigger".

Writability is decided twice, and both gates have to agree. DriverPlugin.supportsTableComment (default false) says whether the engine has table comments at all. PluginTableMetadata.commentIsReadOnly (default true) says whether this relation takes one: PostgreSQL lowers it from pg_class.relkind, MySQL from SHOW TABLE STATUS naming no engine, which is how it reports a view. A driver that cannot establish the relation kind offers no edit, so a materialized view or a foreign table never reaches a COMMENT ON TABLE that its engine would refuse.

The PostgreSQL literal is dollar-quoted rather than '...'. A comment is arbitrary user text, the driver's shared escape helper doubles apostrophes but leaves backslashes, and with standard_conforming_strings off PostgreSQL reads backslash escapes inside an ordinary literal, so a backslash in front of a doubled apostrophe closes the literal early and the rest of the comment parses as SQL. A dollar-quoted body is not scanned for escapes under either setting.

One undo step per editing episode, not per keystroke: the field writes on every character, and a per-character entry would fill all 100 levels of undo from one paragraph and evict the column edits staged beside it. Replaying an undo or redo always registers, so Cmd+Shift+Z puts the comment back.

MySQLTableStatus also fills in avgRowLength, collation, createTime and updateTime, which SHOW TABLE STATUS had always returned and the driver had never read.

PluginKit ABI

Additive, no currentPluginKitVersion bump, and no registry plugin binary changes at all: the two engines that gain comment editing are both bundled.

  • PluginTableMetadata.attributes and commentIsReadOnly arrive through a new init overload; the original keeps its exact signature and is marked @_disfavoredOverload, the same shape PluginTableInfo already uses. Adding a parameter to the existing init would have replaced its mangled symbol and broken every shipped plugin.
  • The decoder is written out rather than synthesized. Measured: Swift's synthesized Decodable does not fall back to an initializer's default value, so a payload written before these fields existed throws keyNotFound.
  • Both compatibility paths, the old initializer and a decode with the keys absent, report commentIsReadOnly = true, so an already-built plugin presents its comment read-only rather than being assumed writable.
  • generateSetTableCommentSQL and supportsTableComment are protocol requirements with default implementations.

Scope and non-goals

  • Six engines read but do not write. ClickHouse, Oracle, SQL Server, Snowflake, Trino and Dameng show their comment read-only. Each needs its own relation-kind check before it can be trusted to pick the right COMMENT ON keyword (Oracle reads materialized-view container rows through ALL_TABLES; Trino's grammar has no materialized-view form), and ClickHouse's MODIFY COMMENT needs 23.9 or later, which the app does not currently require. They are also all registry-only, so enabling them means publishing five plugin artifacts in lockstep with the app.
  • Renaming the table stays in the sidebar. Mixing an immediate rename into a form whose other field is staged would apply half a screen at a time.
  • Redshift and CockroachDB are PostgreSQL variants whose driver subclasses LibPQBackedDriver rather than PostgreSQLPluginDriver, so they inherit no comment generator and stay read-only.

Verified

Every step through verify.sh, plus an end-to-end check against a local PostgreSQL 17.

Step Result
build PASS
test (the suites owning the changed types) PASS
build MySQLDriver, build PostgreSQLDriver PASS
plugins (AllPlugins) INCONCLUSIVE locally: oracle-nio fails with unknown attribute 'usableFromInlinenonisolated' from its own @TaskLocal macro, which blocks the aggregate on this toolchain for any change under Plugins/. CI runs it.
lint PASS, 0 violations
docs PASS
abi recorded below

End to end, driving a Debug build against postgresql://127.0.0.1:5432/postgres:

  • typed a new comment, Cmd+S, then select obj_description('public.orders'::regclass, 'pg_class') returned the typed text;
  • cleared the field, Cmd+S, and the same query returned NULL, so the IS NULL branch is exercised too.

Tests

  • StructureChangeManagerTableCommentTests: staging, clearing, returning to baseline, undo coalescing, redo, discard, and the baseline-refresh cases (a staged edit is kept; a baseline that catches up drops the change).
  • SchemaStatementGeneratorPluginTests: the plugin-delegated statement, the nil-returns-throws case, the clear passing nil, and the ordering after every structural change.
  • PluginTableMetadataCodingTests: decoding a payload written before the new fields existed, the round trip, and that both compatibility paths report the comment read-only.
  • PostgreSQLDollarQuotedLiteralTests: a backslash-and-apostrophe payload survives untouched, and the tag grows until the body cannot close it.
  • PostgreSQLTableAttributeTests and MySQLTableStatusTests: the two read-side parsers, including which relations take a writable comment.
  • StructurePropertiesTabUITests: the tab is the leading segment and opens, and the comment is read-only on SQLite, which stores none.

Review

Codex read the diff cold twice, once as a defect review and once adversarially against the design. Everything it raised that survived checking is fixed:

  • The comment editor was live during the opening fetch. Selecting Properties before loadInitialData reached loadSchemaForEditing let an edit be staged and then dropped by the re-baseline. The field is disabled until the initial load and any post-save reload have finished.
  • Properties loaded eagerly on every structure open. Cassandra and Teradata answer fetchTableMetadata with SELECT COUNT(*), and SQLite scans up to 100,001 rows, which put a scan in front of the Columns tab for tables nobody opened Properties on. The fetch is lazy, and a refresh only repeats it for a tab that already loaded it.
  • Writability failed open. commentIsReadOnly defaulted to false, so a driver that could not classify the relation still got an editor. It defaults to read-only now, and the six engines that cannot yet classify are out of the shipped set entirely.
  • PostgreSQL's comment literal could be closed early. Dollar-quoted, with tests.
  • PostgreSQL reported pg_default for every inheriting table, which is wrong in a database created with its own tablespace. It resolves pg_database.dattablespace.
  • The inspector cache was keyed on the bare table name, so saving sales.orders would overwrite what was cached for archive.orders. Adoption now matches the whole DatabaseScope and only publishes to the visible panel when the originating tab is the selected one.
  • A failed Properties refresh destroyed the last good snapshot and marked the tab fetched, so revisiting never retried. It keeps the snapshot and leaves the tab stale.

Two findings were not acted on, with reasons:

  • "Remove the new doc comments." CLAUDE.md bans comments that describe what code does or explain callers; the surrounding files (StructureEditingSession, TableStructureLoader, PluginTableMetadata) all state the invariant and the defect it prevents, which is what these do. The one clause that did explain a caller was removed.
  • "Disable every structure mutation while a save is in flight." applyStagedChanges snapshots the queue and then clears the whole manager on success, so an edit made during a slow DDL is lost. That is real, but it predates this change and applies to column, index and key edits equally; it is a separate fix.

Screenshots

There is no before for a new tab. Both shots were captured on a 1x display, so they are 1512x861 rather than the 3024x1722 the other docs images use; they are worth re-taking on a Retina machine.

@mintlify

mintlify Bot commented Aug 29, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Aug 29, 2026, 5:20 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin

Copy link
Copy Markdown
Member Author

x

@datlechin

datlechin commented Aug 29, 2026

Copy link
Copy Markdown
Member Author

Properties tab on a PostgreSQL table. There is no before: the tab is new.

Light

Properties tab listing name, schema, database, owner, tablespace and persistence above a comment field holding several lines of JSON

Dark

The same form in dark mode

Both were captured on a 1x display, so they are 1512x861 rather than the 3024x1722 the other docs images use.

注册 for free to join this conversation on GitHub. Already have an account? 登录 to comment

标签

None yet

项目

None yet

Development

Successfully merging this pull request may close these issues.

Thêm tính năng properties cho table

1 participant