Skip to content

Commit b36ad58

Browse files
committed
wip
1 parent 9107dbc commit b36ad58

File tree

3 files changed

+159
-31
lines changed

3 files changed

+159
-31
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#if compiler(>=6)
2+
public typealias _SendableAnyKeyPath = any AnyKeyPath & Sendable
3+
public typealias _SendablePartialKeyPath<Root> = any PartialKeyPath<Root> & Sendable
4+
public typealias _SendableKeyPath<Root, Value> = any KeyPath<Root, Value> & Sendable
5+
public typealias _SendableWritableKeyPath<Root, Value> = any WritableKeyPath<Root, Value>
6+
& Sendable
7+
public typealias _SendableReferenceWritableKeyPath<Root, Value> = any ReferenceWritableKeyPath<
8+
Root, Value
9+
>
10+
& Sendable
11+
#else
12+
public typealias _SendableAnyKeyPath = AnyKeyPath
13+
public typealias _SendablePartialKeyPath<Root> = PartialKeyPath<Root>
14+
public typealias _SendableKeyPath<Root, Value> = KeyPath<Root, Value>
15+
public typealias _SendableWritableKeyPath<Root, Value> = WritableKeyPath<Root, Value>
16+
public typealias _SendableReferenceWritableKeyPath<Root, Value> = ReferenceWritableKeyPath<
17+
Root, Value
18+
>
19+
#endif
20+
21+
// NB: Dynamic member lookup does not currently support sendable key paths and even breaks
22+
// autocomplete.
23+
//
24+
// * https://github.com/swiftlang/swift/issues/77035
25+
// * https://github.com/swiftlang/swift/issues/77105
26+
extension _AppendKeyPath {
27+
@_transparent
28+
func unsafeSendable() -> _SendableAnyKeyPath
29+
where Self == AnyKeyPath {
30+
#if compiler(>=6)
31+
unsafeBitCast(self, to: _SendableAnyKeyPath.self)
32+
#else
33+
self
34+
#endif
35+
}
36+
37+
@_transparent
38+
func unsafeSendable<Root>() -> _SendablePartialKeyPath<Root>
39+
where Self == PartialKeyPath<Root> {
40+
#if compiler(>=6)
41+
unsafeBitCast(self, to: _SendablePartialKeyPath<Root>.self)
42+
#else
43+
self
44+
#endif
45+
}
46+
47+
@_transparent
48+
func unsafeSendable<Root, Value>() -> _SendableKeyPath<Root, Value>
49+
where Self == KeyPath<Root, Value> {
50+
#if compiler(>=6)
51+
unsafeBitCast(self, to: _SendableKeyPath<Root, Value>.self)
52+
#else
53+
self
54+
#endif
55+
}
56+
57+
@_transparent
58+
func unsafeSendable<Root, Value>() -> _SendableWritableKeyPath<Root, Value>
59+
where Self == WritableKeyPath<Root, Value> {
60+
#if compiler(>=6)
61+
unsafeBitCast(self, to: _SendableWritableKeyPath<Root, Value>.self)
62+
#else
63+
self
64+
#endif
65+
}
66+
67+
@_transparent
68+
func unsafeSendable<Root, Value>() -> _SendableReferenceWritableKeyPath<Root, Value>
69+
where Self == ReferenceWritableKeyPath<Root, Value> {
70+
#if compiler(>=6)
71+
unsafeBitCast(self, to: _SendableReferenceWritableKeyPath<Root, Value>.self)
72+
#else
73+
self
74+
#endif
75+
}
76+
}

Sources/StructuredQueriesCore/Updates.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,36 @@ extension Updates: QueryExpression {
6161
}
6262

