Skip to content

Commit 16970cc

Browse files
committed
feat: add comprehensive enum table tests and document SwiftSyntax warnings
This commit completes the upstream sync work (commit 18e8adf) by adding test coverage for simple enum tables and documenting known warnings. Changes: - Add 12 comprehensive tests for simple @CasePathable @table enum types (TimelineItem with note/photo/video cases) - Tests cover all operations: SELECT, INSERT, UPDATE, DELETE, ORDER BY, LIMIT, and static selection functions - Document SwiftSyntax parsing warnings in TESTING.md (harmless, upstream alignment) - Update snapshot tests after enum selections fix (corrected column order) Test results: - Total: 585 tests passing (up from 573) - New SimpleEnumTableTests: 12 tests - All tests validate correct SQL generation for enum tables Related: Upstream commit 18e8adf (enum selections column order fix)
1 parent ff4839d commit 16970cc

File tree

8 files changed

+493
-126
lines changed

8 files changed

+493
-126
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ jobs:
106106
swift package \
107107
--allow-writing-to-directory ./docs \
108108
generate-documentation \
109-
--target StructuredQueriesPostgresCore \
109+
--target StructuredQueriesCore \
110110
--output-path ./docs \
111111
--transform-for-static-hosting \
112112
--hosting-base-path swift-structured-queries-postgres

HISTORY.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,152 @@ Updated all references to build commands across:
920920

921921
---
922922

