File: LMDB%2BTransaction.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (208 lines) | stat: -rw-r--r-- 7,084 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*
 This source file is part of the Swift.org open source project

 Copyright (c) 2021 Apple Inc. and the Swift project authors
 Licensed under Apache License v2.0 with Runtime Library Exception

 See https://swift.org/LICENSE.txt for license information
 See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import Foundation
import CLMDB

extension LMDB {
    
    /**
    A base transaction for LMDB.
     */
    class Transaction {
        
        /// Queue to serialize write access.
        private let queue = DispatchQueue(label: "org.swift.docc.LMDB.serialize")
        
        /// The opaque pointer of the transaction.
        var opaquePointer: OpaquePointer? = nil
        
        /// The environment the transaction is executed.
        let environment: Environment
        
        /// The parent transaction.
        weak var parent: Transaction? = nil
        
        /// Defines if a transaction is read only or not.
        public let readOnly: Bool
        
        /// Internal variable holding information if the transaction has completed or not.
        private var _completed : Bool = false
        
        /// A boolean indicating if a transaction has been completed or not. Note: thread safe.
        public var completed: Bool {
            set {
                queue.sync {
                    _completed = newValue
                }
            }
            get {
                var value: Bool!
                queue.sync {
                    value = _completed
                }
                return value
            }
        }
        
        public init(environment: Environment, parent: Transaction? = nil, readOnly: Bool = false) {
            self.environment = environment
            self.readOnly = environment.readOnly || readOnly
            self.parent = parent
        }
        
        /**
         Run a given block as transaction.
         */
        public func run<R>(_ block: ((Transaction) throws -> R)) throws -> R {
            do {
                try begin()
                let result = try block(self)
                try commit()
                return result
            } catch {
                abort()
                throw error
            }
        }
        
        /// Begins a transaction.
        public func begin() throws {
            let flags = readOnly ? UInt32(MDB_RDONLY) : 0

            let result = mdb_txn_begin(environment.opaquePointer, parent?.opaquePointer ?? nil, flags, &opaquePointer)
            guard result == 0 else {
                throw Error(errorCode: result)
            }
        }
        
        /// Renews a read-only transaction.
        public func renew() throws {
            assert(self.readOnly)
            assert(!self.completed)

            mdb_txn_reset(opaquePointer)

            let result = mdb_txn_renew(opaquePointer)
            guard result == 0 else {
                throw Error(errorCode: result)
            }
        }
        
        /// Commit current transaction.
        public func commit() throws {
            assert(!completed)
            completed = true
            
            let result = mdb_txn_commit(opaquePointer)
            guard result == 0 else {
                throw Error(errorCode: result)
            }
        }
        
        /// Abort current transaction.
        public func abort() {
            if !completed {
                completed = true
                mdb_txn_abort(opaquePointer)
            }
        }
        
        deinit {
            if !completed {
                mdb_txn_abort(opaquePointer)
            }
        }
        
        // MARK: - Convenience
        
        /**
         Get a value from a given database with given key, converting it to the given type.
         
         - Parameters:
            - type: The expected type of the returned object.
            - key: The key of the value associated with.
            - db: The database to look for the value.
         - Returns: The value related to the given key, if existing.
         */
        public func get<Value: LMDBData>(type: Value.Type, forKey key: some LMDBData, from db: Database) -> Value?{
            return key.read { (keyPointer) in
                var keyValue = MDB_val(unsafeRawBufferPointer: keyPointer)
                var value = MDB_val()
                let result = mdb_get(self.opaquePointer, db.handle, &keyValue, &value)
                
                guard result != MDB_NOTFOUND else { return nil }

                return Value(data: UnsafeRawBufferPointer(mdbValue: value))
            }
        }
        
        /**
         Insert a value with the given key inside a database.
         
         - Parameters:
            - key: The key of the value.
            - value: The value to be insert inside the database.
            - db: The database to insert the value into.
         - Throws: An error in case a read-only transaction has been used, an invalid parameter has been specified, the database is full or the transaction has too many dirty pages to complete.
         */
        public func put(key: some LMDBData, value: some LMDBData, in db: Database, flags: Database.WriteFlags = []) throws {
            try key.read() { (key: UnsafeRawBufferPointer) in
                try value.read() { (value: UnsafeRawBufferPointer) in
                    var key = MDB_val(unsafeRawBufferPointer: key)
                    var val = MDB_val(unsafeRawBufferPointer: value)
                    let result = mdb_put(self.opaquePointer, db.handle, &key, &val, UInt32(flags.rawValue))
                    guard result == 0 else {
                        throw Error(errorCode: result)
                    }
                }
            }
        }
        
        /**
        Delete a value from the database with a given key.
        
        - Parameters:
           - key: The key of the value.
           - db: The database form which the value has to be deleted.
        - Throws: An error in case a read-only transaction has been used or an invalid parameter has been specified.
        */
        public func delete(_ key: some LMDBData, from db: Database) throws {
            try key.read() { key in
                var keyVal = MDB_val(unsafeRawBufferPointer:key)
                let result = mdb_del(self.opaquePointer, db.handle, &keyVal, nil)
                guard result == 0 else {
                    throw Error(errorCode: result)
                }
            }
        }
        
    }
}

// MARK: - Utilities

internal extension UnsafeRawBufferPointer {
        
    /// Initialize `UnsafeRawBufferPointer` from the current MDB_val.
    init(mdbValue: MDB_val) {
        self.init(start: mdbValue.mv_data, count: mdbValue.mv_size)
    }
    
}

internal extension MDB_val {
    
    /// Initialize a `MDB_val` from the current pointer.
    init(unsafeRawBufferPointer: UnsafeRawBufferPointer) {
        self.init(mv_size: unsafeRawBufferPointer.count, mv_data: UnsafeMutableRawPointer(mutating: unsafeRawBufferPointer.baseAddress))
    }
    
}