Skip to content

Commit 23f4254

Browse files
committed
Merge branch 'development'
2 parents e02f2c8 + 2df6a17 commit 23f4254

21 files changed

+394
-324
lines changed

.github/workflows/CI.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
name: "macOS 10.15, Xcode 12.4, Swift 5.3"
5858
shell: "/bin/zsh {0}"
5959
steps:
60-
- uses: actions/checkout@v2
60+
- uses: actions/checkout@v3
6161
- name: ${{ matrix.name }}
6262
run: make test_framework_GRDBOSX_maxSwift
6363
iOS:
@@ -77,7 +77,7 @@ jobs:
7777
name: "macOS 10.15, Xcode 12.4, Swift 5.3"
7878
shell: "/bin/zsh {0}"
7979
steps:
80-
- uses: actions/checkout@v2
80+
- uses: actions/checkout@v3
8181
- name: ${{ matrix.name }}
8282
run: make test_framework_GRDBiOS_maxTarget_maxSwift
8383
SPM:
@@ -93,7 +93,7 @@ jobs:
9393
name: "macOS 12, Xcode 13.3.1, Swift 5.6"
9494
shell: "/bin/zsh {0}"
9595
steps:
96-
- uses: actions/checkout@v2
96+
- uses: actions/checkout@v3
9797
- name: ${{ matrix.name }}
9898
run: make test_SPM test_install_SPM
9999
SQLCipher3:
@@ -109,7 +109,7 @@ jobs:
109109
name: "macOS 12, Xcode 13.3.1, Swift 5.6"
110110
shell: "/bin/zsh {0}"
111111
steps:
112-
- uses: actions/checkout@v2
112+
- uses: actions/checkout@v3
113113
- name: ${{ matrix.name }}
114114
run: make test_framework_SQLCipher3Encrypted
115115
SQLCipher4:
@@ -129,7 +129,7 @@ jobs:
129129
name: "macOS 10.15, Xcode 12.4, Swift 5.3"
130130
shell: "/bin/zsh {0}"
131131
steps:
132-
- uses: actions/checkout@v2
132+
- uses: actions/checkout@v3
133133
- name: ${{ matrix.name }}
134134
run: make test_framework_SQLCipher4Encrypted
135135
CustomSQLite:
@@ -149,7 +149,7 @@ jobs:
149149
name: "macOS 10.15, Xcode 12.4, Swift 5.3"
150150
shell: "/bin/zsh {0}"
151151
steps:
152-
- uses: actions/checkout@v2
152+
- uses: actions/checkout@v3
153153
- name: ${{ matrix.name }}
154154
run: make test_framework_GRDBCustomSQLiteOSX
155155
XCFramework:
@@ -165,7 +165,7 @@ jobs:
165165
name: "macOS 12, Xcode 13.3.1, Swift 5.6"
166166
shell: "/bin/zsh {0}"
167167
steps:
168-
- uses: actions/checkout@v2
168+
- uses: actions/checkout@v3
169169
- name: ${{ matrix.name }}
170170
run: make test_archive_GRDBOSX_xcframework
171171

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
77

88
#### 5.x Releases
99

