Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Sources/SQLiteData/FetchAll+Sections.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1556,15 +1556,15 @@ struct FetchAllSectionedStatementValueRequest<
Key: QueryRepresentable
>: FetchKeyRequest
where Value.QueryOutput: Sendable, Key.QueryOutput: Hashable & Sendable {
let statement: SQLQueryExpression<(Value, Key)>
let prepared: PreparedQuery

init(
statement: Select<(), Value, ()>,
sectionBy: _Sectioning<Key>
) where Value: StructuredQueriesCore.Table {
let prefix: Select<(Value, Key), Value, ()> = sectionedColumns(of: Value.self, sectionBy)
let sectioned: Select<(Value, Key), Value, ()> = prefix + statement
self.statement = SQLQueryExpression(sectioned)
self.prepared = PreparedQuery(sectioned.query)
}

init<From: StructuredQueriesCore.Table, each J: StructuredQueriesCore.Table>(
Expand All @@ -1575,21 +1575,21 @@ where Value.QueryOutput: Sendable, Key.QueryOutput: Hashable & Sendable {
sectionedOrder(of: From.self, sectionBy) + statement
let sectioned: Select<(Value, Key), From, (repeat each J)> =
ordered + sectionedColumn(of: From.self, sectionBy)
self.statement = SQLQueryExpression(sectioned)
self.prepared = PreparedQuery(sectioned.query)
}

func fetch(_ db: Database) throws -> ResultsSectionCollection<Value.QueryOutput, Key.QueryOutput>
{
try ResultsSectionCollection(
cursor: QuerySectionedCursor<Value, Key>(db: db, query: statement.queryFragment)
cursor: QuerySectionedCursor<Value, Key>(db: db, prepared: prepared, cached: true)
)
}

static func == (lhs: Self, rhs: Self) -> Bool {
lhs.statement.query == rhs.statement.query
lhs.prepared == rhs.prepared
}

func hash(into hasher: inout Hasher) {
hasher.combine(statement.query)
hasher.combine(prepared)
}
}
15 changes: 9 additions & 6 deletions Sources/SQLiteData/FetchAll.swift
Original file line number Diff line number Diff line change
Expand Up @@ -597,12 +597,15 @@ extension FetchAll: Equatable where Element: Equatable {
}
#endif

struct FetchAllStatementValueRequest<Value: QueryRepresentable>: StatementKeyRequest {
let statement: SQLQueryExpression<Value>
init(statement: some StructuredQueriesCore.Statement<Value>) {
self.statement = SQLQueryExpression(statement)
struct FetchAllStatementValueRequest<QueryValue: QueryRepresentable>: StatementKeyRequest {
let prepared: PreparedQuery
init(statement: some StructuredQueriesCore.Statement<QueryValue>) {
self.prepared = PreparedQuery(statement.query)
}
func fetch(_ db: Database) throws -> [Value.QueryOutput] {
try statement.fetchAll(db)
func fetch(_ db: Database) throws -> [QueryValue.QueryOutput] {
let cursor = try QueryValueCursor<QueryValue>(db: db, prepared: prepared, cached: true)
var output: [QueryValue.QueryOutput] = []
try cursor.forEach { output.append($0) }
return output
}
}
39 changes: 20 additions & 19 deletions Sources/SQLiteData/FetchOne.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1495,38 +1495,39 @@ extension FetchOne: Equatable where Value: Equatable {
}
#endif

private struct FetchOneStatementValueRequest<Value: QueryRepresentable>: StatementKeyRequest {
let statement: SQLQueryExpression<Value>
init(statement: some StructuredQueriesCore.Statement<Value>) {
self.statement = SQLQueryExpression(statement)
private struct FetchOneStatementValueRequest<QueryValue: QueryRepresentable>: StatementKeyRequest {
let prepared: PreparedQuery
init(statement: some StructuredQueriesCore.Statement<QueryValue>) {
self.prepared = PreparedQuery(statement.query)
}
func fetch(_ db: Database) throws -> Value.QueryOutput {
guard let result = try statement.fetchOne(db)
func fetch(_ db: Database) throws -> QueryValue.QueryOutput {
guard
let result = try QueryValueCursor<QueryValue>(db: db, prepared: prepared, cached: true).next()
else { throw NotFound() }
return result
}
}

private struct FetchOneStatementOptionalValueRequest<Value: QueryRepresentable>:
private struct FetchOneStatementOptionalValueRequest<QueryValue: QueryRepresentable>:
StatementKeyRequest
{
let statement: SQLQueryExpression<Value>
init(statement: some StructuredQueriesCore.Statement<Value>) {
self.statement = SQLQueryExpression(statement)
let prepared: PreparedQuery
init(statement: some StructuredQueriesCore.Statement<QueryValue>) {
self.prepared = PreparedQuery(statement.query)
}
func fetch(_ db: Database) throws -> Value.QueryOutput? {
try statement.fetchOne(db)
func fetch(_ db: Database) throws -> QueryValue.QueryOutput? {
try QueryValueCursor<QueryValue>(db: db, prepared: prepared, cached: true).next()
}
}

private struct FetchOneStatementOptionalProtocolRequest<
Value: QueryRepresentable & StructuredQueriesCore._OptionalProtocol
>: StatementKeyRequest where Value.QueryOutput: StructuredQueriesCore._OptionalProtocol {
let statement: SQLQueryExpression<Value>
init(statement: some StructuredQueriesCore.Statement<Value>) {
self.statement = SQLQueryExpression(statement)
QueryValue: QueryRepresentable & StructuredQueriesCore._OptionalProtocol
>: StatementKeyRequest where QueryValue.QueryOutput: StructuredQueriesCore._OptionalProtocol {
let prepared: PreparedQuery
init(statement: some StructuredQueriesCore.Statement<QueryValue>) {
self.prepared = PreparedQuery(statement.query)
}
func fetch(_ db: Database) throws -> Value.QueryOutput {
try statement.fetchOne(db) ?? ._none
func fetch(_ db: Database) throws -> QueryValue.QueryOutput {
try QueryValueCursor<QueryValue>(db: db, prepared: prepared, cached: true).next() ?? ._none
}
}
7 changes: 4 additions & 3 deletions Sources/SQLiteData/Internal/StatementKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import StructuredQueriesCore

protocol StatementKeyRequest<QueryValue>: FetchKeyRequest {
associatedtype QueryValue
var statement: SQLQueryExpression<QueryValue> { get }
var prepared: PreparedQuery { get }
}

extension StatementKeyRequest {
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.statement.query == rhs.statement.query
lhs.prepared.sql == rhs.prepared.sql && lhs.prepared.bindings == rhs.prepared.bindings
}

func hash(into hasher: inout Hasher) {
hasher.combine(statement.query)
hasher.combine(prepared.sql)
hasher.combine(prepared.bindings)
}
}
59 changes: 44 additions & 15 deletions Sources/SQLiteData/StructuredQueries+GRDB/QueryCursor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ public class QueryCursor<Element>: DatabaseCursor {
var decoder: SQLiteQueryDecoder

@usableFromInline
init(db: Database, query: QueryFragment) throws {
(_statement, decoder) = try db.prepare(query: query)
init(db: Database, prepared: PreparedQuery, cached: Bool) throws {
(_statement, decoder) = try db.prepare(prepared, cached: cached)
}

@usableFromInline
convenience init(db: Database, query: QueryFragment, cached: Bool) throws {
try self.init(db: db, prepared: PreparedQuery(query), cached: cached)
}

deinit {
sqlite3_reset(_statement.sqliteStatement)
sqlite3_clear_bindings(_statement.sqliteStatement)
}

public func _element(sqliteStatement _: SQLiteStatement) throws -> Element {
Expand Down Expand Up @@ -84,8 +90,8 @@ final class QueryValueCursor<QueryValue: QueryRepresentable>: QueryCursor<QueryV
// NB: Required to workaround a "Legacy previews execution" bug
// https://github.com/pointfreeco/sqlite-data/pull/60
@usableFromInline
override init(db: Database, query: QueryFragment) throws {
try super.init(db: db, query: query)
override init(db: Database, prepared: PreparedQuery, cached: Bool) throws {
try super.init(db: db, prepared: prepared, cached: cached)
}

@inlinable
Expand All @@ -110,8 +116,8 @@ final class QuerySectionedCursor<
// NB: Required to workaround a "Legacy previews execution" bug
// https://github.com/pointfreeco/sqlite-data/pull/60
@usableFromInline
override init(db: Database, query: QueryFragment) throws {
try super.init(db: db, query: query)
override init(db: Database, prepared: PreparedQuery, cached: Bool) throws {
try super.init(db: db, prepared: prepared, cached: cached)
}

@inlinable
Expand Down Expand Up @@ -139,8 +145,8 @@ final class QueryPackCursor<
// NB: Required to workaround a "Legacy previews execution" bug
// https://github.com/pointfreeco/sqlite-data/pull/60
@usableFromInline
override init(db: Database, query: QueryFragment) throws {
try super.init(db: db, query: query)
override init(db: Database, prepared: PreparedQuery, cached: Bool) throws {
try super.init(db: db, prepared: prepared, cached: cached)
}

@inlinable
Expand All @@ -164,8 +170,8 @@ final class QueryVoidCursor: QueryCursor<Void> {
// NB: Required to workaround a "Legacy previews execution" bug
// https://github.com/pointfreeco/sqlite-data/pull/60
@usableFromInline
override init(db: Database, query: QueryFragment) throws {
try super.init(db: db, query: query)
override init(db: Database, prepared: PreparedQuery, cached: Bool) throws {
try super.init(db: db, prepared: prepared, cached: cached)
}

@inlinable
Expand All @@ -175,15 +181,38 @@ final class QueryVoidCursor: QueryCursor<Void> {
}
}

extension Database {
@inlinable
func prepare(query: QueryFragment) throws -> (GRDB.Statement, SQLiteQueryDecoder) {
@usableFromInline
struct PreparedQuery: Hashable, Sendable {
@usableFromInline
let sql: String

@usableFromInline
let bindings: [QueryBinding]

@usableFromInline
init(_ query: QueryFragment) {
var (sql, bindings) = query.prepare { _ in "?" }
if sql.isEmpty {
sql = "SELECT 1 WHERE 0 -- Empty query generated by StructuredQueries"
}
let statement = try makeStatement(sql: sql)
for (index, binding) in zip(Int32(1)..., bindings) {
self.sql = sql
self.bindings = bindings
}
}

extension Database {
@usableFromInline
func prepare(
_ prepared: PreparedQuery, cached: Bool
) throws -> (GRDB.Statement, SQLiteQueryDecoder) {
let statement: GRDB.Statement
if cached {
statement = try cachedStatement(sql: prepared.sql)
sqlite3_reset(statement.sqliteStatement)
} else {
statement = try makeStatement(sql: prepared.sql)
}
for (index, binding) in zip(Int32(1)..., prepared.bindings) {
try binding.bind(to: statement.sqliteStatement, at: index)
}
return (
Expand Down
29 changes: 16 additions & 13 deletions Sources/SQLiteData/StructuredQueries+GRDB/Statement+GRDB.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extension StructuredQueriesCore.Statement {
/// - Parameter db: A database connection.
@inlinable
public func execute(_ db: Database) throws where QueryValue == () {
try QueryVoidCursor(db: db, query: query).next()
try QueryVoidCursor(db: db, query: query, cached: true).next()
}

/// Returns an array of all values fetched from the database.
Expand All @@ -41,7 +41,7 @@ extension StructuredQueriesCore.Statement {
@inlinable
public func fetchAll(_ db: Database) throws -> [QueryValue.QueryOutput]
where QueryValue: QueryRepresentable {
let cursor = try QueryValueCursor<QueryValue>(db: db, query: query)
let cursor = try QueryValueCursor<QueryValue>(db: db, query: query, cached: true)
var output: [QueryValue.QueryOutput] = []
try cursor.forEach { output.append($0) }
return output
Expand Down Expand Up @@ -69,7 +69,7 @@ extension StructuredQueriesCore.Statement {
@inlinable
public func fetchOne(_ db: Database) throws -> QueryValue.QueryOutput?
where QueryValue: QueryRepresentable {
try fetchCursor(db).next()
try QueryValueCursor<QueryValue>(db: db, query: query, cached: true).next()
}

/// Returns a cursor to all values fetched from the database.
Expand All @@ -92,7 +92,7 @@ extension StructuredQueriesCore.Statement {
@inlinable
public func fetchCursor(_ db: Database) throws -> QueryCursor<QueryValue.QueryOutput>
where QueryValue: QueryRepresentable {
try QueryValueCursor<QueryValue>(db: db, query: query)
try QueryValueCursor<QueryValue>(db: db, query: query, cached: false)
}
}

Expand All @@ -108,7 +108,7 @@ extension StructuredQueriesCore.Statement {
_ db: Database
) throws -> [(repeat (each Value).QueryOutput)]
where QueryValue == (repeat each Value) {
let cursor = try fetchCursor(db)
let cursor = try QueryPackCursor<repeat each Value>(db: db, query: query, cached: true)
return try Array(cursor)
}

Expand All @@ -122,7 +122,7 @@ extension StructuredQueriesCore.Statement {
_ db: Database
) throws -> (repeat (each Value).QueryOutput)?
where QueryValue == (repeat each Value) {
let cursor = try fetchCursor(db)
let cursor = try QueryPackCursor<repeat each Value>(db: db, query: query, cached: true)
return try cursor.next()
}

Expand All @@ -136,7 +136,7 @@ extension StructuredQueriesCore.Statement {
_ db: Database
) throws -> QueryCursor<(repeat (each Value).QueryOutput)>
where QueryValue == (repeat each Value) {
try QueryPackCursor<repeat each Value>(db: db, query: query)
try QueryPackCursor<repeat each Value>(db: db, query: query, cached: false)
}
}

Expand All @@ -160,7 +160,7 @@ extension SelectStatement where QueryValue == (), Joins == () {
@_documentation(visibility: private)
@inlinable
public func fetchAll(_ db: Database) throws -> [From.QueryOutput] {
let cursor = try QueryValueCursor<From>(db: db, query: query)
let cursor = try QueryValueCursor<From>(db: db, query: query, cached: true)
var output: [From.QueryOutput] = []
try cursor.forEach { output.append($0) }
return output
Expand All @@ -173,7 +173,7 @@ extension SelectStatement where QueryValue == (), Joins == () {
@_documentation(visibility: private)
@inlinable
public func fetchOne(_ db: Database) throws -> From.QueryOutput? {
try asSelect().limit(1).fetchCursor(db).next()
try QueryValueCursor<From>(db: db, query: asSelect().limit(1).query, cached: true).next()
}

/// Returns a cursor to all values fetched from the database.
Expand All @@ -183,7 +183,7 @@ extension SelectStatement where QueryValue == (), Joins == () {
@_documentation(visibility: private)
@inlinable
public func fetchCursor(_ db: Database) throws -> QueryCursor<From.QueryOutput> {
try QueryValueCursor<From>(db: db, query: query)
try QueryValueCursor<From>(db: db, query: query, cached: false)
}
}

Expand Down Expand Up @@ -218,7 +218,7 @@ extension SelectStatement where QueryValue == () {
_ db: Database
) throws -> [(From.QueryOutput, repeat (each J).QueryOutput)]
where Joins == (repeat each J) {
try Array(fetchCursor(db))
try Array(QueryPackCursor<From, repeat each J>(db: db, query: query, cached: true))
}

/// Returns a single value fetched from the database.
Expand All @@ -231,7 +231,10 @@ extension SelectStatement where QueryValue == () {
_ db: Database
) throws -> (From.QueryOutput, repeat (each J).QueryOutput)?
where Joins == (repeat each J) {
try asSelect().limit(1).fetchCursor(db).next()
try QueryPackCursor<From, repeat each J>(
db: db, query: asSelect().limit(1).query, cached: true
)
.next()
}

/// Returns a cursor to all values fetched from the database.
Expand All @@ -244,6 +247,6 @@ extension SelectStatement where QueryValue == () {
_ db: Database
) throws -> QueryCursor<(From.QueryOutput, repeat (each J).QueryOutput)>
where Joins == (repeat each J) {
try QueryPackCursor<From, repeat each J>(db: db, query: query)
try QueryPackCursor<From, repeat each J>(db: db, query: query, cached: false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,7 @@ private func sectionedResults<Value: QueryRepresentable, Key: QueryRepresentable
query: QueryFragment
) throws -> ResultsSectionCollection<Value.QueryOutput, Key.QueryOutput>
where Key.QueryOutput: Hashable {
try ResultsSectionCollection(cursor: QuerySectionedCursor<Value, Key>(db: db, query: query))
try ResultsSectionCollection(
cursor: QuerySectionedCursor<Value, Key>(db: db, query: query, cached: true)
)
}
Loading