-
Notifications
You must be signed in to change notification settings - Fork 121
Expand file tree
/
Copy pathQueryCursor.swift
More file actions
129 lines (113 loc) · 3.65 KB
/
Copy pathQueryCursor.swift
File metadata and controls
129 lines (113 loc) · 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import Foundation
import GRDB
import GRDBSQLite
import StructuredQueriesCore
/// A cursor of a structured query.
///
/// Iterates over and decodes all of the rows of a structured query.
public class QueryCursor<Element>: DatabaseCursor {
public var _isDone = false
public let _statement: GRDB.Statement
@usableFromInline
var decoder: SQLiteQueryDecoder
@usableFromInline
init(db: Database, query: QueryFragment) throws {
(_statement, decoder) = try db.prepare(query: query)
}
deinit {
sqlite3_reset(_statement.sqliteStatement)
}
public func _element(sqliteStatement _: SQLiteStatement) throws -> Element {
fatalError("Abstract method should be overridden in subclass")
}
}
@usableFromInline
final class QueryValueCursor<QueryValue: QueryRepresentable>: QueryCursor<QueryValue.QueryOutput> {
public typealias Element = QueryValue.QueryOutput
// NB: Required to workaround a "Legacy previews execution" bug
// https://github.com/pointfreeco/sharing-grdb/pull/60
@usableFromInline
override init(db: Database, query: QueryFragment) throws {
try super.init(db: db, query: query)
}
@inlinable
public override func _element(sqliteStatement _: SQLiteStatement) throws -> Element {
let element = try QueryValue(decoder: &decoder).queryOutput
decoder.next()
return element
}
}
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
@usableFromInline
final class QueryPackCursor<
each QueryValue: QueryRepresentable
>: QueryCursor<(repeat (each QueryValue).QueryOutput)> {
public typealias Element = (repeat (each QueryValue).QueryOutput)
// NB: Required to workaround a "Legacy previews execution" bug
// https://github.com/pointfreeco/sharing-grdb/pull/60
@usableFromInline
override init(db: Database, query: QueryFragment) throws {
try super.init(db: db, query: query)
}
@inlinable
public override func _element(sqliteStatement _: SQLiteStatement) throws -> Element {
let element = try decoder.decodeColumns((repeat each QueryValue).self)
decoder.next()
return element
}
}
@usableFromInline
final class QueryVoidCursor: QueryCursor<Void> {
typealias Element = ()
// NB: Required to workaround a "Legacy previews execution" bug
// https://github.com/pointfreeco/sharing-grdb/pull/60
@usableFromInline
override init(db: Database, query: QueryFragment) throws {
try super.init(db: db, query: query)
}
@inlinable
override func _element(sqliteStatement _: SQLiteStatement) throws {
try decoder.decodeColumns(Void.self)
decoder.next()
}
}
extension Database {
@inlinable
func prepare(query: QueryFragment) throws -> (GRDB.Statement, SQLiteQueryDecoder) {
let queryString =
query.isEmpty
? "SELECT 1 WHERE 0 -- Empty query generated by StructuredQueries"
: query.string
let statement = try makeStatement(sql: queryString)
statement.arguments = try StatementArguments(query.bindings.map { try $0.databaseValue })
return (
statement,
SQLiteQueryDecoder(statement: statement.sqliteStatement)
)
}
}
extension QueryBinding {
@inlinable
var databaseValue: DatabaseValue {
get throws {
switch self {
case let .blob(blob):
return Data(blob).databaseValue
case let .date(date):
return date.iso8601String.databaseValue
case let .double(double):
return double.databaseValue
case let .int(int):
return int.databaseValue
case .null:
return .null
case let .text(text):
return text.databaseValue
case let .uuid(uuid):
return uuid.uuidString.lowercased().databaseValue
case let .invalid(error):
throw error
}
}
}
}