Skip to content

Commit 95d0ac1

Browse files
committed
wip
1 parent a430fb8 commit 95d0ac1

File tree

2 files changed

+24
-12
lines changed

2 files changed

+24
-12
lines changed

Sources/StructuredQueriesCore/QueryFragment.swift

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import StructuredQueriesSupport
66
/// directly interpolated into the string. This most commonly occurs when using the `#sql` macro,
77
/// which takes values of this type.
88
public struct QueryFragment: Hashable, Sendable {
9-
// TODO: Call this 'Element' and make 'QueryFragment' a collection of them?
9+
/// A segment of a query fragment.
1010
public enum Segment: Hashable, Sendable {
11+
/// A raw SQL fragment.
1112
case sql(String)
13+
14+
/// A binding.
1215
case binding(QueryBinding)
1316
}
1417

15-
// TODO: Make 'private(set)' and add APIs to support extensibility like 'indent()'?
18+
/// An array of segments backing this query fragment.
1619
public internal(set) var segments: [Segment] = []
1720

1821
fileprivate init(segments: [Segment]) {
@@ -53,6 +56,24 @@ public struct QueryFragment: Hashable, Sendable {
5356
query += rhs
5457
return query
5558
}
59+
60+
/// Returns a prepared SQL string and associated bindings for this query.
61+
///
62+
/// - Parameter template: Prepare a template string for a binding at a given 1-based offset.
63+
/// - Returns: A SQL string and array of associated bindings.
64+
public func prepare(
65+
_ template: (_ offset: Int) -> String
66+
) -> (sql: String, bindings: [QueryBinding]) {
67+
segments.enumerated().reduce(into: (sql: "", bindings: [QueryBinding]())) {
68+
switch $1.element {
69+
case .sql(let sql):
70+
$0.sql.append(sql)
71+
case .binding(let binding):
72+
$0.sql.append(template($1.offset + 1))
73+
$0.bindings.append(binding)
74+
}
75+
}
76+
}
5677
}
5778

5879
extension QueryFragment: CustomDebugStringConvertible {
@@ -119,7 +140,6 @@ extension QueryFragment: ExpressibleByStringInterpolation {
119140
fileprivate var segments: [Segment] = []
120141

121142
public init(literalCapacity: Int, interpolationCount: Int) {
122-
// TODO: Should all the segments' strings share the same contiguous storage as substring/span?
123143
segments.reserveCapacity(interpolationCount)
124144
}
125145

Sources/StructuredQueriesSQLite/Database.swift

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,7 @@ public struct Database {
127127
func withStatement<R>(
128128
_ query: QueryFragment, body: (OpaquePointer) throws -> R
129129
) throws -> R {
130-
let (sql, bindings) = query.segments.reduce(into: (sql: "", bindings: [QueryBinding]())) {
131-
switch $1 {
132-
case .sql(let sql):
133-
$0.sql.append(sql)
134-
case .binding(let binding):
135-
$0.sql.append("?")
136-
$0.bindings.append(binding)
137-
}
138-
}
130+
let (sql, bindings) = query.prepare { _ in "?" }
139131
var statement: OpaquePointer?
140132
let code = sqlite3_prepare_v2(storage.handle, sql, -1, &statement, nil)
141133
guard code == SQLITE_OK, let statement

0 commit comments

Comments
 (0)