Skip to content

feat(structure): triggers for more engines, metadata columns, filter/sort, persisted layout#1703

Merged
datlechin merged 3 commits into
mainfrom
feat/triggers-v2-readonly
Jun 17, 2026
Merged

feat(structure): triggers for more engines, metadata columns, filter/sort, persisted layout#1703
datlechin merged 3 commits into
mainfrom
feat/triggers-v2-readonly

Conversation

@datlechin

Copy link
Copy Markdown
Member

Follow-up to #1696 (read-only v2). Extends the Triggers tab to more engines, adds richer metadata, and polishes the UI.

Engines

fetchTriggers added to the registry plugins (each ABI-additive, no PluginKit bump):

  • SQL Serversys.triggers + OBJECT_DEFINITION (full body), events aggregated from sys.trigger_events Swift-side (no STRING_AGG version dependency), enabled from is_disabled, timing AFTER/INSTEAD OF.
  • libSQL and Cloudflare D1 — SQLite-based; reuse sqlite_master + TriggerSQLParser (D1 filters _cf_*).
  • OracleALL_TRIGGERS metadata only. The body is intentionally not fetched: TRIGGER_BODY is a LONG column OracleNIO can't read and DBMS_METADATA.GET_DDL corrupts the OracleNIO connection (existing ORA-31603 guard). Shows a reconstructed CREATE OR REPLACE TRIGGER header, enabled from STATUS, when from WHEN_CLAUSE.

supportsTriggers = true set on each plugin's DriverPlugin conformer.

Richer columns

PluginTriggerInfo/TriggerInfo gain optional enabled, orientation, whenClause (new init; the v1 4-arg init kept as @_disfavoredOverload per the PluginKit ABI rule). The trigger table shows For Each, Enabled, and When columns only when the data is present (PostgreSQL populates all three; SQL Server/Oracle populate enabled).

Filter + sort

A filter field above the list and clickable column sort (SwiftUI Table sortOrder).

Persistence

  • Code font size promoted to @AppStorage("structureCodeFontSize") (was plain @State resetting every open; also fixes the DDL tab and standardizes the editor font size on Double).
  • New AutosavingVSplitView (NSSplitView + autosaveName, set after items are added) so the table/detail divider survives reopen.

Notes

  • Registry plugins need a release to reach users: push plugin-mssql-v*, plugin-oracle-v*, plugin-libsql-v*, plugin-cloudflare-d1-v* (all additive). Bundled MySQL/MariaDB/PostgreSQL/SQLite ship with the app.
  • Editing (create/alter/drop) is the separate next PR.

Tests

TriggerInfoMappingTests updated for the new fields (Codable round-trip + adapter mapping). TriggerSQLParserTests already cover the SQLite/libSQL/D1 parse path.

CI lints the app target (included: TablePro); changed app files are SwiftLint --strict clean.

…D1 plus metadata columns, filter/sort, and persisted layout
@mintlify

mintlify Bot commented Jun 17, 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 17, 2026, 8:37 AM

💡 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: 35f341762a

ℹ️ 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".

event: event,
statement: definition
statement: definition,
enabled: row[4].asText == "t",

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 Compare against the normalized PostgreSQL boolean

This value comes from t.tgenabled <> 'D', and the libpq bridge normalizes PostgreSQL boolean columns to the text values "true"/"false" before plugin code sees them. Comparing to "t" makes every enabled PostgreSQL trigger display as disabled whenever the new Enabled column is shown, so the trigger metadata is misleading for normal triggers.

Useful? React with 👍 / 👎.

@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: 7c06e51424

ℹ️ 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".

Comment on lines +500 to +503
let statement = """
CREATE OR REPLACE TRIGGER \(quotedName)
\(timing) \(event) ON \(quotedTable)\(forEach)
"""

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 Include Oracle trigger bodies in the displayed DDL

For Oracle connections with any table trigger, supportsTriggers now exposes this result in TriggerDetailView, which renders/copies PluginTriggerInfo.statement; however this statement is synthesized from only the trigger header and never selects or appends ALL_TRIGGERS.TRIGGER_BODY (or the WHEN clause). The details, Open in Editor, and Copy actions therefore show DDL that cannot recreate the trigger and omits the actual PL/SQL logic, so please fetch the body or use DBMS_METADATA before returning the statement.

Useful? React with 👍 / 👎.

@datlechin datlechin merged commit ab73168 into main Jun 17, 2026
4 checks passed
@datlechin datlechin deleted the feat/triggers-v2-readonly branch June 17, 2026 09:02

@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: b1054780fc

ℹ️ 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".

te.type_desc AS event
FROM sys.triggers t
JOIN sys.trigger_events te ON t.object_id = te.object_id
WHERE t.parent_id = OBJECT_ID('\(bracketedFull)')

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 Escape quotes before calling OBJECT_ID

For SQL Server tables or schemas whose identifier contains a single quote (valid when bracket-quoted, e.g. [O'Brien]), bracketedFull only escapes ] and is then interpolated into a quoted OBJECT_ID('...') literal. Opening the Triggers tab for such a table will at least fail with malformed SQL, and a malicious identifier can terminate the string and append extra statements; escape ' in the full object-name literal before interpolation.

Useful? React with 👍 / 👎.

Comment on lines +21 to +22
statement: String,
enabled: Bool? = nil

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 the four-argument trigger initializer

Adding enabled by changing the existing public initializer removes the old PluginTriggerInfo(name:timing:event:statement:) ABI symbol. Source callers still compile because of the default argument, but already-built plugins that construct trigger info against the previous PluginKit can fail to load or call this initializer; follow the existing PluginKit pattern and add a separate @_disfavoredOverload four-argument initializer that forwards with enabled: nil.

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.

1 participant