From 622a436589704bab7fda4452013275a131d3bdde Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sun, 10 Sep 2023 10:16:50 -0700 Subject: [PATCH] ndb: add NdbTxn transaction class This will be used for transactions --- damus.xcodeproj/project.pbxproj | 4 ++ nostrdb/NdbTxn.swift | 102 ++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 nostrdb/NdbTxn.swift diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index b69ae057..aa2e59f9 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -156,6 +156,7 @@ 4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */; }; 4C3D52B6298DB4E6001C5831 /* ZapEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */; }; 4C3D52B8298DB5C6001C5831 /* TextEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3D52B7298DB5C6001C5831 /* TextEvent.swift */; }; + 4C3DCC762A9FE9EC0091E592 /* NdbTxn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3DCC752A9FC2030091E592 /* NdbTxn.swift */; }; 4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA63C28FF52D600C48A62 /* bolt11.c */; }; 4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA64028FF553900C48A62 /* hash_u5.c */; }; 4C3EA64428FF558100C48A62 /* sha256.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA64328FF558100C48A62 /* sha256.c */; }; @@ -694,6 +695,7 @@ 4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusState.swift; sourceTree = ""; }; 4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapEvent.swift; sourceTree = ""; }; 4C3D52B7298DB5C6001C5831 /* TextEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEvent.swift; sourceTree = ""; }; + 4C3DCC752A9FC2030091E592 /* NdbTxn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbTxn.swift; sourceTree = ""; }; 4C3EA63B28FF52D600C48A62 /* bolt11.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bolt11.h; sourceTree = ""; }; 4C3EA63C28FF52D600C48A62 /* bolt11.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = bolt11.c; sourceTree = ""; }; 4C3EA63E28FF54BD00C48A62 /* short_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = short_types.h; sourceTree = ""; }; @@ -1816,6 +1818,7 @@ 4CDD1ADF2A6B305F001CD4DF /* NdbTagElem.swift */, 4C478E242A9932C100489948 /* Ndb.swift */, 4CDD1AE12A6B3074001CD4DF /* NdbTagsIterator.swift */, + 4C3DCC752A9FC2030091E592 /* NdbTxn.swift */, 4CE9FBB82A6B3B26007E485C /* nostrdb.c */, 4C4793032A993DB900489948 /* midl.c */, 4C4793002A993B9A00489948 /* mdb.c */, @@ -2478,6 +2481,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4C3DCC762A9FE9EC0091E592 /* NdbTxn.swift in Sources */, 4CEF958D2A9CE650000F901B /* verifier.c in Sources */, 4C32B9342A9AD01A00DC3548 /* NdbProfile.swift in Sources */, 4C32B9332A99845B00DC3548 /* Ndb.swift in Sources */, diff --git a/nostrdb/NdbTxn.swift b/nostrdb/NdbTxn.swift new file mode 100644 index 00000000..b053cd91 --- /dev/null +++ b/nostrdb/NdbTxn.swift @@ -0,0 +1,102 @@ +// +// NdbTx.swift +// damus +// +// Created by William Casarin on 2023-08-30. +// + +import Foundation + +#if TXNDEBUG +fileprivate var txn_count: Int = 0 +#endif + +// Would use struct and ~Copyable but generics aren't supported well +class NdbTxn { + var txn: ndb_txn + private var val: T! + var moved: Bool + + init(ndb: Ndb, with: (NdbTxn) -> T) { + self.txn = ndb_txn() + #if TXNDEBUG + txn_count += 1 + print("opening transaction \(txn_count)") + #endif + let _ = ndb_begin_query(ndb.ndb.ndb, &self.txn) + self.moved = false + self.val = with(self) + } + + init(txn: ndb_txn, val: T) { + self.txn = txn + self.val = val + self.moved = false + } + + /// Only access temporarily! Do not store database references for longterm use. If it's a primitive type you + /// can retrieve this value with `.value` + var unsafeUnownedValue: T { + precondition(!moved) + return val + } + + deinit { + if !moved { + #if TXNDEBUG + txn_count -= 1; + print("closing transaction \(txn_count)") + #endif + ndb_end_query(&self.txn) + } + } + + // functor + func map(_ transform: (T) -> Y) -> NdbTxn { + self.moved = true + return .init(txn: self.txn, val: transform(val)) + } + + // comonad!? + // useful for moving ownership of a transaction to another value + func extend(_ with: (NdbTxn) -> Y) -> NdbTxn { + self.moved = true + return .init(txn: self.txn, val: with(self)) + } +} + +protocol OptionalType { + associatedtype Wrapped + var optional: Wrapped? { get } +} + +extension Optional: OptionalType { + typealias Wrapped = Wrapped + + var optional: Wrapped? { + return self + } +} + +extension NdbTxn where T: OptionalType { + func collect() -> NdbTxn? { + guard let unwrappedVal: T.Wrapped = val.optional else { + return nil + } + self.moved = true + return NdbTxn(txn: self.txn, val: unwrappedVal) + } +} + +extension NdbTxn where T == Bool { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == Bool? { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == Int { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == Int? { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == Double { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == Double? { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == UInt64 { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == UInt64? { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == String { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == String? { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == NoteId? { var value: T { return self.unsafeUnownedValue } } +extension NdbTxn where T == NoteId { var value: T { return self.unsafeUnownedValue } }