Skip to content

Commit 25aabdd

Browse files
committed
No explicit bind index
1 parent 324e5c3 commit 25aabdd

6 files changed

Lines changed: 69 additions & 44 deletions

File tree

Sources/Compiler/Gen/Language.swift

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ extension Language {
116116

117117
let inputTypeName = typeName(for: input)
118118
let outputTypeName = typeName(for: output)
119-
var startIndex = 1
120119

121120
return GeneratedQuery(
122121
name: definition.name.description,
@@ -131,13 +130,12 @@ extension Language {
131130
sourceSql: sql,
132131
isReadOnly: statement.isReadOnly,
133132
usedTableNames: statement.usedTableNames.sorted(),
134-
bindings: bindings(for: input, index: &startIndex)
133+
bindings: bindings(for: input)
135134
)
136135
}
137136

138137
private func bindings(
139138
for input: GenerationType,
140-
index: inout Int,
141139
name: String? = nil,
142140
owner: String? = nil,
143141
isOptional: Bool = false
@@ -148,13 +146,11 @@ extension Language {
148146
case .void:
149147
break
150148
case .builtin:
151-
result.append(.value(index: index, name: name ?? "input", owner: owner, isOptional: isOptional))
152-
index += 1
149+
result.append(.value(name: name ?? "input", owner: owner, isOptional: isOptional))
153150
case let .optional(type):
154151
result.append(
155152
contentsOf: bindings(
156153
for: type,
157-
index: &index,
158154
name: name,
159155
owner: owner,
160156
isOptional: isOptional
@@ -165,7 +161,6 @@ extension Language {
165161
result.append(
166162
contentsOf: bindings(
167163
for: field.type,
168-
index: &index,
169164
name: field.name,
170165
owner: "input",
171166
isOptional: isOptional
@@ -174,19 +169,17 @@ extension Language {
174169
}
175170
case .array(let values):
176171
result.append(.arrayStart(name: name ?? "input", owner: owner, elementName: "element"))
177-
result.append(contentsOf: bindings(for: values, index: &index, name: "element"))
172+
result.append(contentsOf: bindings(for: values, name: "element"))
178173
result.append(.arrayEnd)
179174
case .encoded(let storage, _, let adapter):
180175
result.append(
181176
.value(
182-
index: index,
183177
name: name ?? "input",
184178
owner: owner,
185179
isOptional: isOptional,
186180
adapter: (adapter, typeName(for: storage))
187181
)
188182
)
189-
index += 1
190183
}
191184

192185
return result
@@ -442,7 +435,6 @@ public struct GeneratedQuery {
442435

443436
public enum Binding {
444437
case value(
445-
index: Int,
446438
name: String,
447439
owner: String? = nil,
448440
isOptional: Bool = false,

Sources/Compiler/Gen/SwiftLanguage.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -761,14 +761,14 @@ public struct SwiftLanguage: Language {
761761

762762
private func bind(binding: GeneratedQuery.Binding) {
763763
switch binding {
764-
case let .value(index, name, owner, _, adapter):
764+
case let .value(name, owner, _, adapter):
765765
writer.write(line: "try statement.bind(value: ")
766766

767767
if let owner {
768768
writer.write(owner, ".")
769769
}
770770

771-
writer.write(name, ", to: ", index.description)
771+
writer.write(name)
772772

773773
if let adapter {
774774
writer.write(", using: adapters.", adapter.adapter.name, ", as: ", adapter.storage, ".self")

Sources/PureSQL/Statement.swift

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ import SQLite3
1313
/// methods to bind parameters, step through results, and fetch rows using
1414
/// `RowDecodable` types or adapters. It supports single row and multi row
1515
/// fetch operations.
16-
public struct Statement: ~Copyable {
16+
public final class Statement {
1717
let raw: OpaquePointer
18+
/// When calling `bind` without an index it will bind here.
19+
/// This is automatically incremented
20+
private var currentBindIndex: Int32 = 1
1821

1922
public enum Step {
2023
case row
@@ -28,18 +31,17 @@ public struct Statement: ~Copyable {
2831
self.raw = try transaction.connection.prepare(sql: source)
2932
}
3033

31-
public init(
34+
public convenience init(
3235
in transaction: borrowing Transaction,
3336
source: () -> String,
34-
bind: (inout Statement) throws -> Void = { _ in }
37+
bind: (Statement) throws -> Void = { _ in }
3538
) throws {
36-
var statement = try Statement(source(), transaction: transaction)
37-
try bind(&statement)
38-
self = statement
39+
try self.init(source(), transaction: transaction)
40+
try bind(self)
3941
}
4042

41-
public init(in transaction: borrowing Transaction, sql: SQL) throws {
42-
self = try Statement(in: transaction) {
43+
public convenience init(in transaction: borrowing Transaction, sql: SQL) throws {
44+
try self.init(in: transaction) {
4345
sql.source
4446
} bind: { statement in
4547
for (i, parameter) in sql.parameters.enumerated() {
@@ -51,36 +53,37 @@ public struct Statement: ~Copyable {
5153
/// Binds a value to the specified index in the statement.
5254
public func bind<Value: DatabasePrimitive>(
5355
value: Value,
54-
to index: Int32
56+
to index: Int32? = nil
5557
) throws(SQLError) {
56-
try value.bind(to: raw, at: index)
58+
try value.bind(to: raw, at: get(index: index))
5759
}
5860

5961
/// Binds a value using an adapter and storage type.
6062
public func bind<Storage: DatabasePrimitive, Coder: DatabaseValueAdapter>(
6163
value: Coder.Value,
62-
to index: Int32,
64+
to index: Int32? = nil,
6365
using: Coder,
6466
as storage: Storage.Type
6567
) throws(SQLError) {
6668
let storage = try Storage(value: value, into: using)
67-
try storage.bind(to: raw, at: index)
69+
try storage.bind(to: raw, at: get(index: index))
6870
}
6971

7072
/// Binds a value using an adapter and storage type.
7173
@_disfavoredOverload
7274
public func bind<Storage: DatabasePrimitive, Coder: DatabaseValueAdapter>(
7375
value: Coder.Value?,
74-
to index: Int32,
76+
to index: Int32? = nil,
7577
using: Coder,
7678
as storage: Storage.Type
7779
) throws(SQLError) {
80+
7881
if let value {
7982
let storage = try Storage(value: value, into: using)
80-
try storage.bind(to: raw, at: index)
83+
try storage.bind(to: raw, at: get(index: index))
8184
} else {
8285
let storage: Storage? = nil
83-
try storage.bind(to: raw, at: index)
86+
try storage.bind(to: raw, at: get(index: index))
8487
}
8588
}
8689

@@ -94,6 +97,13 @@ public struct Statement: ~Copyable {
9497
}
9598
}
9699

100+
private func get(index: Int32?) -> Int32 {
101+
if let index { return index }
102+
let index = currentBindIndex
103+
currentBindIndex += 1
104+
return index
105+
}
106+
97107
deinit {
98108
sqlite3_finalize(raw)
99109
}

Tests/CompilerTests/Gen/Swift.output

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,11 @@ struct QueriesQueries: ConnectionWrapper, Sendable {
273273
""",
274274
transaction: tx
275275
)
276-
try statement.bind(value: input.textNotNull, to: 1)
277-
try statement.bind(value: input.textNullable, to: 2)
278-
try statement.bind(value: input.dateWithAdapterNotNull, to: 3, using: adapters.date, as: Int.self)
279-
try statement.bind(value: input.dateWithAdapterNullable, to: 4, using: adapters.date, as: Int.self)
280-
try statement.bind(value: input.dateWithCustomAdapter, to: 5, using: adapters.customDate, as: String.self)
276+
try statement.bind(value: input.textNotNull)
277+
try statement.bind(value: input.textNullable)
278+
try statement.bind(value: input.dateWithAdapterNotNull, using: adapters.date, as: Int.self)
279+
try statement.bind(value: input.dateWithAdapterNullable, using: adapters.date, as: Int.self)
280+
try statement.bind(value: input.dateWithCustomAdapter, using: adapters.customDate, as: String.self)
281281
return try statement.fetchOne(adapters: adapters)
282282
},
283283
insertBarReturningIntPk: DatabaseQuery<InsertBarReturningIntPkInput, Int>(
@@ -291,8 +291,8 @@ struct QueriesQueries: ConnectionWrapper, Sendable {
291291
""",
292292
transaction: tx
293293
)
294-
try statement.bind(value: input.customNameIntPk, to: 1)
295-
try statement.bind(value: input.barNotNullText, to: 2)
294+
try statement.bind(value: input.customNameIntPk)
295+
try statement.bind(value: input.barNotNullText)
296296
return try statement.fetchOne()
297297
},
298298
insertBarReturningExtraColumn: DatabaseQuery<InsertBarReturningExtraColumnInput, InsertBarReturningExtraColumnOutput>(
@@ -306,8 +306,8 @@ struct QueriesQueries: ConnectionWrapper, Sendable {
306306
""",
307307
transaction: tx
308308
)
309-
try statement.bind(value: input.intPk, to: 1)
310-
try statement.bind(value: input.barNotNullText, to: 2)
309+
try statement.bind(value: input.intPk)
310+
try statement.bind(value: input.barNotNullText)
311311
return try statement.fetchOne()
312312
},
313313
selectSingleFoo: DatabaseQuery<Int, Foo?>(
@@ -321,7 +321,7 @@ struct QueriesQueries: ConnectionWrapper, Sendable {
321321
""",
322322
transaction: tx
323323
)
324-
try statement.bind(value: input, to: 1)
324+
try statement.bind(value: input)
325325
return try statement.fetchOne(adapters: adapters)
326326
},
327327
hasEmbeddedFoo: DatabaseQuery<Int, [HasEmbeddedFooOutput]>(
@@ -338,7 +338,7 @@ struct QueriesQueries: ConnectionWrapper, Sendable {
338338
""",
339339
transaction: tx
340340
)
341-
try statement.bind(value: input, to: 1)
341+
try statement.bind(value: input)
342342
return try statement.fetchAll(adapters: adapters)
343343
},
344344
bothColumnsShouldNotBeNullable: DatabaseQuery<(), [BothColumnsShouldNotBeNullableOutput]>(
@@ -366,8 +366,8 @@ struct QueriesQueries: ConnectionWrapper, Sendable {
366366
""",
367367
transaction: tx
368368
)
369-
try statement.bind(value: input.intPk, to: 1)
370-
try statement.bind(value: input.textNotNull, to: 2)
369+
try statement.bind(value: input.intPk)
370+
try statement.bind(value: input.textNotNull)
371371
return try statement.fetchOne(adapters: adapters)
372372
},
373373
inputIsArray: DatabaseQuery<[Int], ()>(
@@ -382,7 +382,7 @@ struct QueriesQueries: ConnectionWrapper, Sendable {
382382
transaction: tx
383383
)
384384
for element in input {
385-
try statement.bind(value: element, to: 1)
385+
try statement.bind(value: element)
386386
}
387387
_ = try statement.step()
388388
},
@@ -398,9 +398,9 @@ struct QueriesQueries: ConnectionWrapper, Sendable {
398398
transaction: tx
399399
)
400400
for element in input.intPks {
401-
try statement.bind(value: element, to: 1)
401+
try statement.bind(value: element)
402402
}
403-
try statement.bind(value: input.barNotNullText, to: 2)
403+
try statement.bind(value: input.barNotNullText)
404404
_ = try statement.step()
405405
}
406406
)

Tests/PureSQLTests/QueryTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,24 @@ struct QueryTests {
7272

7373
#expect(result == [.init(foo: .init(bar: 1), baz: nil)])
7474
}
75+
76+
@Test func arrayInput() async throws {
77+
let db = try TestDB.inMemory()
78+
79+
try await db.insertFoo.execute(1)
80+
try await db.insertFoo.execute(2)
81+
try await db.insertFoo.execute(3)
82+
83+
let oneAndThree = try await db.selectFooWithIds.execute([1, 3])
84+
#expect(oneAndThree == [.init(bar: 1), .init(bar: 3)])
85+
86+
let one = try await db.selectFooWithIds.execute([1])
87+
#expect(one == [.init(bar: 1)])
88+
89+
let all = try await db.selectFooWithIds.execute([1, 2, 3])
90+
#expect(all == [.init(bar: 1), .init(bar: 2), .init(bar: 3)])
91+
92+
let none = try await db.selectFooWithIds.execute([])
93+
#expect(none == [])
94+
}
7595
}

Tests/PureSQLTests/TestDatabase.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ struct TestDB {
2828

2929
@Query("SELECT foo.*, baz.* FROM foo INNER JOIN baz ON foo.bar = baz.qux")
3030
var selectFooAndBazNotOptional: any SelectFooAndBazNotOptionalQuery
31+
32+
@Query("SELECT * FROM foo WHERE bar IN ?")
33+
var selectFooWithIds: any SelectFooWithIdsQuery
3134

3235
static var migrations: [String] {
3336
return [

0 commit comments

Comments
 (0)