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
17 changes: 17 additions & 0 deletions Documentation/Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- [In-Memory Databases](#in-memory-databases)
- [URI parameters](#uri-parameters)
- [Thread-Safety](#thread-safety)
- [Closing a Connection](#closing-a-connection)
- [Building Type-Safe SQL](#building-type-safe-sql)
- [Expressions](#expressions)
- [Compound Expressions](#compound-expressions)
Expand Down Expand Up @@ -490,6 +491,22 @@ db.busyHandler({ tries in
> errors, you may be trying to access the same database simultaneously from
> multiple connections.

#### Closing a Connection

Call `close()` when you need the database connection closed before the
`Connection` goes out of scope—for example, before removing a temporary
database file from disk.

```swift
try db.close()
```

Release prepared statements, BLOB handles, and [backup](#online-database-backup)
objects before closing; otherwise `close()` may throw.

> _Note:_ On iOS, call `close()` (and handle any errors) before deleting temporary
> database files to avoid filesystem warnings.


## Building Type-Safe SQL

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ IOS_VERSION = 16.4
SWIFTLINT_VERSION=0.63.1
SWIFTLINT=bin/swiftlint-$(SWIFTLINT_VERSION)
SWIFTLINT_URL=https://github.com/realm/SwiftLint/releases/download/$(SWIFTLINT_VERSION)/portable_swiftlint.zip
XCBEAUTIFY_VERSION=3.1.2
XCBEAUTIFY_VERSION=3.2.1
XCBEAUTIFY=bin/xcbeautify-$(XCBEAUTIFY_VERSION)
ifeq ($(shell uname), Linux)
XCBEAUTIFY_PLATFORM=x86_64-unknown-linux-gnu.tar.xz
Expand Down
27 changes: 25 additions & 2 deletions Sources/SQLite/Core/Connection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ public final class Connection {
}
}

public var handle: OpaquePointer { _handle! }
public var handle: OpaquePointer {
precondition(_handle != nil, "connection already closed")
return _handle!
}

fileprivate var _handle: OpaquePointer?

Expand Down Expand Up @@ -161,7 +164,7 @@ public final class Connection {
}

deinit {
sqlite3_close(handle)
try? close()
}

// MARK: -
Expand All @@ -186,6 +189,26 @@ public final class Connection {
Int(sqlite3_total_changes(handle))
}

/// Closes the database connection immediately.
/// Normally not needed: the underlying connection is automatically closed when `Connection` goes out of scope.
/// Callers should release or exhaust prepared statements, BLOB handles, and backup operations before closing. If
/// SQLite reports that the connection is still busy, this method throws and keeps the connection handle open.
public func close() throws {
try sync {
guard let handle = _handle else {
return
}

let resultCode = sqlite3_close(handle)
if resultCode == SQLITE_OK {
_handle = nil
return
}

try check(resultCode)
}
}

/// Whether or not the database will return extended error codes when errors are handled.
public var usesExtendedErrorCodes: Bool = false {
didSet {
Expand Down
29 changes: 29 additions & 0 deletions Tests/SQLiteTests/Core/ConnectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,35 @@ class ConnectionTests: SQLiteTestCase {
XCTAssertTrue(db.readonly)
}

func test_close_closesConnectionAndIsIdempotent() throws {
let db = try Connection()

try db.close()
try db.close()
}

func test_close_whenPreparedStatementIsActiveThrowsBusyAndCanBeRetried() throws {
let db = try Connection()
// open statement
var statement: Statement? = try db.prepare("SELECT 1")

XCTAssertThrowsError(try db.close()) { error in
if case SQLite.Result.error(_, let code, _) = error {
// there's an open statement
XCTAssertEqual(SQLITE_BUSY, code)
} else {
XCTFail("unexpected error: \(error)")
}
}
// db still operational
XCTAssertEqual(1, try db.scalar("SELECT 1") as? Int64)
// statement is closed, no more references
if statement != nil {
statement = nil
}
try db.close()
}

func test_changes_returnsZeroOnNewConnections() {
XCTAssertEqual(0, db.changes)
}
Expand Down
Loading