6363
extension Updates {
64-
public subscript<Member: TableDefinition>(
65-
dynamicMember keyPath: KeyPath<Base.TableColumns, Member>
64+
public subscript<Member: Table>(
65+
dynamicMember keyPath: KeyPath<Base.TableColumns, SubtableColumns<Base, Member>>
6666
) -> Updates<Member.QueryValue> {
6767
get { Updates<Member.QueryValue> { _ in } }
6868
set { updates.append(contentsOf: newValue.updates) }
6969
}
7070
}
71+
72+
@dynamicMemberLookup
73+
public struct SubtableColumns<Root: Table, Value: Table>: QueryExpression {
74+
public typealias QueryValue = Value
75+
76+
let keyPath: KeyPath<Root, Value> & Sendable
77+
78+
public init(keyPath: KeyPath<Root, Value> & Sendable) {
79+
self.keyPath = keyPath
80+
}
81+
82+
public subscript<Member>(
83+
dynamicMember keyPath: KeyPath<Value.TableColumns, TableColumn<Value, Member>> & Sendable
84+
) -> TableColumn<Root, Member> {
85+
let column = Value.columns[keyPath: keyPath]
86+
return TableColumn<Root, Member>(
87+
column.name,
88+
keyPath: self.keyPath.appending(path: column._keyPath).unsafeSendable(),
89+
default: column.defaultValue
90+
)
91+
}
92+
93+
public var queryFragment: QueryFragment {
94+
Value.columns.queryFragment
95+
}
96+
}

Tests/StructuredQueriesTests/NestedTableTests.swift

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,29 @@ extension SnapshotTests {
3333
"""
3434
)
3535
)
36-
// assertQuery(
37-
// Item.insert {
38-
// Item(
39-
// title: "Hello",
40-
// quantity: 24,
41-
// someColumns: SomeColumns(isCompleted: true, isPastDue: false)
42-
// )
43-
// }
44-
// )
45-
// assertQuery(
46-
// Item.insert {
47-
// ($0.title, $0.quantity, $0.someColumns.isCompleted, $0.someColumns.isPastDue)
48-
// } values: {
49-
// ("Hello", 24, true, false)
50-
// }
51-
// )
36+
assertQuery(
37+
Item.insert {
38+
Item(
39+
title: "Hello",
40+
quantity: 24,
41+
someColumns: SomeColumns(isCompleted: true, isPastDue: false)
42+
)
43+
}
44+
)
45+
assertQuery(
46+
Item.insert {
47+
($0.title, $0.quantity, $0.someColumns.isCompleted, $0.someColumns.isPastDue)
48+
} values: {
49+
("Blob", 42, false, false)
50+
}
51+
) {
52+
"""
53+
INSERT INTO "items"
54+
("title", "quantity", "isCompleted", "isPastDue")
55+
VALUES
56+
('Blob', 42, 0, 0)
57+
"""
58+
}
5259
assertQuery(
5360
Item
5461
// TODO: Should use 'is' and 'is' should not require optionality?
@@ -90,12 +97,17 @@ extension SnapshotTests {
9097
"""
9198
} results: {
9299
"""
93-
┌──────────────────────┐
94-
│ SomeColumns( │
95-
│ isCompleted: true, │
96-
│ isPastDue: false │
97-
│ ) │
98-
└──────────────────────┘
100+
┌───────────────────────┐
101+
│ SomeColumns( │
102+
│ isCompleted: true, │
103+
│ isPastDue: false │
104+
│ ) │
105+
├───────────────────────┤
106+
│ SomeColumns( │
107+
│ isCompleted: false, │
108+
│ isPastDue: false │
109+
│ ) │
110+
└───────────────────────┘
99111
"""
100112
}
101113
assertQuery(
@@ -122,6 +134,15 @@ extension SnapshotTests {
122134
│ isPastDue: true │
123135
│ ) │
124136
│ ) │
137+
├─────────────────────────────┤
138+
│ Item( │
139+
│ title: "Blob", │
140+
│ quantity: 42, │
141+
│ someColumns: SomeColumns( │
142+
│ isCompleted: true, │
143+
│ isPastDue: true │
144+
│ ) │
145+
│ ) │
125146
└─────────────────────────────┘
126147
"""
127148
}
@@ -140,12 +161,17 @@ extension SnapshotTests {
140161
"""
141162
} results: {
142163
"""
143-
┌──────────────────────┐
144-
│ SomeColumns( │
145-
│ isCompleted: true, │
146-
│ isPastDue: false │
147-
│ ) │
148-
└──────────────────────┘
164+
┌───────────────────────┐
165+
│ SomeColumns( │
166+
│ isCompleted: true, │
167+
│ isPastDue: false │
168+
│ ) │
169+
├───────────────────────┤
170+
│ SomeColumns( │
171+
│ isCompleted: false, │
172+
│ isPastDue: false │
173+
│ ) │
174+
└───────────────────────┘
149175
"""
150176
}
151177
assertQuery(
@@ -225,7 +251,7 @@ private struct Item {
225251
keyPath: \QueryValue.quantity,
226252
default: 0
227253
)
228-
public var someColumns: SomeColumns.TableColumns { SomeColumns.columns }
254+
public let someColumns = SubtableColumns(keyPath: \QueryValue.someColumns)
229255
public static var allColumns: [any StructuredQueriesCore.TableColumnExpression] {
230256
[QueryValue.columns.title]
231257
+ [QueryValue.columns.quantity]

0 commit comments

Comments
 (0)