picovolt 1.8.1 → 2.0.0

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.
package/README.md CHANGED
@@ -3,13 +3,19 @@
3
3
  [![CI](https://github.com/MiniJe/picovolt/actions/workflows/ci.yml/badge.svg)](https://github.com/MiniJe/picovolt/actions/workflows/ci.yml)
4
4
  [![crates.io](https://img.shields.io/crates/v/picovolt.svg)](https://crates.io/crates/picovolt)
5
5
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
6
- ![Status: stable 1.x](https://img.shields.io/badge/status-stable%201.x-brightgreen.svg)
6
+ ![Version: 2.0](https://img.shields.io/badge/version-2.0-blue.svg)
7
7
  [![GitHub stars](https://img.shields.io/github/stars/MiniJe/picovolt?style=social)](https://github.com/MiniJe/picovolt)
8
8
 
9
- PicoVolt is an embedded database engine written in Rust. Its 1.x public API and
10
- on-disk format are stable under Semantic Versioning. It is young software and
11
- has not had an external security audit, so review it and keep backups before
12
- trusting it with data you cannot regenerate.
9
+ PicoVolt is an embedded database engine written in Rust. **2.0** provides
10
+ concurrent snapshot readers, bounded writer scheduling, and an incremental
11
+ durable commit log. Independent security review and external application trials
12
+ have not been completed. See the [2.0 release ledger](docs/RELEASE_2_0.md)
13
+ for qualification evidence and publication status.
14
+
15
+ Start with the [2.0 guide for every maintained interface](docs/QUICKSTART_2_0.md)
16
+ for atomic batches, persistence choices, log diagnostics and error recovery.
17
+ The [standalone review prompt](docs/INDEPENDENT_REVIEW_PROMPT.md) defines an
18
+ independent assessment and external trials deferred beyond the 2.0 release.
13
19
 
14
20
  If PicoVolt is useful to you, consider starring the repository on GitHub. It is
15
21
  the simplest way to help others discover the project.
@@ -22,12 +28,13 @@ Virtualization Layer Engine (VLE) that shifts between two on-disk shapes:
22
28
  - **Production mode:** a single contiguous, memory-mappable `.pvdb` file produced
23
29
  by `pv_bake()`.
24
30
 
25
- New records use a slotted row layout for O(1) appends. Idle pages can be
26
- transposed into a packed columnar layout for compression and cache efficiency.
31
+ New records use a slotted row layout for O(1) appends. Applications can run
32
+ bounded maintenance steps that transpose immutable non-tail pages into an
33
+ MVCC-preserving columnar layout with packed decimal encoding.
27
34
 
28
35
  ## Status
29
36
 
30
- The current stable release is exercised by a 240+ test Rust suite plus doctests
37
+ The engine is exercised by Rust unit and integration suites plus doctests
31
38
  and maintained-binding integration tests. CI also enforces formatting and
32
39
  warning-free Clippy builds on Linux and Windows. Shipped changes are tracked in
33
40
  [CHANGELOG.md](CHANGELOG.md), and the remaining work toward 2.0 is tracked in
@@ -54,6 +61,7 @@ warning-free Clippy builds on Linux and Windows. Shipped changes are tracked in
54
61
  | [`engine/compliance.rs`](src/engine/compliance.rs) | optional, app-driven usage-policy hook (not a license requirement) |
55
62
  | [`enterprise.rs`](src/enterprise.rs) | optional, host-owned audit events and honest capability discovery for fleet integrations |
56
63
  | [`db.rs`](src/db.rs) | the `Database` surface that ties it together |
64
+ | [`upgrade.rs`](src/upgrade.rs) | out-of-place format migration with backup and deep verification |
57
65
  | [`ffi.rs`](src/ffi.rs) | C ABI (the `capi` feature): a panic-safe, C-callable surface wrapping the engine for Go, Python, and C bindings |
58
66
 
59
67
  ### Engineering notes
@@ -69,22 +77,27 @@ warning-free Clippy builds on Linux and Windows. Shipped changes are tracked in
69
77
  differential test checks `pv-wasm` against `wasmi` to keep it honest. Floats,
70
78
  tables, globals, imports, SIMD, and `br_table` are out of scope for `pv-wasm`
71
79
  and are rejected rather than mis-run.
72
- - **Page-backed engine.** Tables are append-only chains of row pages, each header
73
- linking to the next. Inserts append to a tail page and write only that page
74
- plus an O(tables) manifest, so autocommit is O(1) per insert rather than a
75
- whole-table rewrite. Reads stream through a bounded buffer pool
76
- ([`storage/cache.rs`](src/storage/cache.rs)), so datasets need not fit in RAM,
77
- and opt-in ordered indexes ([`storage/index.rs`](src/storage/index.rs)) turn
78
- `WHERE col = value` into a point lookup and range comparisons such as
80
+ - **Page-backed engine.** Tables are append-only chains of hot row pages and
81
+ optional packed cold pages, each header linking to the next. Inserts append to
82
+ a row tail. Commit cost also includes catalog/index maintenance, retained-log
83
+ accounting and the selected durability protocol; it is not uniformly O(1).
84
+ Logged 2.0 workspaces persist index definitions to reduce catalog rewrites.
85
+ Reads stream through a
86
+ bounded buffer pool ([`storage/cache.rs`](src/storage/cache.rs)), so datasets
87
+ need not fit in RAM, and opt-in ordered indexes
88
+ ([`storage/index.rs`](src/storage/index.rs)) turn `WHERE col = value` into a
89
+ point lookup and range comparisons such as
79
90
  `WHERE col > v` into an ordered scan rather than a full scan.
80
91
  - **Selectable durability.** `Database::set_durability(Durability::Sync)` makes
81
92
  each flush `fsync` the data and commit the manifest atomically (write to a temp
82
93
  file, `fsync`, then rename). The default `Fast` mode uses the OS cache only:
83
94
  fast and durable on a clean exit, but not power-loss-safe.
84
- - **Crash-recoverable transactions.** Explicit `BEGIN`, `COMMIT`, and
85
- `ROLLBACK` group filesystem or in-memory writes. Filesystem transactions keep
86
- a synced rollback image and recovery marker; reopening after interruption
87
- restores the last committed state before loading the workspace.
95
+ - **Concurrent transactions.** Native `SharedDatabase` exposes independent
96
+ snapshot readers and bounded FIFO writers. Logged workspaces sync original
97
+ pages before overwriting them and publish an ordered physical change stream.
98
+ Reopening rolls back incomplete writes. Format 6 prevents old binaries from
99
+ bypassing recovery. Existing 1.x images remain readable and migratable.
100
+ See [the concurrency contract](docs/CONCURRENCY.md) for limits and costs.
88
101
  - **Hardened against untrusted input.** Opening a `.pvdb` or workspace, or running
89
102
  a WASM module, validates manifest hashes (no path traversal), bounds-checks CAS
90
103
  offsets and page chains (no out-of-bounds reads or infinite loops on a crafted
@@ -112,7 +125,8 @@ cargo run --release --example bench # evaluation harness across modes and wor
112
125
  ```
113
126
 
114
127
  Install the full CLI with `cargo install picovolt --features data-tools`, then use `pv query`,
115
- `pv inspect`, `pv history`, `pv diff`, `pv import`, `pv export`, and `pv bake`.
128
+ `pv inspect`, `pv history`, `pv diff`, `pv migrate`, `pv compact`, `pv import`,
129
+ `pv export`, and `pv bake`.
116
130
  Parquet/SQLite conversion, query explanations, inspection, resumable baking,
117
131
  and dataset signing are documented in [Data tools](docs/DATA_TOOLS.md).
118
132
 
@@ -146,16 +160,51 @@ databases.
146
160
  Durability is selectable via `Database::set_durability` (`Fast` OS-cache default,
147
161
  or crash-safe `Sync` with fsync and an atomic manifest).
148
162
 
163
+ Native Rust applications can begin adopting the 2.0 concurrency surface through
164
+ `SharedDatabase`. It is a cloneable, bounded worker-thread coordinator with
165
+ explicit read and write transaction handles, FIFO admission, cooperative
166
+ cancellation, and rollback before failed or abandoned writes release the queue.
167
+ The first slice serializes execution and preserves format v5; use clones of one
168
+ coordinator rather than independently opening the same development workspace.
169
+ See [Shared database concurrency](docs/CONCURRENCY.md) for the contract and
170
+ current limits.
171
+
149
172
  Measured results and the methodology are in [BENCHMARKS.md](BENCHMARKS.md). In
150
173
  short, PicoVolt is a page-backed engine with O(1) filesystem appends (autocommit
151
174
  around 33k rows/s, linear), larger-than-RAM reads through a bounded buffer pool (a
152
175
  667-page dataset serves from a 16-page pool), ordered secondary indexes (point
153
176
  lookups roughly 6,100 times faster than a scan, plus range predicates), MVCC
154
177
  time-travel, opt-in crash-safe durability (`Durability::Sync`), and a fast
155
- compile-and-publish path (CAS dedup, columnar compression, memory-mappable
156
- single-file artifacts). Current limits include full-workspace transaction
157
- backups rather than an incremental WAL, left-deep equality joins rather than a
158
- general SQL planner, and no concurrent writers.
178
+ compile-and-publish path (CAS dedup, cooperative columnar compression,
179
+ memory-mappable single-file artifacts). Current limits include full-workspace
180
+ transaction backups rather than an incremental WAL, adaptive index access within
181
+ a left-deep equality-join plan rather than a general SQL planner, and no
182
+ concurrent writers.
183
+
184
+ ## Maintenance and migration
185
+
186
+ Cold-page maintenance is explicit in 1.x so the host retains ownership of
187
+ scheduling and threads:
188
+
189
+ ```sh
190
+ pv compact ./data.pv --max-pages 64
191
+ pv inspect ./data.pv --json
192
+ ```
193
+
194
+ `Database::compact_step(max_pages)` preserves record addresses, indexes, and
195
+ complete MVCC history; it never compacts the mutable tail and leaves a page in
196
+ row form when transposition would not save space. Each pass uses the
197
+ crash-recoverable transaction protocol: allow bounded journal space for logged
198
+ workspaces, or a full rollback image for unlogged workspaces. Baked-image migration is
199
+ out-of-place and deeply verified before publication:
200
+
201
+ ```sh
202
+ pv migrate old.pvdb new.pvdb --dry-run
203
+ pv migrate old.pvdb new.pvdb --backup old.exact-backup.pvdb
204
+ ```
205
+
206
+ The source is never modified and existing destinations are never overwritten.
207
+ See [Migration and compaction](docs/MIGRATION.md).
159
208
 
160
209
  ## Install and distribution
161
210
 
@@ -164,7 +213,7 @@ general SQL planner, and no concurrent writers.
164
213
  | **Rust** (crates.io) | `cargo add picovolt` |
165
214
  | **JavaScript / npm** (WebAssembly, browser and Node) | `npm install picovolt` |
166
215
  | **Python** (native wheels) | `python -m pip install picovolt` |
167
- | **Go** (`database/sql` and direct API) | `go get github.com/MiniJe/picovolt/bindings/go@latest`, then provide the matching native C ABI library described in [`bindings/go/`](bindings/go) |
216
+ | **Go** (`database/sql` and direct API) | `go get github.com/MiniJe/picovolt/bindings/go/v2@v2.0.0`, then provide the matching native C ABI library described in [`bindings/go/`](bindings/go) |
168
217
  | **C** | Download the matching `picovolt-capi-*` bundle from the [latest release](https://github.com/MiniJe/picovolt/releases/latest), or run `cargo build --release --features capi` |
169
218
  | **In-memory** (native, no filesystem) | `Database::open_memory()`, export with `bake_to_bytes()` |
170
219
 
@@ -233,7 +282,6 @@ native modules built on the public API. Both are documented in
233
282
  | | |
234
283
  |--|--|
235
284
  | Roadmap | [ROADMAP.md](ROADMAP.md) |
236
- | One-million-download plan | [docs/ROADMAP_1M_DOWNLOADS.md](docs/ROADMAP_1M_DOWNLOADS.md) |
237
285
  | Monetization thesis | [docs/MONETIZATION.md](docs/MONETIZATION.md) |
238
286
  | Enterprise integration foundation | [docs/ENTERPRISE.md](docs/ENTERPRISE.md) |
239
287
  | Platform and file support | [docs/SUPPORT.md](docs/SUPPORT.md) |
package/browser.js CHANGED
@@ -62,6 +62,11 @@ export class PersistentDb {
62
62
  return statement;
63
63
  }
64
64
 
65
+ executeMany(sql, rows) {
66
+ this._assertOpen();
67
+ return JSON.parse(this.db.executeMany(sql, rows)).mutated;
68
+ }
69
+
65
70
  async save() {
66
71
  this._assertOpen();
67
72
  const root = await navigator.storage.getDirectory();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "picovolt",
3
3
  "type": "module",
4
4
  "description": "Embedded SQL database with MVCC history and single-file deployment",
5
- "version": "1.8.1",
5
+ "version": "2.0.0",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",
package/picovolt.d.ts CHANGED
@@ -20,6 +20,10 @@ export class Db {
20
20
  * `... BEFORE tx` time-travel query.
21
21
  */
22
22
  currentTx(): number;
23
+ /**
24
+ * Atomically execute an INSERT/UPDATE/DELETE for an array of parameter arrays.
25
+ */
26
+ executeMany(sql: string, rows: any): string;
23
27
  /**
24
28
  * Export the whole database as a `.pvdb` byte image (a `Uint8Array` in JS).
25
29
  */
package/picovolt_bg.js CHANGED
@@ -45,6 +45,32 @@ export class Db {
45
45
  const ret = wasm.db_currentTx(this.__wbg_ptr);
46
46
  return ret >>> 0;
47
47
  }
48
+ /**
49
+ * Atomically execute an INSERT/UPDATE/DELETE for an array of parameter arrays.
50
+ * @param {string} sql
51
+ * @param {any} rows
52
+ * @returns {string}
53
+ */
54
+ executeMany(sql, rows) {
55
+ let deferred3_0;
56
+ let deferred3_1;
57
+ try {
58
+ const ptr0 = passStringToWasm0(sql, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
59
+ const len0 = WASM_VECTOR_LEN;
60
+ const ret = wasm.db_executeMany(this.__wbg_ptr, ptr0, len0, rows);
61
+ var ptr2 = ret[0];
62
+ var len2 = ret[1];
63
+ if (ret[3]) {
64
+ ptr2 = 0; len2 = 0;
65
+ throw takeFromExternrefTable0(ret[2]);
66
+ }
67
+ deferred3_0 = ptr2;
68
+ deferred3_1 = len2;
69
+ return getStringFromWasm0(ptr2, len2);
70
+ } finally {
71
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
72
+ }
73
+ }
48
74
  /**
49
75
  * Export the whole database as a `.pvdb` byte image (a `Uint8Array` in JS).
50
76
  * @returns {Uint8Array}
package/picovolt_bg.wasm CHANGED
Binary file
package/sqlite.js CHANGED
@@ -94,6 +94,11 @@ class Database {
94
94
  return new Statement(this, sql);
95
95
  }
96
96
 
97
+ executeMany(sql, rows) {
98
+ this._assertOpen();
99
+ return JSON.parse(this._db.executeMany(sql, rows)).mutated;
100
+ }
101
+
97
102
  // Run one or more `;`-separated statements with no bound parameters.
98
103
  exec(sql) {
99
104
  this._assertOpen();
package/worker.js CHANGED
@@ -30,6 +30,10 @@ self.addEventListener("message", async ({ data }) => {
30
30
  result = { statementId, parameterCount: statement.parameterCount };
31
31
  break;
32
32
  }
33
+ case "executeMany":
34
+ if (!database) throw new Error("open the database first");
35
+ result = database.executeMany(data.sql, data.rows);
36
+ break;
33
37
  case "execute": {
34
38
  const statement = statements.get(data.statementId);
35
39
  if (!statement) throw new Error("unknown PicoVolt prepared statement");