From e816e10fd649ad0aeda407eeefbcc141d35d4dab Mon Sep 17 00:00:00 2001 From: Dimitri Bouniol Date: Sat, 22 Jul 2023 13:04:46 -0700 Subject: [PATCH 1/4] Updated list of todos for 1.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe7f9b8..a2bc8a3 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ As this project matures towards release, the project will focus on the functiona - Force migration methods - Composite indexes (via macros?) - Cleaning up old resources on disk -- Reversed ranged reads +- Ranged deletes - Controls for the edit history - Helper types to use with SwiftUI/Observability/Combine that can make data available on the main actor and filter and stay up to date - Comprehensive test coverage From a0d14d9937c902e9d97c0f6d35b78c655f2e5831 Mon Sep 17 00:00:00 2001 From: Dimitri Bouniol Date: Sat, 22 Jul 2023 14:20:01 -0700 Subject: [PATCH 2/4] Cleaned up some vestigial type shenanigans --- Sources/CodableDatastore/Datastore/Datastore.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/CodableDatastore/Datastore/Datastore.swift b/Sources/CodableDatastore/Datastore/Datastore.swift index 8641d75..95bc799 100644 --- a/Sources/CodableDatastore/Datastore/Datastore.swift +++ b/Sources/CodableDatastore/Datastore/Datastore.swift @@ -535,7 +535,7 @@ extension Datastore { order: RangeOrder = .ascending, from indexPath: IndexPath> ) -> some TypedAsyncSequence { - let a: AsyncThrowingBackpressureStream = AsyncThrowingBackpressureStream { provider in + AsyncThrowingBackpressureStream { provider in try await self.warmupIfNeeded() try await self.persistence._withTransaction( @@ -573,7 +573,6 @@ extension Datastore { } } } - return a } @_disfavoredOverload From e4fe1fa4f107ad69f9d3c946938634b4c7a439f3 Mon Sep 17 00:00:00 2001 From: Dimitri Bouniol Date: Wed, 6 Mar 2024 03:19:47 -0800 Subject: [PATCH 3/4] Fixed building on CI --- Sources/CodableDatastore/Indexes/UUID+Comparable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CodableDatastore/Indexes/UUID+Comparable.swift b/Sources/CodableDatastore/Indexes/UUID+Comparable.swift index c94ea44..c18d753 100644 --- a/Sources/CodableDatastore/Indexes/UUID+Comparable.swift +++ b/Sources/CodableDatastore/Indexes/UUID+Comparable.swift @@ -8,7 +8,7 @@ import Foundation -#if swift(<5.9) || !os(xrOS) +#if swift(<5.9) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(Linux) || os(Windows) /// Make UUIDs comparable, so that they can be used transparently as an index. /// /// - SeeAlso: https://github.com/apple/swift-foundation/blob/5388acf1d929865d4df97d3c50e4d08bc4c6bdf0/Sources/FoundationEssentials/UUID.swift#L135-L156 From bafb2dedae74dc6493444e7c0a6dfd919aad7c9a Mon Sep 17 00:00:00 2001 From: Dimitri Bouniol Date: Wed, 6 Mar 2024 03:10:45 -0800 Subject: [PATCH 4/4] Fixed an issue where loading from a fresh datastore would throw an error Fixes #163 --- .../Datastore/Datastore.swift | 77 +++++++++++-------- .../DiskPersistenceDatastoreTests.swift | 14 +++- 2 files changed, 57 insertions(+), 34 deletions(-) diff --git a/Sources/CodableDatastore/Datastore/Datastore.swift b/Sources/CodableDatastore/Datastore/Datastore.swift index 95bc799..02bff25 100644 --- a/Sources/CodableDatastore/Datastore/Datastore.swift +++ b/Sources/CodableDatastore/Datastore/Datastore.swift @@ -476,6 +476,9 @@ extension Datastore { return instance } catch DatastoreInterfaceError.instanceNotFound { return nil + } catch DatastoreInterfaceError.datastoreKeyNotFound { + /// There isn't a datastore yet, so no entries would exist either. + return nil } catch { throw error } @@ -496,12 +499,16 @@ extension Datastore { actionName: nil, options: [.readOnly] ) { transaction, _ in - try await transaction.primaryIndexScan(range: range.applying(order), datastoreKey: self.key) { versionData, instanceData in - let entryVersion = try Version(versionData) - let decoder = try await self.decoder(for: entryVersion) - let decodedValue = try await decoder(instanceData) - - try await provider.yield(decodedValue) + do { + try await transaction.primaryIndexScan(range: range.applying(order), datastoreKey: self.key) { versionData, instanceData in + let entryVersion = try Version(versionData) + let decoder = try await self.decoder(for: entryVersion) + let decodedValue = try await decoder(instanceData) + + try await provider.yield(decodedValue) + } + } catch DatastoreInterfaceError.datastoreKeyNotFound { + /// There isn't a datastore yet, so no entries would exist either. Do nothing and let the stream end. } } } @@ -542,34 +549,38 @@ extension Datastore { actionName: nil, options: [.readOnly] ) { transaction, _ in - let isDirectIndex = self.directIndexes.contains { $0.path == indexPath.path } - - if isDirectIndex { - try await transaction.directIndexScan( - range: range.applying(order), - indexName: indexPath.path, - datastoreKey: self.key - ) { versionData, instanceData in - let entryVersion = try Version(versionData) - let decoder = try await self.decoder(for: entryVersion) - let instance = try await decoder(instanceData).instance - - try await provider.yield(instance) - } - } else { - try await transaction.secondaryIndexScan( - range: range.applying(order), - indexName: indexPath.path, - datastoreKey: self.key - ) { (identifier: IdentifierType) in - let persistedEntry = try await transaction.primaryIndexCursor(for: identifier, datastoreKey: self.key) - - let entryVersion = try Version(persistedEntry.versionData) - let decoder = try await self.decoder(for: entryVersion) - let instance = try await decoder(persistedEntry.instanceData).instance - - try await provider.yield(instance) + do { + let isDirectIndex = self.directIndexes.contains { $0.path == indexPath.path } + + if isDirectIndex { + try await transaction.directIndexScan( + range: range.applying(order), + indexName: indexPath.path, + datastoreKey: self.key + ) { versionData, instanceData in + let entryVersion = try Version(versionData) + let decoder = try await self.decoder(for: entryVersion) + let instance = try await decoder(instanceData).instance + + try await provider.yield(instance) + } + } else { + try await transaction.secondaryIndexScan( + range: range.applying(order), + indexName: indexPath.path, + datastoreKey: self.key + ) { (identifier: IdentifierType) in + let persistedEntry = try await transaction.primaryIndexCursor(for: identifier, datastoreKey: self.key) + + let entryVersion = try Version(persistedEntry.versionData) + let decoder = try await self.decoder(for: entryVersion) + let instance = try await decoder(persistedEntry.instanceData).instance + + try await provider.yield(instance) + } } + } catch DatastoreInterfaceError.datastoreKeyNotFound { + /// There isn't a datastore yet, so no entries would exist either. Do nothing and let the stream end. } } } diff --git a/Tests/CodableDatastoreTests/DiskPersistenceDatastoreTests.swift b/Tests/CodableDatastoreTests/DiskPersistenceDatastoreTests.swift index fe73c37..ecc65a5 100644 --- a/Tests/CodableDatastoreTests/DiskPersistenceDatastoreTests.swift +++ b/Tests/CodableDatastoreTests/DiskPersistenceDatastoreTests.swift @@ -75,6 +75,12 @@ final class DiskPersistenceDatastoreTests: XCTestCase { ] ) + let count = try await datastore.count + XCTAssertEqual(count, 0) + + let entry0 = try await datastore.load("0") + XCTAssertNil(entry0) + try await datastore.persist(TestStruct(id: "3", value: "My name is Dimitri")) try await datastore.persist(TestStruct(id: "1", value: "Hello, World!")) try await datastore.persist(TestStruct(id: "2", value: "Twenty Three is Number One")) @@ -96,6 +102,8 @@ final class DiskPersistenceDatastoreTests: XCTestCase { let count = try await datastore.count XCTAssertEqual(count, 3) + let entry0 = try await datastore.load("0") + XCTAssertNil(entry0) let entry1 = try await datastore.load("1") XCTAssertEqual(entry1?.value, "Hello, World!") let entry2 = try await datastore.load("2") @@ -215,6 +223,10 @@ final class DiskPersistenceDatastoreTests: XCTestCase { ] ) + /// Read before persisting anything + var values = try await datastore.load(...).map { $0.value }.reduce(into: []) { $0.append($1) } + XCTAssertEqual(values, []) + for n in 0..<200 { try await datastore.persist(TestStruct(id: n*2, value: "\(n*2)")) } @@ -223,7 +235,7 @@ final class DiskPersistenceDatastoreTests: XCTestCase { XCTAssertEqual(count, 200) /// Simple ranges - var values = try await datastore.load(5..<9).map { $0.value }.reduce(into: []) { $0.append($1) } + values = try await datastore.load(5..<9).map { $0.value }.reduce(into: []) { $0.append($1) } XCTAssertEqual(values, ["6", "8"]) values = try await datastore.load((5..<9).reversed).map { $0.value }.reduce(into: []) { $0.append($1) }