Skip to content

Commit b105478

Browse files
committed
fix(structure): replace macOS 14.4 conditional trigger columns with a 14.0-safe Enabled column variant
1 parent 7c06e51 commit b105478

9 files changed

Lines changed: 45 additions & 82 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12-
- The table structure view has a Triggers tab for MySQL, MariaDB, PostgreSQL, SQLite, SQL Server, Oracle, libSQL, and Cloudflare D1. It lists each trigger with its timing, event, and (where the engine reports them) orientation, enabled state, and condition, with a filter field and sortable columns. Selecting a trigger shows its full definition in a read-only syntax-highlighted viewer. (#1695)
12+
- The table structure view has a Triggers tab for MySQL, MariaDB, PostgreSQL, SQLite, SQL Server, Oracle, libSQL, and Cloudflare D1. It lists each trigger with its timing and event (plus enabled state where the engine reports it), with a filter field and sortable columns. Selecting a trigger shows its full definition in a read-only syntax-highlighted viewer. (#1695)
1313
- Traditional Chinese (繁體中文) language in Settings > General with full UI translation
1414

1515
### Fixed

Plugins/OracleDriverPlugin/OraclePlugin.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -491,24 +491,23 @@ final class OraclePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
491491
} else {
492492
timing = "AFTER"
493493
}
494-
let orientation = triggerType.contains("EACH ROW") ? "ROW" : "STATEMENT"
494+
let isRowLevel = triggerType.contains("EACH ROW")
495495
let enabled = (row[safe: 3]?.asText ?? "").uppercased() == "ENABLED"
496496
let whenClause = row[safe: 4]?.asText
497497
let quotedName = "\"\(name.replacingOccurrences(of: "\"", with: "\"\""))\""
498498
let quotedTable = "\"\(table.replacingOccurrences(of: "\"", with: "\"\""))\""
499-
let forEach = orientation == "ROW" ? " FOR EACH ROW" : ""
499+
let forEach = isRowLevel ? " FOR EACH ROW" : ""
500+
let whenLine = (whenClause?.isEmpty == false) ? "\n WHEN (\(whenClause ?? ""))" : ""
500501
let statement = """
501502
CREATE OR REPLACE TRIGGER \(quotedName)
502-
\(timing) \(event) ON \(quotedTable)\(forEach)
503+
\(timing) \(event) ON \(quotedTable)\(forEach)\(whenLine)
503504
"""
504505
return PluginTriggerInfo(
505506
name: name,
506507
timing: timing,
507508
event: event,
508509
statement: statement,
509-
enabled: enabled,
510-
orientation: orientation,
511-
whenClause: whenClause
510+
enabled: enabled
512511
)
513512
}
514513
}

