Skip to content

Commit fa96f5d

Browse files
committed
Better gen
1 parent 20ebf6c commit fa96f5d

12 files changed

Lines changed: 154 additions & 36 deletions

File tree

Sources/Compiler/Gen/Language.swift

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,26 +127,30 @@ extension Language {
127127
private func bindings(
128128
for input: GenerationType,
129129
index: inout Int,
130-
owner: String? = nil
130+
name: String? = nil,
131+
owner: String? = nil,
132+
isOptional: Bool = false
131133
) -> [GeneratedQuery.Binding] {
132134
var result: [GeneratedQuery.Binding] = []
133135

134136
switch input {
135137
case .void:
136138
break
137-
case .builtin, .optional:
138-
result.append(.value(index: index, name: "input", owner: owner))
139+
case .builtin:
140+
result.append(.value(index: index, name: name ?? "input", owner: owner, isOptional: isOptional))
139141
index += 1
142+
case let .optional(type):
143+
result.append(contentsOf: bindings(for: type, index: &index, name: name, owner: owner, isOptional: isOptional))
140144
case .model(let model):
141145
for field in model.fields.values {
142-
result.append(contentsOf: bindings(for: field.type, index: &index, owner: field.name))
146+
result.append(contentsOf: bindings(for: field.type, index: &index, name: field.name, owner: "input", isOptional: isOptional))
143147
}
144148
case .array(let values):
145-
result.append(.arrayStart(name: "input", elementName: "element"))
149+
result.append(.arrayStart(name: name ?? "input", elementName: "element"))
146150
result.append(contentsOf: bindings(for: values, index: &index, owner: "element"))
147151
result.append(.arrayEnd)
148-
case .encoded(_, _, let adapter):
149-
result.append(.value(index: index, name: "input", owner: owner, adapter: adapter))
152+
case .encoded(let storage, _, let adapter):
153+
result.append(.value(index: index, name: name ?? "input", owner: owner, isOptional: isOptional, adapter: (adapter, typeName(for: storage))))
150154
index += 1
151155
}
152156

@@ -342,7 +346,7 @@ public struct GeneratedQuery {
342346
let bindings: [Binding]
343347

344348
public enum Binding {
345-
case value(index: Int, name: String, owner: String? = nil, adapter: String? = nil)
349+
case value(index: Int, name: String, owner: String? = nil, isOptional: Bool = false, adapter: (name: String, storage: String)? = nil)
346350
case arrayStart(name: String, elementName: String)
347351
case arrayEnd
348352
}

Sources/Compiler/Gen/Rewriter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ extension Rewriter: StmtSyntaxVisitor {
109109
func visit(_ stmt: CreateTableStmtSyntax) -> [Range<Substring.Index>] {
110110
switch stmt.kind {
111111
case let .columns(columns, _, _):
112-
return columns.values.compactMap { $0.type.alias?.name.location.range }
112+
return columns.values.compactMap { $0.type.alias?.location.range }
113113
case .select:
114114
return []
115115
}

Sources/Compiler/Gen/SwiftLanguage.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,8 @@ public struct SwiftLanguage: Language {
498498
writer.write("row.optionallyEmbedded(at: start + ", index.description, ")")
499499
case let .encoded(_, _, adapter):
500500
writer.write("row.value(at: start + ", index.description, ", using: ", adapter, ".self)")
501-
case let .optional(.encoded(_, _, adapter)):
502-
writer.write("row.optionalValue(at: start + ", index.description, ", using: ", adapter, ".self)")
501+
case let .optional(.encoded(storage, _, adapter)):
502+
writer.write("row.optionalValue(at: start + ", index.description, ", using: ", adapter, ".self, storage: ", typeName(for: storage), ".self)")
503503
default:
504504
fatalError("Invalid field type \(field.typeName) \(field.type)")
505505
}
@@ -639,7 +639,7 @@ public struct SwiftLanguage: Language {
639639

640640
private func bind(binding: GeneratedQuery.Binding) {
641641
switch binding {
642-
case let .value(index, name, owner, adapter):
642+
case let .value(index, name, owner, _, adapter):
643643
writer.write(line: "try statement.bind(value: ")
644644

645645
if let owner {
@@ -649,7 +649,7 @@ public struct SwiftLanguage: Language {
649649
writer.write(name, ", to: ", index.description)
650650

651651
if let adapter {
652-
writer.write(", using: ", adapter, ".self")
652+
writer.write(", using: ", adapter.name, ".self, as: ", adapter.storage, ".self")
653653
}
654654

655655
writer.write(")")

Sources/Compiler/Sema/Type.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,6 @@ public enum Type: Equatable, CustomStringConvertible, Sendable {
126126
return tv
127127
}
128128

129-
/// The alias if there is one
130-
var alias: Alias? {
131-
switch self {
132-
case .alias(_, let a, _): a
133-
case .optional(let t): t.alias
134-
default: nil
135-
}
136-
}
137-
138129
/// Will return the optional version of `self` if it is not already optional
139130
func coerceToOptional() -> Type {
140131
isOptional ? self : .optional(self)

Sources/Compiler/Syntax/TypeNameSyntax.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ struct TypeNameSyntax: Syntax, CustomStringConvertible, Sendable {
1717
let name: AliasSyntax
1818
let using: IdentifierSyntax?
1919

20+
var location: SourceLocation {
21+
guard let using else { return name.location }
22+
return name.location.spanning(using.location)
23+
}
24+
2025
var description: String {
2126
if let using {
2227
return "\(name) USING \(using)"

Sources/Otter/Cursor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public struct Row: ~Copyable {
5151
@inlinable public func optionalValue<Storage: DatabasePrimitive, Coder: DatabaseValueAdapter>(
5252
at column: Int32,
5353
using adapter: Coder.Type,
54-
storage: Storage
54+
storage: Storage.Type
5555
) throws(OtterError) -> Coder.Value? {
5656
guard let storage = try Storage?(from: sqliteStatement, at: column) else { return nil}
5757
return try storage.decode(from: adapter)

Sources/Otter/Statement.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@ public struct Statement: ~Copyable {
6969
try storage.bind(to: raw, at: index)
7070
}
7171

72+
@_disfavoredOverload
73+
public func bind<Storage: DatabasePrimitive, Coder: DatabaseValueAdapter>(
74+
value: Coder.Value?,
75+
to index: Int32,
76+
using: Coder.Type,
77+
as storage: Storage.Type
78+
) throws(OtterError) {
79+
if let value {
80+
let storage = try Storage(value: value, into: using)
81+
try storage.bind(to: raw, at: index)
82+
} else {
83+
let storage: Storage? = nil
84+
try storage.bind(to: raw, at: index)
85+
}
86+
}
87+
7288
public func step() throws(OtterError) -> Step {
7389
let code = SQLiteCode(sqlite3_step(raw))
7490
switch code {

Tests/CompilerTests/Gen/Migrations.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ CREATE TABLE user (
55
preference INTEGER AS Bool,
66
favoriteNumber INTEGER,
77
randomValue ANY,
8-
bornOn STRING AS Date USING CustomDate NOT NULL,
8+
bornOn TEXT AS Date USING CustomDate,
99
fullName TEXT NOT NULL GENERATED ALWAYS AS (firstName || ' ' || lastName)
1010
);
1111

1212
CREATE TABLE interest (
1313
id INTEGER PRIMARY KEY AUTOINCREMENT,
1414
value TEXT NOT NULL,
15-
userId TEXT REFERENCES user(id)
15+
userId INTEGER REFERENCES user(id)
1616
);

Tests/CompilerTests/Gen/Queries.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
insertUser:
2+
INSERT INTO user VALUES (?, ?, ?, ?, ?, ?, ?);
3+
14
selectUsers:
25
SELECT * FROM user;
36

Tests/CompilerTests/Gen/Swift.output

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Otter
44
struct Interest: Hashable, Sendable, Identifiable, RowDecodable {
55
let id: Int
66
let value: String
7-
let userId: String?
7+
let userId: Int?
88

99
static let nonOptionalIndices: [Int32] = [0, 1]
1010

@@ -20,7 +20,7 @@ struct Interest: Hashable, Sendable, Identifiable, RowDecodable {
2020
init(
2121
id: Int,
2222
value: String,
23-
userId: String?
23+
userId: Int?
2424
) {
2525
self.id = id
2626
self.value = value
@@ -35,10 +35,10 @@ struct User: Hashable, Sendable, Identifiable, RowDecodable {
3535
let preference: Bool?
3636
let favoriteNumber: Int?
3737
let randomValue: SQLAny?
38-
let bornOn: Date
38+
let bornOn: Date?
3939
let fullName: String
4040

41-
static let nonOptionalIndices: [Int32] = [0, 1, 2, 6, 7]
41+
static let nonOptionalIndices: [Int32] = [0, 1, 2, 7]
4242

4343
init(
4444
row: borrowing Otter.Row,
@@ -47,10 +47,10 @@ struct User: Hashable, Sendable, Identifiable, RowDecodable {
4747
self.id = try row.value(at: start + 0)
4848
self.firstName = try row.value(at: start + 1)
4949
self.lastName = try row.value(at: start + 2)
50-
self.preference = try row.optionalValue(at: start + 3, using: BoolDatabaseValueAdapter.self)
50+
self.preference = try row.optionalValue(at: start + 3, using: BoolDatabaseValueAdapter.self, storage: Int.self)
5151
self.favoriteNumber = try row.value(at: start + 4)
5252
self.randomValue = try row.value(at: start + 5)
53-
self.bornOn = try row.value(at: start + 6, using: CustomDateDatabaseValueAdapter.self)
53+
self.bornOn = try row.optionalValue(at: start + 6, using: CustomDateDatabaseValueAdapter.self, storage: String.self)
5454
self.fullName = try row.value(at: start + 7)
5555
}
5656

@@ -61,7 +61,7 @@ struct User: Hashable, Sendable, Identifiable, RowDecodable {
6161
preference: Bool?,
6262
favoriteNumber: Int?,
6363
randomValue: SQLAny?,
64-
bornOn: Date,
64+
bornOn: Date?,
6565
fullName: String
6666
) {
6767
self.id = id
@@ -75,6 +75,16 @@ struct User: Hashable, Sendable, Identifiable, RowDecodable {
7575
}
7676
}
7777

78+
struct InsertUserInput: Hashable, Sendable, Identifiable {
79+
let id: Int
80+
let firstName: String
81+
let lastName: String
82+
let preference: Bool?
83+
let favoriteNumber: Int?
84+
let randomValue: SQLAny?
85+
let bornOn: Date?
86+
}
87+
7888
struct SelectUserWithManyInputsInput: Hashable, Sendable, Identifiable {
7989
let id: Int
8090
let firstName: String
@@ -173,6 +183,8 @@ struct SelectWithOptionalInterestOutput: Hashable, Sendable, RowDecodable {
173183
}
174184

175185
protocol QueriesQueries {
186+
associatedtype InsertUser: InsertUserQuery
187+
var insertUser: InsertUser { get }
176188
associatedtype SelectUsers: SelectUsersQuery
177189
var selectUsers: SelectUsers { get }
178190
associatedtype SelectUserById: SelectUserByIdQuery
@@ -190,6 +202,7 @@ protocol QueriesQueries {
190202
}
191203

192204
struct QueriesQueriesNoop: QueriesQueries {
205+
let insertUser: AnyQuery<InsertUserInput, ()>
193206
let selectUsers: AnyQuery<(), [User]>
194207
let selectUserById: AnyQuery<Int, User?>
195208
let selectUserByIds: AnyQuery<[Int], [User]>
@@ -199,6 +212,7 @@ struct QueriesQueriesNoop: QueriesQueries {
199212
let selectWithOptionalInterest: AnyQuery<(), [SelectWithOptionalInterestOutput]>
200213

201214
init(
215+
insertUser: any InsertUserQuery = Queries.Just(),
202216
selectUsers: any SelectUsersQuery = Queries.Just(),
203217
selectUserById: any SelectUserByIdQuery = Queries.Just(),
204218
selectUserByIds: any SelectUserByIdsQuery = Queries.Just(),
@@ -207,6 +221,7 @@ struct QueriesQueriesNoop: QueriesQueries {
207221
selectWithInterest: any SelectWithInterestQuery = Queries.Just(),
208222
selectWithOptionalInterest: any SelectWithOptionalInterestQuery = Queries.Just()
209223
) {
224+
self.insertUser = insertUser.eraseToAnyQuery()
210225
self.selectUsers = selectUsers.eraseToAnyQuery()
211226
self.selectUserById = selectUserById.eraseToAnyQuery()
212227
self.selectUserByIds = selectUserByIds.eraseToAnyQuery()
@@ -220,6 +235,29 @@ struct QueriesQueriesNoop: QueriesQueries {
220235
struct QueriesQueriesImpl: QueriesQueries {
221236
let connection: any Connection
222237

238+
var insertUser: AnyDatabaseQuery<InsertUserInput, ()> {
239+
AnyDatabaseQuery<InsertUserInput, ()>(
240+
.write,
241+
in: connection,
242+
watchingTables: ["user"]
243+
) { input, tx in
244+
var statement = try Otter.Statement(
245+
"""
246+
INSERT INTO user VALUES (?, ?, ?, ?, ?, ?, ?)
247+
""",
248+
transaction: tx
249+
)
250+
try statement.bind(value: input.id, to: 1)
251+
try statement.bind(value: input.firstName, to: 2)
252+
try statement.bind(value: input.lastName, to: 3)
253+
try statement.bind(value: input.preference, to: 4, using: BoolDatabaseValueAdapter.self, as: Int.self)
254+
try statement.bind(value: input.favoriteNumber, to: 5)
255+
try statement.bind(value: input.randomValue, to: 6)
256+
try statement.bind(value: input.bornOn, to: 7, using: CustomDateDatabaseValueAdapter.self, as: String.self)
257+
_ = try statement.step()
258+
}
259+
}
260+
223261
var selectUsers: AnyDatabaseQuery<(), [User]> {
224262
AnyDatabaseQuery<(), [User]>(
225263
.read,
@@ -301,8 +339,8 @@ struct QueriesQueriesImpl: QueriesQueries {
301339
""",
302340
transaction: tx
303341
)
304-
try statement.bind(value: id.input, to: 1)
305-
try statement.bind(value: firstName.input, to: 2)
342+
try statement.bind(value: input.id, to: 1)
343+
try statement.bind(value: input.firstName, to: 2)
306344
return try statement.fetchOne()
307345
}
308346
}
@@ -353,15 +391,15 @@ struct DB: Database{
353391
preference INTEGER ,
354392
favoriteNumber INTEGER,
355393
randomValue ANY,
356-
bornOn STRING USING CustomDate NOT NULL,
394+
bornOn TEXT ,
357395
fullName TEXT NOT NULL GENERATED ALWAYS AS (firstName || ' ' || lastName)
358396
);
359397
""",
360398
"""
361399
CREATE TABLE interest (
362400
id INTEGER PRIMARY KEY AUTOINCREMENT,
363401
value TEXT NOT NULL,
364-
userId TEXT REFERENCES user(id)
402+
userId INTEGER REFERENCES user(id)
365403
);;
366404
"""
367405
]
@@ -371,6 +409,17 @@ struct DB: Database{
371409
}
372410
}
373411

412+
typealias InsertUserQuery = Query<InsertUserInput, ()>
413+
extension Query where Input == InsertUserInput {
414+
func execute(id: Int, firstName: String, lastName: String, preference: Bool?, favoriteNumber: Int?, randomValue: SQLAny?, bornOn: Date?) async throws -> Output {
415+
try await execute(with: InsertUserInput(id: id, firstName: firstName, lastName: lastName, preference: preference, favoriteNumber: favoriteNumber, randomValue: randomValue, bornOn: bornOn))
416+
}
417+
418+
func observe(id: Int, firstName: String, lastName: String, preference: Bool?, favoriteNumber: Int?, randomValue: SQLAny?, bornOn: Date?) -> any QueryObservation<Output> {
419+
observe(with: InsertUserInput(id: id, firstName: firstName, lastName: lastName, preference: preference, favoriteNumber: favoriteNumber, randomValue: randomValue, bornOn: bornOn))
420+
}
421+
}
422+
374423
typealias SelectUsersQuery = Query<(), [User]>
375424
typealias SelectUserByIdQuery = Query<Int, User?>
376425
typealias SelectUserByIdsQuery = Query<[Int], [User]>

0 commit comments

Comments
 (0)