10+
- `5.26.x` Releases - [5.26.0](#5260)
1011
- `5.25.x` Releases - [5.25.0](#5250)
1112
- `5.24.x` Releases - [5.24.0](#5240) | [5.24.1](#5241)
1213
- `5.23.x` Releases - [5.23.0](#5230)
@@ -92,6 +93,14 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
9293

9394
---
9495

96+
## 5.26.0
97+
98+
Released July 9, 2022 • [diff](https://github.com/groue/GRDB.swift/compare/v5.25.0...v5.26.0)
99+
100+
- **New**: [#1248](https://github.com/groue/GRDB.swift/pull/1248) by [@groue](https://github.com/groue): Avoid double notification of the initial value for ValueObservation on DatabasePool
101+
- **New**: [#1253](https://github.com/groue/GRDB.swift/pull/1253) by [@george-signal](https://github.com/george-signal): Make memory management optional
102+
- **Fixed**: DatabasePool no longer prevents database reads when UIKit posts `UIApplication.didReceiveMemoryWarningNotification` (see [#1253](https://github.com/groue/GRDB.swift/pull/1253) for more information).
103+
95104
## 5.25.0
96105

97106
Released June 11, 2022 • [diff](https://github.com/groue/GRDB.swift/compare/v5.24.1...v5.25.0)

Documentation/AssociationsBasics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,7 @@ let sortedAuthor = Book.author.order(Column("name"))
15101510
let request = Book.including(required: sortedAuthor)
15111511
```
15121512

1513-
When you sort both the base record on the associated record, the request is sorted on the base record first, and on the associated record next:
1513+
When you sort both the base record and the associated record, the request is sorted on the base record first, and on the associated record next:
15141514

15151515
```swift
15161516
// SELECT book.*, person.*

Documentation/FullTextSearch.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ let pattern = FTS3Pattern(matchingAnyTokenIn: "") // nil
304304
let pattern = FTS3Pattern(matchingAnyTokenIn: "*") // nil
305305
```
306306

307-
FTS3Pattern are regular [values](../README.md#values). You can use them as query [arguments](http://groue.github.io/GRDB.swift/docs/5.25/Structs/StatementArguments.html):
307+
FTS3Pattern are regular [values](../README.md#values). You can use them as query [arguments](http://groue.github.io/GRDB.swift/docs/5.26/Structs/StatementArguments.html):
308308

309309
```swift
310310
let documents = try Document.fetchAll(db,
@@ -587,7 +587,7 @@ let pattern = FTS5Pattern(matchingAnyTokenIn: "") // nil
587587
let pattern = FTS5Pattern(matchingAnyTokenIn: "*") // nil
588588
```
589589

590-
FTS5Pattern are regular [values](../README.md#values). You can use them as query [arguments](http://groue.github.io/GRDB.swift/docs/5.25/Structs/StatementArguments.html):
590+
FTS5Pattern are regular [values](../README.md#values). You can use them as query [arguments](http://groue.github.io/GRDB.swift/docs/5.26/Structs/StatementArguments.html):
591591

592592
```swift
593593
let documents = try Document.fetchAll(db,

Documentation/Migrations.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ try dbQueue.read { db in
7777
}
7878
```
7979

80-
See the [DatabaseMigrator reference](http://groue.github.io/GRDB.swift/docs/5.25/Structs/DatabaseMigrator.html) for more migrator methods.
80+
See the [DatabaseMigrator reference](http://groue.github.io/GRDB.swift/docs/5.26/Structs/DatabaseMigrator.html) for more migrator methods.
8181

8282

8383
## The `eraseDatabaseOnSchemaChange` Option

GRDB.swift.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'GRDB.swift'
3-
s.version = '5.25.0'
3+
s.version = '5.26.0'
44

55
s.license = { :type => 'MIT', :file => 'LICENSE' }
66
s.summary = 'A toolkit for SQLite databases, with a focus on application development.'

GRDB.xcodeproj/project.pbxproj

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,10 @@
733733
56B14E831D4DAE54000BF4A3 /* RowFromDictionaryLiteralTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B14E7E1D4DAE54000BF4A3 /* RowFromDictionaryLiteralTests.swift */; };
734734
56B6EF56208CB4E3002F0ACB /* ColumnExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B6EF55208CB4E3002F0ACB /* ColumnExpressionTests.swift */; };
735735
56B6EF57208CB4E3002F0ACB /* ColumnExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B6EF55208CB4E3002F0ACB /* ColumnExpressionTests.swift */; };
736+
56B7EE832863781300C0525F /* WALSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B7EE822863781300C0525F /* WALSnapshot.swift */; };
737+
56B7EE842863781300C0525F /* WALSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B7EE822863781300C0525F /* WALSnapshot.swift */; };
738+
56B7EE852863781300C0525F /* WALSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B7EE822863781300C0525F /* WALSnapshot.swift */; };
739+
56B7EE862863781300C0525F /* WALSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B7EE822863781300C0525F /* WALSnapshot.swift */; };
736740
56B7F42A1BE14A1900E39BBF /* CGFloatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B7F4291BE14A1900E39BBF /* CGFloatTests.swift */; };
737741
56B7F43A1BEB42D500E39BBF /* Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B7F4391BEB42D500E39BBF /* Migration.swift */; };
738742
56B7F43B1BEB42D500E39BBF /* Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B7F4391BEB42D500E39BBF /* Migration.swift */; };
@@ -1595,6 +1599,7 @@
15951599
56B021C81D8C0D3900B239BB /* MutablePersistableRecordPersistenceConflictPolicyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutablePersistableRecordPersistenceConflictPolicyTests.swift; sourceTree = "<group>"; };
15961600
56B14E7E1D4DAE54000BF4A3 /* RowFromDictionaryLiteralTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowFromDictionaryLiteralTests.swift; sourceTree = "<group>"; };
15971601
56B6EF55208CB4E3002F0ACB /* ColumnExpressionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnExpressionTests.swift; sourceTree = "<group>"; };
1602+
56B7EE822863781300C0525F /* WALSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WALSnapshot.swift; sourceTree = "<group>"; };
15981603
56B7F4291BE14A1900E39BBF /* CGFloatTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGFloatTests.swift; sourceTree = "<group>"; };
15991604
56B7F4391BEB42D500E39BBF /* Migration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Migration.swift; sourceTree = "<group>"; };
16001605
56B86E72220FF4E000524C16 /* SQLLiteralTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLLiteralTests.swift; sourceTree = "<group>"; };
@@ -2375,8 +2380,9 @@
23752380
56A238781B9C75030082EB20 /* Statement.swift */,
23762381
566B912A1FA4D0CC0012D5B0 /* StatementAuthorizer.swift */,
23772382
560D923F1C672C3E00F4F92B /* StatementColumnConvertible.swift */,
2378-
5605F1471C672E4000235C62 /* Support */,
23792383
566B91321FA4D3810012D5B0 /* TransactionObserver.swift */,
2384+
56B7EE822863781300C0525F /* WALSnapshot.swift */,
2385+
5605F1471C672E4000235C62 /* Support */,
23802386
);
23812387
path = Core;
23822388
sourceTree = "<group>";
@@ -2976,6 +2982,7 @@
29762982
565490D81D5AE252005622CB /* DatabaseMigrator.swift in Sources */,
29772983
5656A8AF2295BFD7001FF3FF /* TableRecord+Association.swift in Sources */,
29782984
565490C21D5AE236005622CB /* Row.swift in Sources */,
2985+
56B7EE852863781300C0525F /* WALSnapshot.swift in Sources */,
29792986
565490C71D5AE236005622CB /* StatementColumnConvertible.swift in Sources */,
29802987
565490D91D5AE252005622CB /* Migration.swift in Sources */,
29812988
56CEB5001EAA2F4D00BFAF62 /* FTS3.swift in Sources */,
@@ -3145,6 +3152,7 @@
31453152
5671FC231DA3CAC9003BF4FF /* FTS3TokenizerDescriptor.swift in Sources */,
31463153
56D51D031EA789FA0074638A /* FetchableRecord+TableRecord.swift in Sources */,
31473154
56DDDDF5242F87C60025E381 /* ValueObservationScheduler.swift in Sources */,
3155+
56B7EE842863781300C0525F /* WALSnapshot.swift in Sources */,
31483156
560D924C1C672C4B00F4F92B /* TableRecord.swift in Sources */,
31493157
56A2FA3B24424F4700E97D23 /* Export.swift in Sources */,
31503158
569BBA4F229170F900478429 /* Inflections+English.swift in Sources */,
@@ -3753,6 +3761,7 @@
37533761
AAA4DCA5230F1E0600C74B15 /* FTS3TokenizerDescriptor.swift in Sources */,
37543762
AAA4DCA6230F1E0600C74B15 /* FetchableRecord+TableRecord.swift in Sources */,
37553763
56DDDDF7242F87C70025E381 /* ValueObservationScheduler.swift in Sources */,
3764+
56B7EE862863781300C0525F /* WALSnapshot.swift in Sources */,
37563765
AAA4DCA7230F1E0600C74B15 /* TableRecord.swift in Sources */,
37573766
56A2FA3D24424F4800E97D23 /* Export.swift in Sources */,
37583767
AAA4DCA8230F1E0600C74B15 /* Inflections+English.swift in Sources */,
@@ -4125,6 +4134,7 @@
41254134
569BBA4E229170F800478429 /* Inflections+English.swift in Sources */,
41264135
5656A81E2295B12F001FF3FF /* SQLAssociation.swift in Sources */,
41274136
5617294E223533F40006E219 /* EncodableRecord.swift in Sources */,
4137+
56B7EE832863781300C0525F /* WALSnapshot.swift in Sources */,
41284138
5698AD181DAAD17A0056AF8C /* FTS5Tokenizer.swift in Sources */,
41294139
56A2FA3624424D2A00E97D23 /* Export.swift in Sources */,
41304140
56B964B11DA51D010002DA19 /* FTS5TokenizerDescriptor.swift in Sources */,

GRDB/Core/Configuration.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,15 @@ public struct Configuration {
275275
///
276276
/// Default: nil
277277
public var writeTargetQueue: DispatchQueue? = nil
278-
278+
279+
#if os(iOS)
280+
/// Sets whether GRDB will release memory when entering the background or
281+
/// upon receiving a memory warning in iOS.
282+
///
283+
/// Default: true
284+
public var automaticMemoryManagement = true
285+
#endif
286+
279287
// MARK: - Factory Configuration
280288

281289
/// Creates a factory configuration

GRDB/Core/Database.swift

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -553,38 +553,6 @@ public final class Database: CustomStringConvertible, CustomDebugStringConvertib
553553
_readOnlyDepth > 0 || configuration.readonly
554554
}
555555

556-
// MARK: - Snapshots
557-
558-
#if SQLITE_ENABLE_SNAPSHOT
559-
/// Returns a snapshot that must be freed with `sqlite3_snapshot_free`.
560-
///
561-
/// See <https://www.sqlite.org/c3ref/snapshot.html>
562-
func takeVersionSnapshot() throws -> UnsafeMutablePointer<sqlite3_snapshot> {
563-
var snapshot: UnsafeMutablePointer<sqlite3_snapshot>?
564-
let code = withUnsafeMutablePointer(to: &snapshot) {
565-
sqlite3_snapshot_get(sqliteConnection, "main", $0)
566-
}
567-
guard code == SQLITE_OK else {
568-
throw DatabaseError(resultCode: code, message: lastErrorMessage)
569-
}
570-
if let snapshot = snapshot {
571-
return snapshot
572-
} else {
573-
throw DatabaseError(resultCode: .SQLITE_INTERNAL) // WTF SQLite?
574-
}
575-
}
576-
577-
func wasChanged(since initialSnapshot: UnsafeMutablePointer<sqlite3_snapshot>) throws -> Bool {
578-
let secondSnapshot = try takeVersionSnapshot()
579-
defer {
580-
sqlite3_snapshot_free(secondSnapshot)
581-
}
582-
let cmp = sqlite3_snapshot_cmp(initialSnapshot, secondSnapshot)
583-
assert(cmp <= 0, "Unexpected snapshot ordering")
584-
return cmp < 0
585-
}
586-
#endif
587-
588556
// MARK: - Recording of the selected region
589557

590558
func recordingSelection<T>(_ region: inout DatabaseRegion, _ block: () throws -> T) rethrows -> T {
@@ -1216,7 +1184,8 @@ public final class Database: CustomStringConvertible, CustomDebugStringConvertib
12161184

12171185
// MARK: - Memory Management
12181186

1219-
func releaseMemory() {
1187+
public func releaseMemory() {
1188+
SchedulingWatchdog.preconditionValidQueue(self)
12201189
sqlite3_db_release_memory(sqliteConnection)
12211190
schemaCache.clear()
12221191
internalStatementCache.clear()

GRDB/Core/DatabasePool.swift

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ public final class DatabasePool: DatabaseWriter {
103103
// Be a nice iOS citizen, and don't consume too much memory
104104
// See https://github.com/groue/GRDB.swift/#memory-management
105105
#if os(iOS)
106-
setupMemoryManagement()
106+
if configuration.automaticMemoryManagement {
107+
setupMemoryManagement()
108+
}
107109
#endif
108110
}
109111

@@ -154,13 +156,6 @@ public final class DatabasePool: DatabaseWriter {
154156

155157
return configuration
156158
}
157-
158-
/// Blocks the current thread until all database connections have
159-
/// executed the *body* block.
160-
fileprivate func forEachConnection(_ body: (Database) -> Void) {
161-
writer.sync(body)
162-
readerPool?.forEach { $0.sync(body) }
163-
}
164159
}
165160

166161
#if swift(>=5.6) && canImport(_Concurrency)
@@ -172,19 +167,49 @@ extension DatabasePool {
172167

173168
// MARK: - Memory management
174169

175-
/// Free as much memory as possible.
170+
/// Frees as much memory as possible, by disposing non-essential memory from
171+
/// the writer connection, and closing all reader connections.
172+
///
173+
/// This method is synchronous, and blocks the current thread until all
174+
/// database accesses are completed.
176175
///
177-
/// This method blocks the current thread until all database accesses
178-
/// are completed.
176+
/// - warning: This method can prevent concurrent reads from executing,
177+
/// until it returns. Prefer ``releaseMemoryEventually()`` if you intend
178+
/// to keep on using the database while releasing memory.
179179
public func releaseMemory() {
180180
// Release writer memory
181181
writer.sync { $0.releaseMemory() }
182-
// Release readers memory by closing all connections
182+
183+
// Release readers memory by closing all connections.
184+
//
185+
// We must use a barrier in order to guarantee that memory has been
186+
// freed (reader connections closed) when the method exits, as
187+
// documented.
188+
//
189+
// Without the barrier, connections would only close _eventually_ (after
190+
// their eventual concurrent jobs have completed).
183191
readerPool?.barrier {
184192
readerPool?.removeAll()
185193
}
186194
}
187195

196+
/// Eventually frees as much memory as possible, by disposing non-essential
197+
/// memory from the writer connection, and closing all reader connections.
198+
///
199+
/// Unlike ``releaseMemory()``, this method does not prevent concurrent
200+
/// database accesses when it is executing. But it does not notify when
201+
/// non-essential memory has been freed.
202+
public func releaseMemoryEventually() {
203+
// Release readers memory by eventually closing all reader connections
204+
// (they will close after their current jobs have completed).
205+
readerPool?.removeAll()
206+
207+
// Release writer memory eventually.
208+
writer.async { db in
209+
db.releaseMemory()
210+
}
211+
}
212+
188213
#if os(iOS)
189214
/// Listens to UIApplicationDidEnterBackgroundNotification and
190215
/// UIApplicationDidReceiveMemoryWarningNotification in order to release
@@ -216,22 +241,28 @@ extension DatabasePool {
216241

217242
let task: UIBackgroundTaskIdentifier = application.beginBackgroundTask(expirationHandler: nil)
218243
if task == .invalid {
219-
// Perform releaseMemory() synchronously.
244+
// Release memory synchronously
220245
releaseMemory()
221246
} else {
222-
// Perform releaseMemory() asynchronously.
223-
DispatchQueue.global().async {
224-
self.releaseMemory()
247+
// Release memory eventually.
248+
//
249+
// We don't know when reader connections will be closed (because
250+
// they may be currently in use), so we don't quite know when
251+
// reader memory will be freed (which would be the ideal timing for
252+
// ending our background task).
253+
//
254+
// So let's just end the background task after the writer connection
255+
// has freed its memory. That's better than nothing.
256+
releaseMemoryEventually()
257+
writer.async { _ in
225258
application.endBackgroundTask(task)
226259
}
227260
}
228261
}
229262

230263
@objc
231264
private func applicationDidReceiveMemoryWarning(_ notification: NSNotification) {
232-
DispatchQueue.global().async {
233-
self.releaseMemory()
234-
}
265+
releaseMemoryEventually()
235266
}
236267
#endif
237268
}

0 commit comments

Comments
 (0)