Plugins/PostgreSQLDriverPlugin/PostgreSQLPluginDriver.swift

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,7 @@ final class PostgreSQLPluginDriver: LibPQBackedDriver, @unchecked Sendable {
307307
WHEN (t.tgtype & 16) != 0 THEN 'DELETE'
308308
WHEN (t.tgtype & 32) != 0 THEN 'TRUNCATE'
309309
ELSE '' END AS event,
310-
CASE WHEN (t.tgtype & 1) = 1 THEN 'ROW' ELSE 'STATEMENT' END AS orientation,
311310
t.tgenabled <> 'D' AS enabled,
312-
pg_get_expr(t.tgqual, t.tgrelid) AS when_clause,
313311
pg_get_triggerdef(t.oid) AS definition
314312
FROM pg_catalog.pg_trigger t
315313
JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid
@@ -321,20 +319,18 @@ final class PostgreSQLPluginDriver: LibPQBackedDriver, @unchecked Sendable {
321319
"""
322320
let result = try await execute(query: query)
323321
let triggers: [PluginTriggerInfo] = result.rows.compactMap { row -> PluginTriggerInfo? in
324-
guard row.count >= 7,
322+
guard row.count >= 5,
325323
let name = row[0].asText,
326324
let timing = row[1].asText,
327325
let event = row[2].asText,
328-
let definition = row[6].asText
326+
let definition = row[4].asText
329327
else { return nil }
330328
return PluginTriggerInfo(
331329
name: name,
332330
timing: timing,
333331
event: event,
334332
statement: definition,
335-
enabled: row[4].asText == "t",
336-
orientation: row[3].asText,
337-
whenClause: row[5].asText
333+
enabled: row[3].asText == "t"
338334
)
339335
}
340336
Self.logger.info("[trigger] postgres fetchTriggers schema=\(resolvedSchema, privacy: .public) table=\(table, privacy: .public) rows=\(result.rows.count) parsed=\(triggers.count)")

Plugins/TableProPluginKit/PluginTriggerInfo.swift

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,18 @@ public struct PluginTriggerInfo: Codable, Sendable {
1313
public let event: String
1414
public let statement: String
1515
public let enabled: Bool?
16-
public let orientation: String?
17-
public let whenClause: String?
1816

1917
public init(
2018
name: String,
2119
timing: String,
2220
event: String,
2321
statement: String,
24-
enabled: Bool? = nil,
25-
orientation: String? = nil,
26-
whenClause: String? = nil
22+
enabled: Bool? = nil
2723
) {
2824
self.name = name
2925
self.timing = timing
3026
self.event = event
3127
self.statement = statement
3228
self.enabled = enabled
33-
self.orientation = orientation
34-
self.whenClause = whenClause
35-
}
36-
37-
@_disfavoredOverload
38-
public init(
39-
name: String,
40-
timing: String,
41-
event: String,
42-
statement: String
43-
) {
44-
self.init(name: name, timing: timing, event: event, statement: statement, enabled: nil, orientation: nil, whenClause: nil)
4529
}
4630
}

TablePro/Core/Plugins/PluginDriverAdapter.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,7 @@ final class PluginDriverAdapter: DatabaseDriver, SchemaSwitchable {
259259
timing: trigger.timing,
260260
event: trigger.event,
261261
statement: trigger.statement,
262-
enabled: trigger.enabled,
263-
orientation: trigger.orientation,
264-
whenClause: trigger.whenClause
262+
enabled: trigger.enabled
265263
)
266264
}
267265
}

TablePro/Models/Query/QueryResult.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,25 +224,19 @@ struct TriggerInfo: Identifiable, Hashable {
224224
let event: String
225225
let statement: String
226226
let enabled: Bool?
227-
let orientation: String?
228-
let whenClause: String?
229227

230228
init(
231229
name: String,
232230
timing: String,
233231
event: String,
234232
statement: String,
235-
enabled: Bool? = nil,
236-
orientation: String? = nil,
237-
whenClause: String? = nil
233+
enabled: Bool? = nil
238234
) {
239235
self.name = name
240236
self.timing = timing
241237
self.event = event
242238
self.statement = statement
243239
self.enabled = enabled
244-
self.orientation = orientation
245-
self.whenClause = whenClause
246240
}
247241
}
248242

TablePro/Views/Structure/TriggerDetailView.swift

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -72,50 +72,52 @@ private struct TriggerListPane: View {
7272
@Bindable var state: TriggerInspectorState
7373

7474
private var showEnabled: Bool { triggers.contains { $0.enabled != nil } }
75-
private var showOrientation: Bool { triggers.contains { !($0.orientation ?? "").isEmpty } }
76-
private var showWhen: Bool { triggers.contains { !($0.whenClause ?? "").isEmpty } }
7775

7876
var body: some View {
7977
VStack(spacing: 0) {
8078
NativeSearchField(text: $state.searchText, placeholder: String(localized: "Filter"))
8179
.padding(.horizontal, 8)
8280
.padding(.vertical, 6)
8381
Divider()
82+
table
83+
}
84+
}
85+
86+
@ViewBuilder
87+
private var table: some View {
88+
if showEnabled {
8489
Table(state.displayed(triggers), selection: $state.selectedID, sortOrder: $state.sortOrder) {
8590
TableColumn(String(localized: "Name"), value: \.name)
8691
.width(min: 140, ideal: 220)
8792
TableColumn(String(localized: "Timing"), value: \.timing)
8893
.width(min: 70, ideal: 90)
8994
TableColumn(String(localized: "Event"), value: \.event)
9095
.width(min: 90, ideal: 150)
91-
if showOrientation {
92-
TableColumn(String(localized: "For Each")) { trigger in
93-
Text(trigger.orientation ?? "")
94-
}
95-
.width(min: 70, ideal: 90)
96-
}
97-
if showEnabled {
98-
TableColumn(String(localized: "Enabled")) { trigger in
99-
if let enabled = trigger.enabled {
100-
Image(systemName: enabled ? "checkmark.circle.fill" : "xmark.circle")
101-
.foregroundStyle(enabled ? Color.green : Color.secondary)
102-
.accessibilityLabel(enabled
103-
? String(localized: "Enabled")
104-
: String(localized: "Disabled"))
105-
}
106-
}
107-
.width(min: 60, ideal: 70)
108-
}
109-
if showWhen {
110-
TableColumn(String(localized: "When")) { trigger in
111-
Text(trigger.whenClause ?? "")
112-
.foregroundStyle(.secondary)
113-
}
114-
.width(min: 100, ideal: 180)
96+
TableColumn(String(localized: "Enabled")) { trigger in
97+
enabledIndicator(trigger)
11598
}
99+
.width(min: 60, ideal: 70)
100+
}
101+
} else {
102+
Table(state.displayed(triggers), selection: $state.selectedID, sortOrder: $state.sortOrder) {
103+
TableColumn(String(localized: "Name"), value: \.name)
104+
.width(min: 140, ideal: 240)
105+
TableColumn(String(localized: "Timing"), value: \.timing)
106+
.width(min: 70, ideal: 90)
107+
TableColumn(String(localized: "Event"), value: \.event)
108+
.width(min: 90, ideal: 160)
116109
}
117110
}
118111
}
112+
113+
@ViewBuilder
114+
private func enabledIndicator(_ trigger: TriggerInfo) -> some View {
115+
if let enabled = trigger.enabled {
116+
Image(systemName: enabled ? "checkmark.circle.fill" : "xmark.circle")
117+
.foregroundStyle(enabled ? Color.green : Color.secondary)
118+
.accessibilityLabel(enabled ? String(localized: "Enabled") : String(localized: "Disabled"))
119+
}
120+
}
119121
}
120122

121123
private struct TriggerDetailPane: View {

TableProTests/Core/Database/TriggerInfoMappingTests.swift

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,7 @@ struct TriggerInfoMappingTests {
6969
timing: "AFTER",
7070
event: "INSERT OR UPDATE",
7171
statement: "CREATE TRIGGER trg_audit ...",
72-
enabled: false,
73-
orientation: "ROW",
74-
whenClause: "new.id IS NOT NULL"
72+
enabled: false
7573
)
7674
]
7775
let connection = DatabaseConnection(name: "Test", type: .postgresql)
@@ -85,26 +83,20 @@ struct TriggerInfoMappingTests {
8583
#expect(trigger.event == "INSERT OR UPDATE")
8684
#expect(trigger.statement == "CREATE TRIGGER trg_audit ...")
8785
#expect(trigger.enabled == false)
88-
#expect(trigger.orientation == "ROW")
89-
#expect(trigger.whenClause == "new.id IS NOT NULL")
9086
}
9187

92-
@Test("PluginTriggerInfo carries optional metadata through Codable")
93-
func codableRoundTripWithMetadata() throws {
88+
@Test("PluginTriggerInfo carries enabled state through Codable")
89+
func codableRoundTripWithEnabled() throws {
9490
let original = PluginTriggerInfo(
9591
name: "trg_check",
9692
timing: "BEFORE",
9793
event: "UPDATE",
9894
statement: "CREATE TRIGGER trg_check ...",
99-
enabled: true,
100-
orientation: "STATEMENT",
101-
whenClause: "new.amount > 0"
95+
enabled: true
10296
)
10397
let data = try JSONEncoder().encode(original)
10498
let decoded = try JSONDecoder().decode(PluginTriggerInfo.self, from: data)
10599
#expect(decoded.enabled == true)
106-
#expect(decoded.orientation == "STATEMENT")
107-
#expect(decoded.whenClause == "new.amount > 0")
108100
}
109101
}
110102

docs/features/table-structure.mdx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,9 @@ The Triggers tab lists the table's triggers and shows the full definition of the
166166
| **Name** | Trigger name |
167167
| **Timing** | BEFORE, AFTER, or INSTEAD OF |
168168
| **Event** | INSERT, UPDATE, DELETE (PostgreSQL can combine events, such as INSERT OR UPDATE) |
169-
| **For Each** | ROW or STATEMENT (shown when the engine reports it) |
170169
| **Enabled** | Whether the trigger is enabled (PostgreSQL, SQL Server, Oracle) |
171-
| **When** | The trigger's WHEN condition, where present |
172170

173-
Select a trigger to see its full definition in a syntax-highlighted viewer with **Copy** and **Open in Editor** buttons. The definition is the `CREATE TRIGGER` statement (MySQL and MariaDB reconstruct it from the body; PostgreSQL uses `pg_get_triggerdef`; SQLite, libSQL, and Cloudflare D1 read the stored statement; SQL Server uses `OBJECT_DEFINITION`).
171+
Select a trigger to see its full definition in a syntax-highlighted viewer with **Copy** and **Open in Editor** buttons. The definition is the `CREATE TRIGGER` statement, which includes the orientation (FOR EACH ROW/STATEMENT) and any WHEN condition. MySQL and MariaDB reconstruct it from the body; PostgreSQL uses `pg_get_triggerdef`; SQLite, libSQL, and Cloudflare D1 read the stored statement; SQL Server uses `OBJECT_DEFINITION`.
174172

175173
<Note>
176174
Available for MySQL, MariaDB, PostgreSQL, SQLite, SQL Server, Oracle, libSQL, and Cloudflare D1. The tab is hidden for databases that do not expose triggers. Oracle shows trigger metadata only; the body is not retrieved.

0 commit comments

Comments
 (0)