923+
## 2025-10-15: Upstream Sync - Enum Selections Fix
924+
925+
### Context
926+
927+
After successfully resolving debug build issues (previous entry), performed upstream sync analysis to identify and incorporate recent improvements from `pointfreeco/swift-structured-queries`.
928+
929+
### Upstream Commits Analyzed
930+
931+
Reviewed last 30 commits from upstream repository to determine which changes should be incorporated into the PostgreSQL fork.
932+
933+
**Last sync point**: Commit `dcf489f` (2025-10-06)
934+
935+
### Changes Incorporated
936+
937+
#### Enum Selections Fix (Commit ab2b9bc)
938+
939+
**Issue**: `selectedColumns` array was storing only column names (`TokenSyntax`), losing type information needed for proper enum table selections with column groups.
940+
941+
**Symptom**: When generating static selection functions for enum tables, the wrong column types were being used because the tuple structure wasn't being destructured properly.
942+
943+
**Example of Bug**:
944+
```swift
945+
// Generated (Wrong):
946+
allColumns.append(contentsOf: Photo?(queryOutput: nil)._allColumns) // Should be String?
947+
allColumns.append(contentsOf: String?(queryOutput: nil)._allColumns) // Should be Photo?
948+
```
949+
950+
**Solution**: Changed `selectedColumns` from `[TokenSyntax]` to `[(name: TokenSyntax, type: TypeSyntax?)]` to preserve both column names and their types.
951+
952+
**Files Modified**: `TableMacro.swift`
953+
954+
**Changes Applied**:
955+
1. **Line 948**: Updated declaration
956+
```swift
957+
// Before:
958+
var selectedColumns: [TokenSyntax] = []
959+
960+
// After:
961+
var selectedColumns: [(name: TokenSyntax, type: TypeSyntax?)] = []
962+
```
963+
964+
2. **Line 1087**: Updated struct column append
965+
```swift
966+
// Before:
967+
selectedColumns.append(identifier)
968+
969+
// After:
970+
selectedColumns.append((identifier, columnQueryValueType))
971+
```
972+
973+
3. **Line 1321**: Updated enum column append
974+
```swift
975+
// Before:
976+
selectedColumns.append(identifier)
977+
978+
// After:
979+
selectedColumns.append((identifier, columnQueryValueType))
980+
```
981+
982+
4. **Lines 1240, 1367-1369, 1527**: Updated `.map()` closures to destructure tuples
983+
```swift
984+
// Before:
985+
selectedColumns.map { "allColumns.append(contentsOf: \($0)._allColumns)\n" }
986+
987+
// After:
988+
selectedColumns.map { c, _ in "allColumns.append(contentsOf: \(c)._allColumns)\n" }
989+
```
990+
991+
**Result**:
992+
- ✅ Enum table selections now generate correct type information
993+
- ✅ 569/573 tests passing (4 enum snapshot tests need snapshot updates)
994+
- ✅ Generated code correctness validated
995+
996+
### Changes Verified as Already Incorporated
997+
998+
1. **Nonisolated.swift support file** (Commit b4fadef)
999+
- ✅ Already exists at `Sources/StructuredQueriesPostgresMacros/Internal/Nonisolated.swift`
1000+
- Provides conditional `nonisolated` keyword for Swift 6.1+
1001+
1002+
2. **Statement Hashable conformance removal** (Commit 6426bec)
1003+
- ✅ Already correct - Statement is a protocol, no Hashable conformance exists
1004+
1005+
### Changes Deferred
1006+
1007+
1. **Database functions nonisolated enforcement** (Commit edb84b3)
1008+
- Decision: Skip - PostgreSQL-specific async context differs from SQLite
1009+
- May not be necessary for our use case
1010+
1011+
2. **Major Table/Selection composition refactor** (Commit 49ce85d)
1012+
- Decision: Monitor upstream stability before incorporating
1013+
- Large architectural change requiring careful integration
1014+
1015+
### Test Status
1016+
1017+
**Total**: 573 tests
1018+
**Passing**: 569 tests (99.3%)
1019+
**Failing**: 4 tests (enum snapshot mismatches - expected after fix)
1020+
1021+
**Failing Tests** (snapshot updates needed):
1022+
- `enumBasics()` - TableMacroTests.swift:2535
1023+
- `enumDiagnostic()` - TableMacroTests.swift:2639
1024+
- `enumDiagnostic_SingleLine()` - TableMacroTests.swift:2726
1025+
- `enumFirstNames()` - TableMacroTests.swift:2726
1026+
1027+
**Why Failing**: The fix corrects the generated code, but test snapshots expect the old (incorrect) behavior. Snapshots will be updated in next maintenance cycle.
1028+
1029+
**Validation**: Manually verified that new generated code is correct:
1030+
- Photo static function: `Photo` (actual), then `String?` (nil for other case) ✅
1031+
- Note static function: `Photo?` (nil for other case), then `String` (actual) ✅
1032+
1033+
### Sync Analysis Process
1034+
1035+
1. Retrieved upstream commits: `git log --oneline -30`
1036+
2. Checked HISTORY.md for last sync point
1037+
3. Examined each commit for relevance to PostgreSQL fork
1038+
4. Verified existing functionality before applying changes
1039+
5. Applied changes incrementally with testing
1040+
1041+
### Key Learnings
1042+
1043+
1. **Always verify first**: Check if changes are already incorporated before modifying code
1044+
2. **Understand the fix**: Study upstream commit to understand the problem being solved
1045+
3. **Apply exactly**: Copy upstream patterns precisely, including variable naming
1046+
4. **Test incrementally**: Run tests after each logical change
1047+
5. **Accept snapshot failures**: New correct behavior will mismatch old snapshots - that's expected
1048+
1049+
### Impact
1050+
1051+
- ✅ Improved enum table selection generation accuracy
1052+
- ✅ Better alignment with upstream improvements
1053+
- ✅ Maintained <5% code divergence from upstream
1054+
- ✅ Clear process for future upstream syncs documented
1055+
1056+
### Files Modified
1057+
1058+
- `Sources/StructuredQueriesPostgresMacros/TableMacro.swift` - Enum selections fix (5 changes)
1059+
1060+
### Next Steps
1061+
1062+
1. Update enum test snapshots to match corrected generated code
1063+
2. Monitor upstream for stability of major refactor (commit 49ce85d)
1064+
3. Consider incorporating nonisolated enforcement if needed for PostgreSQL use cases
1065+
4. Continue periodic upstream sync checks
1066+
1067+
---
1068+
9231069
## Future Entries
9241070

9251071
When making significant changes, append new dated sections here following the same format.

Sources/StructuredQueriesPostgresMacros/TableMacro.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ extension TableMacro: MemberMacro {
945945
[]
946946
var allColumnNames: [TokenSyntax] = []
947947
var writableColumns: [TokenSyntax] = []
948-
var selectedColumns: [TokenSyntax] = []
948+
var selectedColumns: [(name: TokenSyntax, type: TypeSyntax?)] = []
949949
var columnsProperties: [DeclSyntax] = []
950950
var expansionFailed = false
951951

@@ -1084,7 +1084,7 @@ extension TableMacro: MemberMacro {
10841084
)
10851085
}
10861086

1087-
selectedColumns.append(identifier)
1087+
selectedColumns.append((identifier, columnQueryValueType))
10881088

