picovolt 1.5.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.5.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)
@@ -58,6 +58,7 @@ Linux and Windows. Changes are tracked in [CHANGELOG.md](CHANGELOG.md).
58
58
  | [`engine/interp.rs`](src/engine/interp.rs) | `pv-wasm`: a from-scratch WASM interpreter (integer subset) |
59
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
@@ -128,8 +133,9 @@ FROM t [WHERE <pred>] [GROUP BY cols] [HAVING <pred>] [BEFORE tx]
128
133
  `AND`, `OR`, and parentheses. Integer and decimal values compare by magnitude.
129
134
  Two-table equality `INNER`/`LEFT JOIN` queries support qualified references,
130
135
  projection, aliases, `DISTINCT`, filters, ordering, and pagination. Rust callers
131
- can cache `Database::prepare(...)` templates and use
132
- atomic `Database::transaction(...)` closures with in-memory databases.
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.
133
139
  Durability is selectable via `Database::set_durability` (`Fast` OS-cache default,
134
140
  or crash-safe `Sync` with fsync and an atomic manifest).
135
141
 
@@ -140,8 +146,9 @@ around 33k rows/s, linear), larger-than-RAM reads through a bounded buffer pool
140
146
  lookups roughly 11,000 times faster than a scan, plus range predicates), MVCC
141
147
  time-travel, opt-in crash-safe durability (`Durability::Sync`), and a fast
142
148
  compile-and-publish path (CAS dedup, columnar compression, memory-mappable
143
- single-file artifacts). Current limits include no filesystem `BEGIN`/`COMMIT`
144
- 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.
145
152
 
146
153
  ## Install and distribution
147
154
 
@@ -149,7 +156,7 @@ transactions, only basic two-table equality joins, and no concurrent writers.
149
156
  |--------|-----|
150
157
  | **Rust** (crates.io) | `cargo add picovolt` |
151
158
  | **JavaScript / npm** (WebAssembly, browser and Node) | `npm install picovolt` |
152
- | **Python** (native wheels) | `python -m pip install picovolt` (after the first PyPI release) |
159
+ | **Python** (native wheels) | `python -m pip install picovolt` |
153
160
  | **C / Go** (native, via the C ABI) | `cargo build --release --features capi`, then see [`bindings/`](bindings) |
154
161
  | **In-memory** (native, no filesystem) | `Database::open_memory()`, export with `bake_to_bytes()` |
155
162
 
@@ -172,7 +179,8 @@ JavaScript API (`import Database from "picovolt/sqlite"`), a Python DB-API 2.0
172
179
  module (`import picovolt.dbapi2 as sqlite`), and the Go `database/sql` driver
173
180
  ([`bindings/go/pvsql`](bindings/go/pvsql)). Shared limits include positional `?`
174
181
  only and the intentionally compact SQL grammar; JavaScript and in-memory Rust
175
- 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.
176
184
 
177
185
  ## Server mode
178
186
 
@@ -197,6 +205,11 @@ result rows, and response size are bounded. TLS is not built in, so network
197
205
  deployments still belong behind a TLS-terminating reverse proxy. See
198
206
  [src/bin/server.rs](src/bin/server.rs).
199
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
+
200
213
  Applications accepting SQL from users can also call `Database::query_with_limits`
201
214
  directly and choose their own scan, result, memory, and deadline budgets.
202
215
 
@@ -212,6 +225,8 @@ native modules built on the public API. Both are documented in
212
225
  |--|--|
213
226
  | Roadmap | [ROADMAP.md](ROADMAP.md) |
214
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) |
215
230
  | Platform and file support | [docs/SUPPORT.md](docs/SUPPORT.md) |
216
231
  | Contributing | [CONTRIBUTING.md](CONTRIBUTING.md) |
217
232
  | Code of conduct | [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.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.5.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;