picovolt 1.4.0 → 1.6.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
@@ -1,7 +1,7 @@
1
1
  # PicoVolt (PVDB)
2
2
 
3
3
  [![CI](https://github.com/MiniJe/picovolt/actions/workflows/ci.yml/badge.svg)](https://github.com/MiniJe/picovolt/actions/workflows/ci.yml)
4
- [![Version](https://img.shields.io/badge/version-1.4.0-blue.svg)](CHANGELOG.md)
4
+ [![Version](https://img.shields.io/badge/version-1.6.0-blue.svg)](CHANGELOG.md)
5
5
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
6
6
  ![Status: 1.0 stable](https://img.shields.io/badge/status-1.0%20stable-brightgreen.svg)
7
7
  [![GitHub stars](https://img.shields.io/github/stars/MiniJe/picovolt?style=social)](https://github.com/MiniJe/picovolt)
@@ -56,8 +56,9 @@ Linux and Windows. Changes are tracked in [CHANGELOG.md](CHANGELOG.md).
56
56
  | [`engine/mvcc.rs`](src/engine/mvcc.rs) | transaction clock and snapshot visibility |
57
57
  | [`engine/wasm.rs`](src/engine/wasm.rs) | sandboxed `wasmi` extension runtime and the `WasmExec` backend trait |
58
58
  | [`engine/interp.rs`](src/engine/interp.rs) | `pv-wasm`: a from-scratch WASM interpreter (integer subset) |
59
- | [`engine/query.rs`](src/engine/query.rs) | SQL front-end (CREATE/INSERT/UPDATE/DELETE/DROP, `SELECT` with projection, `AS` aliases, `DISTINCT`, aggregates, `GROUP BY`/`HAVING`, `WHERE` predicates incl. `IN`/`BETWEEN`/`IS NULL`/`LIKE`, `BEFORE`, multi-column `ORDER BY`, `LIMIT`) |
59
+ | [`engine/query.rs`](src/engine/query.rs) | SQL front-end (CREATE/INSERT/UPDATE/DELETE/DROP, `SELECT` with projection, `AS` aliases, `DISTINCT`, aggregates, `GROUP BY`/`HAVING`, `WHERE` predicates incl. `IN`/`BETWEEN`/`IS NULL`/`LIKE`, `BEFORE`, multi-column `ORDER BY`, `LIMIT`/`OFFSET`) |
60
60
  | [`engine/compliance.rs`](src/engine/compliance.rs) | optional, app-driven usage-policy hook (not a license requirement) |
61
+ | [`enterprise.rs`](src/enterprise.rs) | optional, host-owned audit events and honest capability discovery for fleet integrations |
61
62
  | [`db.rs`](src/db.rs) | the `Database` surface that ties it together |
62
63
  | [`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 |
63
64
 
@@ -86,6 +87,10 @@ Linux and Windows. Changes are tracked in [CHANGELOG.md](CHANGELOG.md).
86
87
  each flush `fsync` the data and commit the manifest atomically (write to a temp
87
88
  file, `fsync`, then rename). The default `Fast` mode uses the OS cache only:
88
89
  fast and durable on a clean exit, but not power-loss-safe.
90
+ - **Crash-recoverable transactions.** Explicit `BEGIN`, `COMMIT`, and
91
+ `ROLLBACK` group filesystem or in-memory writes. Filesystem transactions keep
92
+ a synced rollback image and recovery marker; reopening after interruption
93
+ restores the last committed state before loading the workspace.
89
94
  - **Hardened against untrusted input.** Opening a `.pvdb` or workspace, or running
90
95
  a WASM module, validates manifest hashes (no path traversal), bounds-checks CAS
91
96
  offsets and page chains (no out-of-bounds reads or infinite loops on a crafted
@@ -113,22 +118,24 @@ cargo run --release --example bench # evaluation harness across modes and wor
113
118
  ```
114
119
 
115
120
  Install the first-class CLI with `cargo install picovolt`, then use `pv query`,
116
- `pv inspect`, `pv import`, `pv export`, and `pv bake`. Copyable Rust, Python, Go,
117
- and browser projects are in [`starters/`](starters/README.md); supported adapters
121
+ `pv inspect`, `pv history`, `pv import`, `pv export`, and `pv bake`. Copyable
122
+ Rust, Python, Go, Node, and browser projects are in [`starters/`](starters/README.md); supported adapters
118
123
  are catalogued in [`docs/INTEGRATIONS.md`](docs/INTEGRATIONS.md).
119
124
 
120
- SQL supported: `CREATE TABLE` with `PRIMARY KEY`, `UNIQUE`, and `NOT NULL`,
121
- `CREATE [UNIQUE] INDEX ON t (col)`, `INSERT`,
122
- `UPDATE ... SET ... WHERE`, `DELETE ... WHERE`, `DROP TABLE`, and
125
+ SQL supported: `CREATE TABLE [IF NOT EXISTS]` with `PRIMARY KEY`, `UNIQUE`, and `NOT NULL`,
126
+ `CREATE [UNIQUE] INDEX ON t (col)`, single- and multi-row `INSERT`,
127
+ `UPDATE ... SET ... WHERE`, `DELETE ... WHERE`, `DROP TABLE [IF EXISTS]`, and
123
128
  `SELECT [DISTINCT] {* | col [AS alias], ... | COUNT/SUM/MIN/MAX/AVG(...) [AS alias]}
124
129
  FROM t [WHERE <pred>] [GROUP BY cols] [HAVING <pred>] [BEFORE tx]
125
- [ORDER BY col [ASC|DESC], ...] [LIMIT n]`, where `<pred>` combines
130
+ [ORDER BY col [ASC|DESC], ...] [LIMIT n] [OFFSET n]`, where `<pred>` combines
126
131
  `col <op> value` (`=`, `!=`, `<`, `<=`, `>`, `>=`, `LIKE`, `NOT LIKE`),
127
132
  `col [NOT] IN (...)`, `col [NOT] BETWEEN a AND b`, and `col IS [NOT] NULL` with
128
133
  `AND`, `OR`, and parentheses. Integer and decimal values compare by magnitude.
129
- Basic `SELECT * FROM a [INNER|LEFT] JOIN b ON a_key = b_key` equality joins are
130
- also supported. Rust callers can cache `Database::prepare(...)` templates and use
131
- atomic `Database::transaction(...)` closures with in-memory databases.
134
+ Two-table equality `INNER`/`LEFT JOIN` queries support qualified references,
135
+ projection, aliases, `DISTINCT`, filters, ordering, and pagination. Rust callers
136
+ can cache `Database::prepare(...)` templates and use explicit transaction
137
+ lifecycle methods or atomic `Database::transaction(...)` closures with both
138
+ filesystem and in-memory databases.
132
139
  Durability is selectable via `Database::set_durability` (`Fast` OS-cache default,
133
140
  or crash-safe `Sync` with fsync and an atomic manifest).
134
141
 
@@ -139,8 +146,9 @@ around 33k rows/s, linear), larger-than-RAM reads through a bounded buffer pool
139
146
  lookups roughly 11,000 times faster than a scan, plus range predicates), MVCC
140
147
  time-travel, opt-in crash-safe durability (`Durability::Sync`), and a fast
141
148
  compile-and-publish path (CAS dedup, columnar compression, memory-mappable
142
- single-file artifacts). Current limits include no filesystem `BEGIN`/`COMMIT`
143
- transactions, only basic two-table equality joins, and no concurrent writers.
149
+ single-file artifacts). Current limits include full-workspace transaction
150
+ backups rather than an incremental WAL, only basic two-table equality joins,
151
+ and no concurrent writers.
144
152
 
145
153
  ## Install and distribution
146
154
 
@@ -148,7 +156,7 @@ transactions, only basic two-table equality joins, and no concurrent writers.
148
156
  |--------|-----|
149
157
  | **Rust** (crates.io) | `cargo add picovolt` |
150
158
  | **JavaScript / npm** (WebAssembly, browser and Node) | `npm install picovolt` |
151
- | **Python** (native wheels) | `python -m pip install picovolt` (after the first PyPI release) |
159
+ | **Python** (native wheels) | `python -m pip install picovolt` |
152
160
  | **C / Go** (native, via the C ABI) | `cargo build --release --features capi`, then see [`bindings/`](bindings) |
153
161
  | **In-memory** (native, no filesystem) | `Database::open_memory()`, export with `bake_to_bytes()` |
154
162
 
@@ -171,7 +179,8 @@ JavaScript API (`import Database from "picovolt/sqlite"`), a Python DB-API 2.0
171
179
  module (`import picovolt.dbapi2 as sqlite`), and the Go `database/sql` driver
172
180
  ([`bindings/go/pvsql`](bindings/go/pvsql)). Shared limits include positional `?`
173
181
  only and the intentionally compact SQL grammar; JavaScript and in-memory Rust
174
- also expose rollback-capable transaction wrappers.
182
+ also expose rollback-capable transaction wrappers. Native bindings expose the
183
+ same transaction lifecycle through the C ABI.
175
184
 
176
185
  ## Server mode
177
186
 
@@ -196,6 +205,11 @@ result rows, and response size are bounded. TLS is not built in, so network
196
205
  deployments still belong behind a TLS-terminating reverse proxy. See
197
206
  [src/bin/server.rs](src/bin/server.rs).
198
207
 
208
+ The HTTP API is sessionless, so each request is an atomic statement and explicit
209
+ transaction-control statements are rejected. Applications needing a
210
+ multi-statement transaction should use an embedded language binding, where the
211
+ transaction belongs to one database handle.
212
+
199
213
  Applications accepting SQL from users can also call `Database::query_with_limits`
200
214
  directly and choose their own scan, result, memory, and deadline budgets.
201
215
 
@@ -211,6 +225,9 @@ native modules built on the public API. Both are documented in
211
225
  |--|--|
212
226
  | Roadmap | [ROADMAP.md](ROADMAP.md) |
213
227
  | One-million-download plan | [docs/ROADMAP_1M_DOWNLOADS.md](docs/ROADMAP_1M_DOWNLOADS.md) |
228
+ | Monetization thesis | [docs/MONETIZATION.md](docs/MONETIZATION.md) |
229
+ | Enterprise integration foundation | [docs/ENTERPRISE.md](docs/ENTERPRISE.md) |
230
+ | Platform and file support | [docs/SUPPORT.md](docs/SUPPORT.md) |
214
231
  | Contributing | [CONTRIBUTING.md](CONTRIBUTING.md) |
215
232
  | Code of conduct | [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) |
216
233
  | Changelog | [CHANGELOG.md](CHANGELOG.md) |
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "picovolt",
3
3
  "type": "module",
4
4
  "description": "PicoVolt (PVDB): a polymorphic embedded database engine in Rust.",
5
- "version": "1.4.0",
5
+ "version": "1.6.0",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",
package/picovolt.d.ts CHANGED
@@ -7,6 +7,14 @@
7
7
  export class Db {
8
8
  free(): void;
9
9
  [Symbol.dispose](): void;
10
+ /**
11
+ * Begin an explicit multi-statement in-memory transaction.
12
+ */
13
+ beginTransaction(): void;
14
+ /**
15
+ * Commit the active transaction.
16
+ */
17
+ commitTransaction(): void;
10
18
  /**
11
19
  * The most recently committed transaction id, the upper bound for a
12
20
  * `... BEFORE tx` time-travel query.
@@ -21,6 +29,10 @@ export class Db {
21
29
  * [`export`](Db::export)). Writable, with full time-travel history intact.
22
30
  */
23
31
  static fromBytes(bytes: Uint8Array): Db;
32
+ /**
33
+ * Whether an explicit transaction is active.
34
+ */
35
+ inTransaction(): boolean;
24
36
  /**
25
37
  * Create a new, empty in-memory database.
26
38
  */
@@ -40,6 +52,10 @@ export class Db {
40
52
  * `{"done":true}` otherwise. Throws the error message (a string) on failure.
41
53
  */
42
54
  query(sql: string, params: any): string;
55
+ /**
56
+ * Roll back the active transaction.
57
+ */
58
+ rollbackTransaction(): void;
43
59
  /**
44
60
  * A JSON array of the table names in this database (for introspecting an
45
61
  * uploaded `.pvdb` whose schema is unknown).
package/picovolt_bg.js CHANGED
@@ -18,6 +18,24 @@ export class Db {
18
18
  const ptr = this.__destroy_into_raw();
19
19
  wasm.__wbg_db_free(ptr, 0);
20
20
  }
21
+ /**
22
+ * Begin an explicit multi-statement in-memory transaction.
23
+ */
24
+ beginTransaction() {
25
+ const ret = wasm.db_beginTransaction(this.__wbg_ptr);
26
+ if (ret[1]) {
27
+ throw takeFromExternrefTable0(ret[0]);
28
+ }
29
+ }
30
+ /**
31
+ * Commit the active transaction.
32
+ */
33
+ commitTransaction() {
34
+ const ret = wasm.db_commitTransaction(this.__wbg_ptr);
35
+ if (ret[1]) {
36
+ throw takeFromExternrefTable0(ret[0]);
37
+ }
38
+ }
21
39
  /**
22
40
  * The most recently committed transaction id, the upper bound for a
23
41
  * `... BEFORE tx` time-travel query.
@@ -55,6 +73,14 @@ export class Db {
55
73
  }
56
74
  return Db.__wrap(ret[0]);
57
75
  }
76
+ /**
77
+ * Whether an explicit transaction is active.
78
+ * @returns {boolean}
79
+ */
80
+ inTransaction() {
81
+ const ret = wasm.db_inTransaction(this.__wbg_ptr);
82
+ return ret !== 0;
83
+ }
58
84
  /**
59
85
  * Create a new, empty in-memory database.
60
86
  */
@@ -110,6 +136,15 @@ export class Db {
110
136
  wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
111
137
  }
112
138
  }
139
+ /**
140
+ * Roll back the active transaction.
141
+ */
142
+ rollbackTransaction() {
143
+ const ret = wasm.db_rollbackTransaction(this.__wbg_ptr);
144
+ if (ret[1]) {
145
+ throw takeFromExternrefTable0(ret[0]);
146
+ }
147
+ }
113
148
  /**
114
149
  * A JSON array of the table names in this database (for introspecting an
115
150
  * uploaded `.pvdb` whose schema is unknown).
package/picovolt_bg.wasm CHANGED
Binary file
package/sqlite.js CHANGED
@@ -94,19 +94,21 @@ class Database {
94
94
  throw new Error("picovolt: pragma is not supported");
95
95
  }
96
96
 
97
- // Wrap a synchronous callback in an atomic unit. A compact PVDB snapshot is
98
- // restored if the callback throws, matching better-sqlite3's common pattern.
97
+ // Wrap a synchronous callback in an engine transaction, matching
98
+ // better-sqlite3's common pattern.
99
99
  transaction(fn) {
100
100
  if (typeof fn !== "function") throw new TypeError("transaction expects a function");
101
101
  const db = this;
102
102
  function wrapped(...args) {
103
103
  if (db._inTransaction) return fn(...args);
104
- const snapshot = db.serialize();
104
+ db._db.beginTransaction();
105
105
  db._inTransaction = true;
106
106
  try {
107
- return fn(...args);
107
+ const value = fn(...args);
108
+ db._db.commitTransaction();
109
+ return value;
108
110
  } catch (error) {
109
- db._db = Db.fromBytes(snapshot);
111
+ if (db._db.inTransaction()) db._db.rollbackTransaction();
110
112
  throw error;
111
113
  } finally {
112
114
  db._inTransaction = false;