Skip to content

feat(structure): add Triggers tab to Show Structure for MySQL, PostgreSQL, and SQLite#1696

Open
datlechin wants to merge 2 commits into
mainfrom
feat/structure-triggers
Open

feat(structure): add Triggers tab to Show Structure for MySQL, PostgreSQL, and SQLite#1696
datlechin wants to merge 2 commits into
mainfrom
feat/structure-triggers

Conversation

@datlechin

Copy link
Copy Markdown
Member

Closes #1695

What

Adds a read-only Triggers tab to the table Show Structure view, alongside Columns / Indexes / Foreign Keys / DDL. It lists each trigger (name, timing, event) and shows the full definition SQL in the existing read-only syntax-highlighted viewer with a Copy button.

Available for MySQL, MariaDB, PostgreSQL, and SQLite. The tab is hidden for databases that don't expose triggers.

Why this shape

The trigger body SQL is the point of the feature, so it renders in a master-detail layout (selectable list + full definition pane) rather than as a clipped grid cell. This reuses the read-only DDLTextView, following the precedent the .ddl and .parts tabs already set: read-only tabs bypass the editable DataGridView path.

Read-only for v1, matching Beekeeper Studio's model and avoiding the edit/localization bugs the editable tools (TablePlus, Sequel Ace) have hit.

How it's wired

  • PluginKit (additive, no ABI bump): new PluginTriggerInfo; fetchTriggers on PluginDatabaseDriver with a default { [] }; supportsTriggers on DriverPlugin defaulting false.
  • App layer: TriggerInfo model; fetchTriggers on DatabaseDriver (default []) + PluginDriverAdapter bridge; supportsTriggers capability via PluginMetadataRegistry + DatabaseType.
  • Bundled drivers: MySQL (information_schema.TRIGGERS), PostgreSQL (pg_trigger + pg_get_triggerdef, handles multi-event triggers and skips internal ones), SQLite (sqlite_master with token-based timing/event parse).
  • UI: StructureTab.triggers, new TriggerDetailView, lazy fetch, availableTabs filter, count badge, and .triggers no-op cases across the grid delegate / row provider / change manager.

Registry-only plugins inherit the empty default, so no plugin rebuild is needed. Plugins like Oracle/MSSQL can add trigger support later per-plugin without an ABI bump.

Tests

  • PluginTriggerInfo Codable round-trips (with and without a WHEN clause)
  • PluginDriverAdapter.fetchTriggers mapping through a stub driver
  • StructureTab.triggers presence and display name
  • StructureGridDelegate add/delete are no-ops on the triggers tab

Docs

  • New Triggers section in docs/features/table-structure.mdx
  • CHANGELOG [Unreleased] / Added entry

Verification

SwiftLint --strict clean on all changed lines. Build extracted the new string-catalog keys (Triggers, No triggers).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dac4060657

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

supportsAddIndex: driverType.supportsAddIndex,
supportsDropIndex: driverType.supportsDropIndex,
supportsModifyPrimaryKey: driverType.supportsModifyPrimaryKey,
supportsTriggers: driverType.supportsTriggers,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid reading new DriverPlugin witnesses from stale plugins

For third-party drivers compiled against the previous PluginKit, this new DriverPlugin requirement has no witness-table entry; this method already avoids supportsColumnReorder for exactly that reason. Accessing driverType.supportsTriggers while registering such a plugin can crash or reject the plugin before it can fall back to the default false, so existing installed drivers are not actually compatible with this additive change.

Useful? React with 👍 / 👎.

Comment on lines +306 to +307
WHEN (t.tgtype & 8) != 0 THEN 'UPDATE'
WHEN (t.tgtype & 16) != 0 THEN 'DELETE'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Decode PostgreSQL UPDATE/DELETE trigger bits correctly

PostgreSQL's tgtype uses bit 8 for DELETE and bit 16 for UPDATE, but these branches label them the other way around. Any single-event UPDATE trigger will be shown as DELETE, and DELETE triggers as UPDATE, with the same mix-up in the multi-event labels above.

Useful? React with 👍 / 👎.

selectedName: $selectedTriggerName,
fontSize: $ddlFontSize,
databaseType: connection.type,
isLoading: !loadedTabs.contains(.triggers)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop leaving the triggers tab loading after fetch errors

When fetchTriggers throws (for example after a dropped connection or catalog permission error), fetchTabData only logs the error and does not insert .triggers into loadedTabs or surface an error. Because the new view's loading state is derived solely from !loadedTabs.contains(.triggers), the Triggers tab stays on ProgressView indefinitely instead of showing an error or an empty/retryable state.

Useful? React with 👍 / 👎.

@mintlify

mintlify Bot commented Jun 16, 2026

Copy link
Copy Markdown

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

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jun 16, 2026, 4:13 PM

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 531b4552b6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

var supportsTriggers: Bool {
PluginMetadataRegistry.shared.snapshot(forTypeId: pluginTypeId)?.capabilities.supportsTriggers ?? false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep trigger support type-specific

When the connection type is an alias such as Redshift or CockroachDB, pluginTypeId resolves to PostgreSQL, so this reads PostgreSQL's supportsTriggers = true instead of the alias snapshot. I checked the variant drivers returned from PostgreSQLPlugin.createDriver; RedshiftPluginDriver and CockroachPluginDriver do not implement fetchTriggers, so those connections now expose a Triggers tab that can only show the default empty result rather than being hidden. Please read the type-specific snapshot or preserve per-variant support here.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Triggers on Show Structure

1 participant