Skip to content

Commit c53497d

Browse files
committed
INSERT statements should not return an optional
1 parent 566b962 commit c53497d

File tree

7 files changed

+60
-13
lines changed

7 files changed

+60
-13
lines changed

Sources/Compiler/Gen/Language.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,8 @@ extension Language {
264264
// Will return an array if it returns many or optional if its a single result
265265
let singleOrMany: (GenerationType) -> GenerationType = {
266266
switch statement.outputCardinality {
267-
case .single: .optional($0)
267+
// INSERTs will always return a value so no need to do optional
268+
case .single: statement.isInsert ? $0 : .optional($0)
268269
case .many: .array($0)
269270
}
270271
}

Sources/Compiler/Gen/SwiftLanguage.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,25 @@ public struct SwiftLanguage: Language {
244244
writer.write(line: "init(")
245245
writer.indent()
246246
for (position, query) in queries.positional() {
247-
writer.write(line: query.variableName, ": any ", query.typealiasName, " = Queries.Just()")
247+
writer.write(line: query.variableName, ": any ", query.typealiasName)
248+
249+
switch query.output {
250+
case .model:
251+
// We might be able to initialize one in the future with all default values
252+
// but it seems hacky so just fail
253+
writer.write(" = Queries.Fail()")
254+
case .builtin(let name):
255+
let defaultValue = switch name {
256+
case "Double": "0.0"
257+
case "Int": "0"
258+
case "String": "\"\""
259+
case "Data": "Data()"
260+
default: "SQLAny.int(0)"
261+
}
262+
writer.write(" = Queries.Just(", defaultValue, ")")
263+
default:
264+
writer.write(" = Queries.Just()")
265+
}
248266

249267
if !position.isLast {
250268
writer.write(",")

Sources/Compiler/Sema/Statement.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ public struct Statement {
3737
return definition?.name
3838
}
3939

40+
/// Whether or not the source syntax is an INSERT statement
41+
public var isInsert: Bool {
42+
if syntax is InsertStmtSyntax { return true }
43+
guard let definition = syntax as? QueryDefinitionStmtSyntax else { return false }
44+
return definition.statement is InsertStmtSyntax
45+
}
46+
4047
/// Replaces the definition with the given input
4148
public func with(definition: Definition?) -> Statement {
4249
return Statement(

Sources/Otter/Queries/Fail.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public extension Queries {
1313
/// The error to throw on execution
1414
let error: any Error
1515

16+
/// The default error to throw if none is provided
17+
struct FailError: Error {}
18+
1619
/// Initializes a query that always fails with an error.
1720
/// This is useful for unit tests and previews to test
1821
/// how a part of an application behaives when an error
@@ -23,6 +26,14 @@ public extension Queries {
2326
self.error = error
2427
}
2528

29+
/// Initializes a query that always fails with an error.
30+
/// This is useful for unit tests and previews to test
31+
/// how a part of an application behaives when an error
32+
/// is thrown.
33+
public init() {
34+
self.error = FailError()
35+
}
36+
2637
public var transactionKind: Transaction.Kind { .read }
2738
public var watchedTables: Set<String> { [] }
2839
public var connection: any Connection { NoopConnection() }

Sources/Otter/Statement.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,21 @@ public struct Statement: ~Copyable {
103103
return result
104104
}
105105

106-
/// Fetches a single row returned by the statement
106+
/// Optionally fetches a single row returned by the statement
107107
public consuming func fetchOne<T: RowDecodable>() throws(OtterError) -> T? {
108108
return try fetchOne(of: T.self)
109109
}
110110

111+
/// Fetches a single row returned by the statement
112+
@_disfavoredOverload
113+
public consuming func fetchOne<T: RowDecodable>() throws(OtterError) -> T {
114+
guard let row = try fetchOne(of: T.self) else {
115+
throw OtterError.queryReturnedNoValue
116+
}
117+
118+
return row
119+
}
120+
111121
/// Fetches a single row returned by the statement
112122
public consuming func fetchOne<T: RowDecodable>(
113123
of _: T.Type

Tests/CompilerTests/Gen/Queries.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
insertUser:
2-
INSERT INTO user VALUES (?, ?, ?, ?, ?, ?, ?);
2+
INSERT INTO user VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING id;
33

44
selectUsers:
55
SELECT * FROM user;

Tests/CompilerTests/Gen/Swift.output

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ protocol QueriesQueries: ConnectionWrapper {
203203

204204
struct QueriesQueriesNoop: QueriesQueries {
205205
let connection: any Connection = NoopConnection()
206-
let insertUser: AnyQuery<InsertUserInput, ()>
206+
let insertUser: AnyQuery<InsertUserInput, Int>
207207
let selectUsers: AnyQuery<(), [User]>
208208
let selectUserById: AnyQuery<Int, User?>
209209
let selectUserByIds: AnyQuery<[Int], [User]>
@@ -213,7 +213,7 @@ struct QueriesQueriesNoop: QueriesQueries {
213213
let selectWithOptionalInterest: AnyQuery<(), [SelectWithOptionalInterestOutput]>
214214

215215
init(
216-
insertUser: any InsertUserQuery = Queries.Just(),
216+
insertUser: any InsertUserQuery = Queries.Just(0),
217217
selectUsers: any SelectUsersQuery = Queries.Just(),
218218
selectUserById: any SelectUserByIdQuery = Queries.Just(),
219219
selectUserByIds: any SelectUserByIdsQuery = Queries.Just(),
@@ -236,15 +236,15 @@ struct QueriesQueriesNoop: QueriesQueries {
236236
struct QueriesQueriesImpl: QueriesQueries {
237237
let connection: any Connection
238238

239-
var insertUser: DatabaseQuery<InsertUserInput, ()> {
240-
DatabaseQuery<InsertUserInput, ()>(
239+
var insertUser: DatabaseQuery<InsertUserInput, Int> {
240+
DatabaseQuery<InsertUserInput, Int>(
241241
.write,
242242
in: connection,
243243
watchingTables: ["user"]
244244
) { input, tx in
245245
let statement = try Otter.Statement(
246246
"""
247-
INSERT INTO user VALUES (?, ?, ?, ?, ?, ?, ?)
247+
INSERT INTO user VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING id
248248
""",
249249
transaction: tx
250250
)
@@ -255,7 +255,7 @@ struct QueriesQueriesImpl: QueriesQueries {
255255
try statement.bind(value: input.favoriteNumber, to: 5)
256256
try statement.bind(value: input.randomValue, to: 6)
257257
try statement.bind(value: input.bornOn, to: 7, using: CustomDateDatabaseValueAdapter.self, as: String.self)
258-
_ = try statement.step()
258+
return try statement.fetchOne()
259259
}
260260
}
261261

@@ -410,13 +410,13 @@ struct DB: Database{
410410
}
411411
}
412412

413-
typealias InsertUserQuery = Query<InsertUserInput, ()>
413+
typealias InsertUserQuery = Query<InsertUserInput, Int>
414414
extension Query where Input == InsertUserInput {
415415
func execute(id: Int, firstName: String, lastName: String, preference: Bool?, favoriteNumber: Int?, randomValue: SQLAny?, bornOn: Date?) async throws -> Output {
416416
try await execute(with: InsertUserInput(id: id, firstName: firstName, lastName: lastName, preference: preference, favoriteNumber: favoriteNumber, randomValue: randomValue, bornOn: bornOn))
417417
}
418418

419-
func execute(id: Int, firstName: String, lastName: String, preference: Bool?, favoriteNumber: Int?, randomValue: SQLAny?, bornOn: Date?, tx: borrowing Transaction) async throws -> Output {
419+
func execute(id: Int, firstName: String, lastName: String, preference: Bool?, favoriteNumber: Int?, randomValue: SQLAny?, bornOn: Date?, tx: borrowing Transaction) throws -> Output {
420420
try execute(with: InsertUserInput(id: id, firstName: firstName, lastName: lastName, preference: preference, favoriteNumber: favoriteNumber, randomValue: randomValue, bornOn: bornOn), tx: tx)
421421
}
422422

@@ -435,7 +435,7 @@ extension Query where Input == SelectUserWithManyInputsInput {
435435
try await execute(with: SelectUserWithManyInputsInput(id: id, firstName: firstName))
436436
}
437437

438-
func execute(id: Int, firstName: String, tx: borrowing Transaction) async throws -> Output {
438+
func execute(id: Int, firstName: String, tx: borrowing Transaction) throws -> Output {
439439
try execute(with: SelectUserWithManyInputsInput(id: id, firstName: firstName), tx: tx)
440440
}
441441

0 commit comments

Comments
 (0)