cedrodb 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cedrodb-0.3.0/Cargo.lock +253 -0
- cedrodb-0.3.0/Cargo.toml +28 -0
- cedrodb-0.3.0/LICENSE +21 -0
- cedrodb-0.3.0/PKG-INFO +300 -0
- cedrodb-0.3.0/README.md +279 -0
- cedrodb-0.3.0/core/Cargo.toml +15 -0
- cedrodb-0.3.0/core/LICENSE +21 -0
- cedrodb-0.3.0/core/src/advanced.rs +226 -0
- cedrodb-0.3.0/core/src/lib.rs +760 -0
- cedrodb-0.3.0/core/src/maintenance.rs +373 -0
- cedrodb-0.3.0/core/src/query.rs +374 -0
- cedrodb-0.3.0/examples/atomic_outbox.py +42 -0
- cedrodb-0.3.0/examples/offline_forms.py +44 -0
- cedrodb-0.3.0/pyproject.toml +39 -0
- cedrodb-0.3.0/python/cedrodb/__init__.py +446 -0
- cedrodb-0.3.0/python/cedrodb/_native.pyi +24 -0
- cedrodb-0.3.0/python/cedrodb/py.typed +1 -0
- cedrodb-0.3.0/scripts/prepare_core.py +42 -0
- cedrodb-0.3.0/scripts/verify_sdist.py +65 -0
- cedrodb-0.3.0/src/lib.rs +272 -0
- cedrodb-0.3.0/tests/test_backups.py +140 -0
- cedrodb-0.3.0/tests/test_database.py +241 -0
cedrodb-0.3.0/Cargo.lock
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 3
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "cedrodb"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"crc32fast",
|
|
10
|
+
"fs2",
|
|
11
|
+
"serde",
|
|
12
|
+
"serde_json",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[[package]]
|
|
16
|
+
name = "cedrodb-python"
|
|
17
|
+
version = "0.3.0"
|
|
18
|
+
dependencies = [
|
|
19
|
+
"cedrodb",
|
|
20
|
+
"pyo3",
|
|
21
|
+
"serde",
|
|
22
|
+
"serde_json",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[[package]]
|
|
26
|
+
name = "cfg-if"
|
|
27
|
+
version = "1.0.5"
|
|
28
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
29
|
+
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
|
|
30
|
+
|
|
31
|
+
[[package]]
|
|
32
|
+
name = "crc32fast"
|
|
33
|
+
version = "1.5.2"
|
|
34
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
35
|
+
checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78"
|
|
36
|
+
dependencies = [
|
|
37
|
+
"cfg-if",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[[package]]
|
|
41
|
+
name = "fs2"
|
|
42
|
+
version = "0.4.3"
|
|
43
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
44
|
+
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
|
45
|
+
dependencies = [
|
|
46
|
+
"libc",
|
|
47
|
+
"winapi",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[[package]]
|
|
51
|
+
name = "heck"
|
|
52
|
+
version = "0.5.0"
|
|
53
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
54
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
55
|
+
|
|
56
|
+
[[package]]
|
|
57
|
+
name = "itoa"
|
|
58
|
+
version = "1.0.18"
|
|
59
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
60
|
+
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
61
|
+
|
|
62
|
+
[[package]]
|
|
63
|
+
name = "libc"
|
|
64
|
+
version = "0.2.190"
|
|
65
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
66
|
+
checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
|
|
67
|
+
|
|
68
|
+
[[package]]
|
|
69
|
+
name = "memchr"
|
|
70
|
+
version = "2.8.3"
|
|
71
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
72
|
+
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
73
|
+
|
|
74
|
+
[[package]]
|
|
75
|
+
name = "once_cell"
|
|
76
|
+
version = "1.21.4"
|
|
77
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
78
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
79
|
+
|
|
80
|
+
[[package]]
|
|
81
|
+
name = "portable-atomic"
|
|
82
|
+
version = "1.15.0"
|
|
83
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
84
|
+
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
|
|
85
|
+
|
|
86
|
+
[[package]]
|
|
87
|
+
name = "proc-macro2"
|
|
88
|
+
version = "1.0.107"
|
|
89
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
90
|
+
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
|
91
|
+
dependencies = [
|
|
92
|
+
"unicode-ident",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
[[package]]
|
|
96
|
+
name = "pyo3"
|
|
97
|
+
version = "0.30.1"
|
|
98
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
99
|
+
checksum = "229ca13b8a15bb0d1487b81ab975bfcd97513416499a8312011b6036e42e0de4"
|
|
100
|
+
dependencies = [
|
|
101
|
+
"libc",
|
|
102
|
+
"once_cell",
|
|
103
|
+
"portable-atomic",
|
|
104
|
+
"pyo3-build-config",
|
|
105
|
+
"pyo3-ffi",
|
|
106
|
+
"pyo3-macros",
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
[[package]]
|
|
110
|
+
name = "pyo3-build-config"
|
|
111
|
+
version = "0.30.1"
|
|
112
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
113
|
+
checksum = "70130e2b6dd1ee589a8788026a429561ca5c40fa7a2fa7914c2282eddae24700"
|
|
114
|
+
dependencies = [
|
|
115
|
+
"target-lexicon",
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
[[package]]
|
|
119
|
+
name = "pyo3-ffi"
|
|
120
|
+
version = "0.30.1"
|
|
121
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
122
|
+
checksum = "5e7ff73c555c286d212d4ce7e5fbda845a973c4f61bd5f160f5f2c3c2a04d1c4"
|
|
123
|
+
dependencies = [
|
|
124
|
+
"libc",
|
|
125
|
+
"pyo3-build-config",
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
[[package]]
|
|
129
|
+
name = "pyo3-macros"
|
|
130
|
+
version = "0.30.1"
|
|
131
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
132
|
+
checksum = "c46d2e5862c7bb8207e3b639f713dda3c499933444784154c11d013c5a691b60"
|
|
133
|
+
dependencies = [
|
|
134
|
+
"proc-macro2",
|
|
135
|
+
"pyo3-macros-backend",
|
|
136
|
+
"quote",
|
|
137
|
+
"syn",
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
[[package]]
|
|
141
|
+
name = "pyo3-macros-backend"
|
|
142
|
+
version = "0.30.1"
|
|
143
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
144
|
+
checksum = "38b89f066f2ae72a2aef52d1537ca2fcd104440ebfadee06e97aa90738a25b9b"
|
|
145
|
+
dependencies = [
|
|
146
|
+
"heck",
|
|
147
|
+
"proc-macro2",
|
|
148
|
+
"quote",
|
|
149
|
+
"syn",
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
[[package]]
|
|
153
|
+
name = "quote"
|
|
154
|
+
version = "1.0.47"
|
|
155
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
156
|
+
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
|
157
|
+
dependencies = [
|
|
158
|
+
"proc-macro2",
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
[[package]]
|
|
162
|
+
name = "serde"
|
|
163
|
+
version = "1.0.229"
|
|
164
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
165
|
+
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
|
166
|
+
dependencies = [
|
|
167
|
+
"serde_core",
|
|
168
|
+
"serde_derive",
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
[[package]]
|
|
172
|
+
name = "serde_core"
|
|
173
|
+
version = "1.0.229"
|
|
174
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
175
|
+
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
|
176
|
+
dependencies = [
|
|
177
|
+
"serde_derive",
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
[[package]]
|
|
181
|
+
name = "serde_derive"
|
|
182
|
+
version = "1.0.229"
|
|
183
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
184
|
+
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|
185
|
+
dependencies = [
|
|
186
|
+
"proc-macro2",
|
|
187
|
+
"quote",
|
|
188
|
+
"syn",
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
[[package]]
|
|
192
|
+
name = "serde_json"
|
|
193
|
+
version = "1.0.154"
|
|
194
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
195
|
+
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
|
|
196
|
+
dependencies = [
|
|
197
|
+
"itoa",
|
|
198
|
+
"memchr",
|
|
199
|
+
"serde",
|
|
200
|
+
"serde_core",
|
|
201
|
+
"zmij",
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
[[package]]
|
|
205
|
+
name = "syn"
|
|
206
|
+
version = "3.0.9"
|
|
207
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
208
|
+
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
|
|
209
|
+
dependencies = [
|
|
210
|
+
"proc-macro2",
|
|
211
|
+
"quote",
|
|
212
|
+
"unicode-ident",
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
[[package]]
|
|
216
|
+
name = "target-lexicon"
|
|
217
|
+
version = "0.13.5"
|
|
218
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
219
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
220
|
+
|
|
221
|
+
[[package]]
|
|
222
|
+
name = "unicode-ident"
|
|
223
|
+
version = "1.0.27"
|
|
224
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
225
|
+
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
|
|
226
|
+
|
|
227
|
+
[[package]]
|
|
228
|
+
name = "winapi"
|
|
229
|
+
version = "0.3.9"
|
|
230
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
231
|
+
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
232
|
+
dependencies = [
|
|
233
|
+
"winapi-i686-pc-windows-gnu",
|
|
234
|
+
"winapi-x86_64-pc-windows-gnu",
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
[[package]]
|
|
238
|
+
name = "winapi-i686-pc-windows-gnu"
|
|
239
|
+
version = "0.4.0"
|
|
240
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
241
|
+
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
242
|
+
|
|
243
|
+
[[package]]
|
|
244
|
+
name = "winapi-x86_64-pc-windows-gnu"
|
|
245
|
+
version = "0.4.0"
|
|
246
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
247
|
+
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
248
|
+
|
|
249
|
+
[[package]]
|
|
250
|
+
name = "zmij"
|
|
251
|
+
version = "1.0.23"
|
|
252
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
253
|
+
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
cedrodb-0.3.0/Cargo.toml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "cedrodb-python"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
rust-version = "1.85"
|
|
6
|
+
description = "Native Python bindings for the CedroDB embedded database"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
repository = "https://github.com/uppy19d0/cedrodb"
|
|
9
|
+
homepage = "https://www.ltavarez.me/labs/cedrodb"
|
|
10
|
+
publish = false
|
|
11
|
+
include = ["src/**/*.rs", "Cargo.toml", "Cargo.lock", "LICENSE"]
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
|
|
14
|
+
[lib]
|
|
15
|
+
name = "_native"
|
|
16
|
+
crate-type = ["cdylib"]
|
|
17
|
+
|
|
18
|
+
[dependencies]
|
|
19
|
+
cedrodb = { path = "core", version = "=0.3.0" }
|
|
20
|
+
pyo3 = { version = "0.30.1", features = ["abi3-py39"] }
|
|
21
|
+
serde = { version = "1.0", features = ["derive"] }
|
|
22
|
+
serde_json = "1.0"
|
|
23
|
+
|
|
24
|
+
[profile.release]
|
|
25
|
+
lto = "thin"
|
|
26
|
+
strip = "symbols"
|
|
27
|
+
|
|
28
|
+
[workspace]
|
cedrodb-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luis Tavarez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cedrodb-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cedrodb
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
7
|
+
Classifier: Programming Language :: Rust
|
|
8
|
+
Classifier: Topic :: Database :: Database Engines/Servers
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Summary: Embedded Rust document database with atomic batches, indexes and a durable changefeed
|
|
11
|
+
Keywords: database,embedded,document,outbox,changefeed,rust
|
|
12
|
+
Home-Page: https://www.ltavarez.me/labs/cedrodb
|
|
13
|
+
Author: Luis Tavarez
|
|
14
|
+
License: MIT
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
17
|
+
Project-URL: Documentation, https://www.ltavarez.me/labs/cedrodb
|
|
18
|
+
Project-URL: Homepage, https://www.ltavarez.me/labs/cedrodb
|
|
19
|
+
Project-URL: Repository, https://github.com/uppy19d0/cedrodb
|
|
20
|
+
|
|
21
|
+
# CedroDB for Python
|
|
22
|
+
|
|
23
|
+
**Keep application documents, events and recovery history in one local database.**
|
|
24
|
+
CedroDB is an embedded document engine written in Rust, with a native Python API.
|
|
25
|
+
A form submission and its outbox event can commit together, indexed queries find
|
|
26
|
+
pending work, and a durable changefeed lets your application resume processing
|
|
27
|
+
after restarting. Version 0.3 adds integrity verification and portable backups.
|
|
28
|
+
|
|
29
|
+
Use it for offline forms and inspections, desktop application state, local
|
|
30
|
+
automation, or small services that need JSON documents without running a database
|
|
31
|
+
server. The Python package calls the Rust engine directly; it does not spawn a
|
|
32
|
+
process or maintain a second Python storage implementation.
|
|
33
|
+
|
|
34
|
+
**Experimental software.** Active documents and indexes live in RAM, a directory
|
|
35
|
+
has one owning handle/process, and the log currently has no compaction. See the
|
|
36
|
+
limits below before choosing it for important data. There are no performance
|
|
37
|
+
claims or production-readiness guarantees.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
For the 0.3.0 release on PyPI:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
python -m pip install cedrodb==0.3.0
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
PyPI publication requires the project's publisher configuration. If that version
|
|
48
|
+
is not available there yet, obtain its wheel or self-contained source archive
|
|
49
|
+
from [GitHub Releases](https://github.com/uppy19d0/cedrodb/releases), then install
|
|
50
|
+
the downloaded file:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
python -m pip install ./cedrodb-0.3.0.tar.gz
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
A compatible wheel needs no Rust toolchain at runtime. Source installation
|
|
57
|
+
requires Python 3.9+, Rust 1.85+ and a working C linker. The source archive bundles
|
|
58
|
+
the Rust core; it builds independently of the repository.
|
|
59
|
+
|
|
60
|
+
The release wheel build matrix covers four platform/architecture combinations:
|
|
61
|
+
|
|
62
|
+
| Platform | Architecture | Python ABI |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| Linux | x86_64 | CPython 3.9+ (`cp39-abi3`) |
|
|
65
|
+
| macOS | Apple Silicon / ARM64 | CPython 3.9+ (`cp39-abi3`) |
|
|
66
|
+
| macOS | Intel / x86_64 | CPython 3.9+ (`cp39-abi3`) |
|
|
67
|
+
| Windows | x86_64 | CPython 3.9+ (`cp39-abi3`) |
|
|
68
|
+
|
|
69
|
+
Check a release's assets for its available builds and platform tags. Other
|
|
70
|
+
architectures may require a source build; a Linux wheel does not imply support
|
|
71
|
+
for every Linux distribution or musl-based system.
|
|
72
|
+
|
|
73
|
+
## Quickstart: save and query documents
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from cedrodb import Database
|
|
77
|
+
|
|
78
|
+
with Database("./app.cedro") as db:
|
|
79
|
+
orders = db.collection("orders")
|
|
80
|
+
orders.put_many({
|
|
81
|
+
"order:1": {"status": "paid", "total": 120},
|
|
82
|
+
"order:2": {"status": "pending", "total": 35},
|
|
83
|
+
})
|
|
84
|
+
orders.create_index("status")
|
|
85
|
+
page = orders.find_page(
|
|
86
|
+
[{"field": "status", "op": "eq", "value": "paid"}], limit=20
|
|
87
|
+
)
|
|
88
|
+
print(page["records"]) # [{"key": "order:1", "value": {...}}]
|
|
89
|
+
print(page["used_index"]) # status
|
|
90
|
+
print(orders.get("order:1"))
|
|
91
|
+
print(db.stats())
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Collections store any supported JSON value, including arrays and null. `put`
|
|
95
|
+
replaces the complete value for a key; `delete` removes it. `put_many` accepts a
|
|
96
|
+
mapping or an iterable of `(key, value)` pairs and commits them atomically.
|
|
97
|
+
|
|
98
|
+
`find()` returns one list of `{key, value}` records. `find_page()` also returns
|
|
99
|
+
`next_after`, `scanned` and `used_index`. To continue, pass `next_after` as `after`;
|
|
100
|
+
`None` means there is no continuation. `iter_records(page_size=100)` iterates
|
|
101
|
+
bounded result pages. Results are ordered by key; pages do not share a snapshot.
|
|
102
|
+
Writes between requests can change subsequent results. Limits are 1..1000 records.
|
|
103
|
+
|
|
104
|
+
Filters are combined with AND. Operations are `eq`, `ne`, `gt`, `gte`, `lt`,
|
|
105
|
+
`lte`, and `exists` (with a boolean value). A field is a top-level name or an
|
|
106
|
+
RFC 6901 JSON Pointer, such as `/customer/city`. Missing fields differ from JSON
|
|
107
|
+
null; use `exists` to test absence. Equality indexes stay consistent with writes
|
|
108
|
+
and must be recreated after reopening or restoring a database.
|
|
109
|
+
|
|
110
|
+
## Offline writes and the atomic outbox
|
|
111
|
+
|
|
112
|
+
Commit the document and the event in the same transaction. Your uploader can
|
|
113
|
+
then find the event after a restart without losing the corresponding form.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from cedrodb import Database
|
|
117
|
+
|
|
118
|
+
with Database("./field-app.cedro") as db:
|
|
119
|
+
with db.transaction() as tx:
|
|
120
|
+
tx.put("forms", "inspection:1", {
|
|
121
|
+
"site": "Santo Domingo", "status": "pending_upload"
|
|
122
|
+
})
|
|
123
|
+
tx.put("outbox", "inspection:1:submitted", {
|
|
124
|
+
"event_id": "inspection:1:submitted",
|
|
125
|
+
"type": "inspection.submitted", "form_id": "inspection:1"
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
page = db.changes(since=0, limit=100)
|
|
129
|
+
for transaction in page["transactions"]:
|
|
130
|
+
for operation in transaction["operations"]:
|
|
131
|
+
if operation["collection"] == "outbox" and operation["kind"] == "put":
|
|
132
|
+
print(operation["value"])
|
|
133
|
+
# Save page["next_cursor"] only after processing succeeds.
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The feed survives close/reopen and contains complete put/delete operations in
|
|
137
|
+
transaction order. `since` is an exclusive transaction cursor, starting at zero,
|
|
138
|
+
and belongs to this database history. `limit` counts transactions (1..1000), not
|
|
139
|
+
operations. `has_more` tells you whether another page is available. Pages also
|
|
140
|
+
cap encoded frame bytes at `max_frame_bytes + 12` without splitting a transaction;
|
|
141
|
+
decoded Python objects require additional memory.
|
|
142
|
+
|
|
143
|
+
Your application owns the uploader, checkpoint storage and event deduplication.
|
|
144
|
+
A crash after an external effect but before its checkpoint can repeat delivery;
|
|
145
|
+
CedroDB does not promise exactly-once delivery or provide a background worker.
|
|
146
|
+
|
|
147
|
+
`db.transaction()` buffers writes and copies staged values. A successful context
|
|
148
|
+
exit commits one batch; an exception rolls it back. Reads continue to observe
|
|
149
|
+
committed data, without read-your-writes, snapshot isolation or automatic conflict
|
|
150
|
+
detection. Alternatively, pass operations directly to `db.batch([...])`:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
with Database("./app.cedro") as db:
|
|
154
|
+
db.batch([
|
|
155
|
+
{"kind": "put", "collection": "orders", "key": "order:3", "value": {"total": 10}},
|
|
156
|
+
{"kind": "delete", "collection": "drafts", "key": "order:3"},
|
|
157
|
+
])
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Verify, back up and restore
|
|
161
|
+
|
|
162
|
+
A backup contains the committed log inside a versioned container with a length
|
|
163
|
+
and CRC checksum. It preserves transaction history and changefeed cursors.
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from pathlib import Path
|
|
167
|
+
from cedrodb import Database
|
|
168
|
+
|
|
169
|
+
backup = Path("./field-app.backup") # Must not already exist.
|
|
170
|
+
restored = Path("./recovered.cedro") # Must not already exist, even empty.
|
|
171
|
+
|
|
172
|
+
with Database("./field-app.cedro") as db:
|
|
173
|
+
print(db.verify())
|
|
174
|
+
print(db.backup(backup))
|
|
175
|
+
|
|
176
|
+
report = Database.restore_backup(backup, restored)
|
|
177
|
+
print(report) # log_bytes, transactions, records, content_bytes
|
|
178
|
+
|
|
179
|
+
with Database(restored) as db:
|
|
180
|
+
db.collection("forms").create_index("status")
|
|
181
|
+
print(db.changes(since=0))
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`verify()` reads and validates the committed log; it does not repair data.
|
|
185
|
+
`backup(path)` creates a new file and never overwrites an existing file or
|
|
186
|
+
folder. `restore_backup(backup_path, destination_path)` validates the complete
|
|
187
|
+
container and log before creating a new destination directory. Parent directories
|
|
188
|
+
must already exist. Static restore returns an `IntegrityReport` dictionary,
|
|
189
|
+
without retaining an open handle or file lock. Index definitions are handle-local
|
|
190
|
+
and must be recreated; the committed documents and history are retained.
|
|
191
|
+
|
|
192
|
+
For custom content limits, pass the same configuration to restore and open:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
limits = {"max_document_bytes": 2_097_152, "max_state_bytes": 134_217_728}
|
|
196
|
+
Database.restore_backup("./large.backup", "./large-restored.cedro", **limits)
|
|
197
|
+
with Database("./large-restored.cedro", **limits) as db:
|
|
198
|
+
print(db.verify())
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
All three methods are synchronous and perform disk I/O; the native operation
|
|
202
|
+
releases Python's GIL. Verification replays state in memory and can require
|
|
203
|
+
another copy of the live state plus a frame buffer. CRC detects accidental
|
|
204
|
+
corruption; it does not authenticate or encrypt a backup. Interrupted I/O can
|
|
205
|
+
require inspecting and removing an incomplete output before retrying. See
|
|
206
|
+
[backup format and recovery details](https://github.com/uppy19d0/cedrodb/blob/main/docs/backups.md).
|
|
207
|
+
|
|
208
|
+
Run the complete offline-form example with `python examples/offline_forms.py`.
|
|
209
|
+
|
|
210
|
+
## Conditional updates and null
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from cedrodb import Database, DELETE, MISSING
|
|
214
|
+
|
|
215
|
+
with Database("./app.cedro") as db:
|
|
216
|
+
claims = db.collection("claims")
|
|
217
|
+
created = claims.compare_exchange(
|
|
218
|
+
"job:1", expected=MISSING, replacement={"owner": "worker-1"}
|
|
219
|
+
)
|
|
220
|
+
removed = claims.compare_exchange(
|
|
221
|
+
"job:1", expected={"owner": "worker-1"}, replacement=DELETE
|
|
222
|
+
)
|
|
223
|
+
missing = claims.get("unknown", MISSING) is MISSING
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`replacement` is required. `None` stores JSON null; `DELETE` removes the record.
|
|
227
|
+
`MISSING` as expected means the key must not exist, while `expected=None` requires
|
|
228
|
+
a stored null. A mismatch returns `False` without appending a transaction. The
|
|
229
|
+
comparison covers the complete JSON value. Exact integers and integral floats
|
|
230
|
+
compare equally only within the safe range ±2**53; booleans remain distinct.
|
|
231
|
+
|
|
232
|
+
## API reference
|
|
233
|
+
|
|
234
|
+
| Object | Methods |
|
|
235
|
+
| --- | --- |
|
|
236
|
+
| `Database` | `collection`, `batch`, `transaction`, `changes`, `stats`, `verify`, `backup`, `close` |
|
|
237
|
+
| `Database.restore_backup` | Static restore with optional content-limit keywords |
|
|
238
|
+
| `Collection` | `put`, `get`, `delete`, `put_many`, `find`, `find_page`, `iter_records`, `create_index`, `compare_exchange`, `aggregate` |
|
|
239
|
+
| `Transaction` | `put(collection, key, value)`, `delete(collection, key)`, `commit`, `rollback`, context manager |
|
|
240
|
+
|
|
241
|
+
`Database(path, *, max_document_bytes=1_048_576, max_frame_bytes=16_777_216,
|
|
242
|
+
max_state_bytes=67_108_864)` opens or creates a directory. Paths accept strings
|
|
243
|
+
and `os.PathLike` objects returning strings. Use a context manager or `close()`;
|
|
244
|
+
closing is idempotent and releases the native lock immediately. `db.closed`
|
|
245
|
+
reports its state. Existing collection handles fail with `DatabaseClosedError`
|
|
246
|
+
after close.
|
|
247
|
+
|
|
248
|
+
Engine failures raise `CedroError`. Unsupported JSON raises `TypeError` or
|
|
249
|
+
`ValueError`; integers outside `[-2**63, 2**64 - 1]` raise `OverflowError`. NaN,
|
|
250
|
+
infinity, cycles, non-string object keys and unsupported objects are rejected
|
|
251
|
+
before mutation. Supported integers round-trip exactly. JSON depth is limited
|
|
252
|
+
to 64 levels, and the transport always uses `allow_nan=False`. Type annotations
|
|
253
|
+
and a `py.typed` marker are included.
|
|
254
|
+
|
|
255
|
+
## Current limits
|
|
256
|
+
|
|
257
|
+
- One open owning handle/process per directory; its native calls are serialized.
|
|
258
|
+
- Documents, equality indexes and transaction offsets are held in RAM. Encoded
|
|
259
|
+
content limits exclude allocator overhead, indexes and temporary buffers.
|
|
260
|
+
- Append-only log with no compaction, retention or bounded total disk use.
|
|
261
|
+
- Handle-local indexes and key-based query pagination, without snapshot isolation.
|
|
262
|
+
- `aggregate(field)` uses floating-point arithmetic on a top-level numeric field
|
|
263
|
+
and can lose integer precision above 2**53; stored documents remain exact.
|
|
264
|
+
- No SQL, MVCC, replication, encryption or automatic synchronization.
|
|
265
|
+
|
|
266
|
+
## Upgrade from 0.2 to 0.3
|
|
267
|
+
|
|
268
|
+
Existing document, batch, query, CAS and changefeed APIs keep their signatures.
|
|
269
|
+
The database log format is unchanged; 0.2 stores can be opened directly. Version
|
|
270
|
+
0.3 adds `db.verify()`, `db.backup(path)` and `Database.restore_backup(...)`.
|
|
271
|
+
Backup containers are distinct from raw `cedro.log` files: use `backup()` to
|
|
272
|
+
create them. Restore requires a new destination and preserves the original
|
|
273
|
+
history/cursor sequence. Recreate equality indexes after opening the restored
|
|
274
|
+
store, and configure matching limits when your store exceeds the defaults.
|
|
275
|
+
|
|
276
|
+
## Build and test
|
|
277
|
+
|
|
278
|
+
```sh
|
|
279
|
+
python -m venv .venv
|
|
280
|
+
# Activate the environment with your platform's activation command.
|
|
281
|
+
python -m pip install 'maturin>=1.15,<2'
|
|
282
|
+
python scripts/prepare_core.py
|
|
283
|
+
maturin build --release --out dist
|
|
284
|
+
maturin sdist --out dist
|
|
285
|
+
python -m pip install ./dist/<your-platform-wheel>.whl
|
|
286
|
+
python -m unittest discover -s tests -v
|
|
287
|
+
python scripts/verify_sdist.py dist/cedrodb-0.3.0.tar.gz
|
|
288
|
+
python examples/offline_forms.py
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
`prepare_core.py` copies only the core manifest, license and Rust source into
|
|
292
|
+
ignored `core/`. Regenerate that copy after engine changes. The sdist includes
|
|
293
|
+
those files and has no dependency on `../..` at installation. `verify_sdist.py`
|
|
294
|
+
rebuilds an extracted archive, installs the resulting wheel in an isolated target
|
|
295
|
+
and runs its tests. Keep `Cargo.lock` for reproducible dependency versions.
|
|
296
|
+
|
|
297
|
+
[Source](https://github.com/uppy19d0/cedrodb) ·
|
|
298
|
+
[Demo](https://www.ltavarez.me/labs/cedrodb) ·
|
|
299
|
+
[Issues](https://github.com/uppy19d0/cedrodb/issues)
|
|
300
|
+
|