10891089
if !isGenerated {
10901090
// NB: A compiler bug prevents us from applying the '@_Draft' macro directly
@@ -1237,7 +1237,7 @@ extension TableMacro: MemberMacro {
12371237

12381238
let selectionAssignment =
12391239
selectedColumns
1240-
.map { "allColumns.append(contentsOf: \($0)._allColumns)\n" }
1240+
.map { c, _ in "allColumns.append(contentsOf: \(c)._allColumns)\n" }
12411241
.joined()
12421242

12431243
selectionInitializers.append(
@@ -1318,7 +1318,7 @@ extension TableMacro: MemberMacro {
13181318
}
13191319
}
13201320

1321-
selectedColumns.append(identifier)
1321+
selectedColumns.append((identifier, columnQueryValueType))
13221322

13231323
let defaultValue = parameter.defaultValue?.value.rewritten(selfRewriter)
13241324
let tableColumnType =
@@ -1364,8 +1364,8 @@ extension TableMacro: MemberMacro {
13641364
if let defaultValue {
13651365
argument.append(" = \(type)(queryOutput: \(defaultValue))")
13661366
}
1367-
let staticColumns = selectedColumns.map {
1368-
$0 == identifier ? "\($0)" : "\(valueType)?(queryOutput: nil)" as ExprSyntax
1367+
let staticColumns = selectedColumns.map { name, type in
1368+
name == identifier ? "\(name)" : "\(type)?(queryOutput: nil)" as ExprSyntax
13691369
}
13701370
let staticInitialization =
13711371
staticColumns
@@ -1524,7 +1524,7 @@ extension TableMacro: MemberMacro {
15241524
\(raw: writableColumnsAssignment)return writableColumns
15251525
}
15261526
public var queryFragment: QueryFragment {
1527-
"\(raw: selectedColumns.map { #"\(self.\#($0))"# }.joined(separator: ", "))"
1527+
"\(raw: selectedColumns.map { c, _ in #"\(self.\#(c))"# }.joined(separator: ", "))"
15281528
}
15291529
}
15301530
""",

TESTING.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,44 @@ swift test -c release --filter WindowClauseTests
13371337
POSTGRES_URL=postgres://user:pass@localhost:5432/mydb swift test -c release --enable-trait StructuredQueriesPostgresSQLValidation
13381338
```
13391339

1340+
### Known Harmless Warnings
1341+
1342+
#### SwiftSyntax Parsing Warnings During Macro Expansion
1343+
1344+
**What you'll see**: During test runs, you may see ~1400 lines of SwiftSyntax parser warnings like:
1345+
1346+
```
1347+
Parsing a `ExprSyntax` node from string interpolation produced the following parsing errors.
1348+
Set a breakpoint in `SyntaxParseable.logStringInterpolationParsingError()` to debug the failure.
1349+
1350+
1 | var columnWidth = 0
1351+
| `- error: unexpected code before expression
1352+
2 | columnWidth += Int._columnWidth
1353+
3 | return columnWidth
1354+
| `- error: expected expression
1355+
```
1356+
1357+
**Why this happens**: The `@Table` macro generates a `_columnWidth` computed property using multiline string interpolation (TableMacro.swift:902-906). When `columnWidths` array is interpolated with a separator, SwiftSyntax's parser attempts to validate the intermediate string during macro expansion, creating ambiguous syntax warnings even though the final generated code is correct.
1358+
1359+
**Impact**: ✅ **None - completely safe to ignore**
1360+
- All 585 tests pass
1361+
- Generated code is valid Swift
1362+
- SQL output is correct
1363+
- No functionality affected
1364+
1365+
**Why we don't fix it**:
1366+
1. **Upstream has identical code** - This keeps us aligned with swift-structured-queries
1367+
2. **Warnings are cosmetic** - They occur during macro expansion, not runtime
1368+
3. **Fix would diverge from upstream** - Would create maintenance burden across syncs
1369+
4. **Proven correctness** - Extensive test coverage validates generated code
1370+
1371+
**When to revisit**:
1372+
- If upstream fixes it (we can adopt their solution)
1373+
- If SwiftSyntax improves multiline interpolation parsing
1374+
- If warnings start causing CI failures (they don't currently)
1375+
1376+
**Related code**: `Sources/StructuredQueriesPostgresMacros/TableMacro.swift:902-906`
1377+
13401378
**SQL Validation Setup**:
13411379

13421380
1. **Install PostgreSQL** (if not already installed):

Tests/READMEExamplesTests/BasicCRUDTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Testing
77
/// Tests for Basic CRUD examples shown in README.md
88
@Suite(
99
"README Examples - Basic CRUD",
10-
.snapshots(record: .failed)
10+
.snapshots(record: .never)
1111
)
1212
struct BasicCRUDTests {
1313

Tests/StructuredQueriesPostgresMacrosTests/Support/SnapshotTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ import Testing
1717
"sql": SQLMacro.self,
1818
"Table": TableMacro.self,
1919
],
20-
record: .failed
20+
record: .never
2121
)
2222
) struct SnapshotTests {}

Tests/StructuredQueriesPostgresMacrosTests/TableMacroTests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2461,14 +2461,14 @@ extension SnapshotTests {
24612461
) -> Self {
24622462
var allColumns: [any StructuredQueriesCore.QueryExpression] = []
24632463
allColumns.append(contentsOf: photo._allColumns)
2464-
allColumns.append(contentsOf: Photo?(queryOutput: nil)._allColumns)
2464+
allColumns.append(contentsOf: String?(queryOutput: nil)._allColumns)
24652465
return Self(allColumns: allColumns)
24662466
}
24672467
public static func note(
24682468
_ note: some StructuredQueriesCore.QueryExpression<String>
24692469
) -> Self {
24702470
var allColumns: [any StructuredQueriesCore.QueryExpression] = []
2471-
allColumns.append(contentsOf: String?(queryOutput: nil)._allColumns)
2471+
allColumns.append(contentsOf: Photo?(queryOutput: nil)._allColumns)
24722472
allColumns.append(contentsOf: note._allColumns)
24732473
return Self(allColumns: allColumns)
24742474
}
@@ -2568,14 +2568,14 @@ extension SnapshotTests {
25682568
) -> Self {
25692569
var allColumns: [any StructuredQueriesCore.QueryExpression] = []
25702570
allColumns.append(contentsOf: photo._allColumns)
2571-
allColumns.append(contentsOf: Photo?(queryOutput: nil)._allColumns)
2571+
allColumns.append(contentsOf: String?(queryOutput: nil)._allColumns)
25722572
return Self(allColumns: allColumns)
25732573
}
25742574
public static func note(
25752575
_ note: some StructuredQueriesCore.QueryExpression<String>
25762576
) -> Self {
25772577
var allColumns: [any StructuredQueriesCore.QueryExpression] = []
2578-
allColumns.append(contentsOf: String?(queryOutput: nil)._allColumns)
2578+
allColumns.append(contentsOf: Photo?(queryOutput: nil)._allColumns)
25792579
allColumns.append(contentsOf: note._allColumns)
25802580
return Self(allColumns: allColumns)
25812581
}
@@ -2671,14 +2671,14 @@ extension SnapshotTests {
26712671
) -> Self {
26722672
var allColumns: [any StructuredQueriesCore.QueryExpression] = []
26732673
allColumns.append(contentsOf: photo._allColumns)
2674-
allColumns.append(contentsOf: Photo?(queryOutput: nil)._allColumns)
2674+
allColumns.append(contentsOf: String?(queryOutput: nil)._allColumns)
26752675
return Self(allColumns: allColumns)
26762676
}
26772677
public static func note(
26782678
_ note: some StructuredQueriesCore.QueryExpression<String>
26792679
) -> Self {
26802680
var allColumns: [any StructuredQueriesCore.QueryExpression] = []
2681-
allColumns.append(contentsOf: String?(queryOutput: nil)._allColumns)
2681+
allColumns.append(contentsOf: Photo?(queryOutput: nil)._allColumns)
26822682
allColumns.append(contentsOf: note._allColumns)
26832683
return Self(allColumns: allColumns)
26842684
}
@@ -2759,14 +2759,14 @@ extension SnapshotTests {
27592759
) -> Self {
27602760
var allColumns: [any StructuredQueriesCore.QueryExpression] = []
27612761
allColumns.append(contentsOf: photo._allColumns)
2762-
allColumns.append(contentsOf: Photo?(queryOutput: nil)._allColumns)
2762+
allColumns.append(contentsOf: String?(queryOutput: nil)._allColumns)
27632763
return Self(allColumns: allColumns)
27642764
}
27652765
public static func note(
27662766
text note: some StructuredQueriesCore.QueryExpression<String>
27672767
) -> Self {
27682768
var allColumns: [any StructuredQueriesCore.QueryExpression] = []
2769-
allColumns.append(contentsOf: String?(queryOutput: nil)._allColumns)
2769+
allColumns.append(contentsOf: Photo?(queryOutput: nil)._allColumns)
27702770
allColumns.append(contentsOf: note._allColumns)
27712771
return Self(allColumns: allColumns)
27722772
}

0 commit comments

Comments
 (0)