@bullet./paraql 0.1.2 → 0.3.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 +39 -45
- package/binding.c +279 -119
- package/index.js +66 -28
- package/lib/codecs.js +12 -53
- package/lib/constants.js +36 -3
- package/lib/statement.d.ts +0 -11
- package/lib/statement.js +1 -27
- package/lib/vfs.js +31 -97
- package/package.json +3 -8
- package/prebuilds/android-arm/bullet.__paraql.bare +0 -0
- package/prebuilds/android-arm/bullet.__paraql.node +0 -0
- package/prebuilds/android-arm64/bullet.__paraql.bare +0 -0
- package/prebuilds/android-arm64/bullet.__paraql.node +0 -0
- package/prebuilds/android-ia32/bullet.__paraql.bare +0 -0
- package/prebuilds/android-ia32/bullet.__paraql.node +0 -0
- package/prebuilds/android-x64/bullet.__paraql.bare +0 -0
- package/prebuilds/android-x64/bullet.__paraql.node +0 -0
- package/prebuilds/darwin-arm64/bullet.__paraql.bare +0 -0
- package/prebuilds/darwin-arm64/bullet.__paraql.node +0 -0
- package/prebuilds/darwin-x64/bullet.__paraql.bare +0 -0
- package/prebuilds/darwin-x64/bullet.__paraql.node +0 -0
- package/prebuilds/ios-arm64/bullet.__paraql.bare +0 -0
- package/prebuilds/ios-arm64-simulator/bullet.__paraql.bare +0 -0
- package/prebuilds/ios-x64-simulator/bullet.__paraql.bare +0 -0
- package/prebuilds/linux-arm64/bullet.__paraql.bare +0 -0
- package/prebuilds/linux-arm64/bullet.__paraql.node +0 -0
- package/prebuilds/linux-x64/bullet.__paraql.bare +0 -0
- package/prebuilds/linux-x64/bullet.__paraql.node +0 -0
- package/prebuilds/win32-arm64/bullet.__paraql.bare +0 -0
- package/prebuilds/win32-arm64/bullet.__paraql.node +0 -0
- package/prebuilds/win32-x64/bullet.__paraql.bare +0 -0
- package/prebuilds/win32-x64/bullet.__paraql.node +0 -0
package/README.md
CHANGED
|
@@ -2,35 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
ParaQL is a mad-science experiment combining [libSQL](https://github.com/tursodatabase/libsql) (an open-contribution [SQLite](https://github.com/sqlite/sqlite) fork with native vector support) with [Autobase](https://github.com/holepunchto/autobase) (or to be more precise, it's next-gen iteration [Autobee](https://github.com/holepunchto/autobee)) for massively-parallel multi-writer access.
|
|
4
4
|
|
|
5
|
-
It logs every write in a per-instance append-only log (oplog) and applies them in deterministic order to a shared view (the database) ensuring no corruption can occur.
|
|
5
|
+
It logs every write in a per-instance append-only log (oplog) and applies them in deterministic order to a shared view (the database) ensuring no corruption can occur. Performance is impressive, it blows vanilla SQLite out of the water, in part due to flushing to disk less often. That's not an issue though, because by the time database write occurs the operation is already in the oplog, so in case crashes or corruption it can simply be reapplied. Peers who haven't written to the database simply fast-forward (download) the latest database version which on a good connection is even faster.
|
|
6
|
+
|
|
7
|
+
The oplog based design means that disk space used is roughly double (after compaction) that of vanilla SQLite. Operating requirements are considerably higher, because Autobee uses RocksDB as the storage backend and RocksDB doesn't free deleted data immediately as a performance optimization. Periodic compaction, both automatic, and manual, makes this an easily solvable issue.
|
|
6
8
|
|
|
7
9
|
For a better idea of how ParaQL performs and compares to other solutions see [the benchmark](./BENCHMARK.md).
|
|
8
10
|
|
|
9
|
-
ParaQL is developed on [Bare](https://github.com/holepunchto/bare) but it
|
|
11
|
+
ParaQL is developed on [Bare](https://github.com/holepunchto/bare) but it's also tested on Node. Because Bare is multi-platform and supports mobile operating systems as first class citizens, ParaQL runs on recent versions of Android, iOS, macOS, Linux, and Windows.
|
|
10
12
|
|
|
11
13
|
ParaQL supports encrypting the database with a 256-bit key, both on disk and in transport (meaning remote peers need to know the key to read or write into the database). The local-only temporary files are currently encrypted with unique but not random nonces. This is something we're still working on.
|
|
12
14
|
|
|
13
|
-
ParaQL also supports compression with zlib's deflate algorithm. While this slows down writing considerably, it reduces disk space used by ~3x or more, so depending on your environment it might be worth considering. Do note that compression affects every instance of the same database.
|
|
14
|
-
|
|
15
15
|
ParaQL has native support for vector data types and vector search functions with optional indexing. This is courtesy of libSQL and one of the primary reasons ParaQL was made: to support vector similarity search in P2P context.
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
19
19
|
```shell
|
|
20
|
-
npm i paraql
|
|
20
|
+
npm i @bullet./paraql
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
25
25
|
```javascript
|
|
26
26
|
const Corestore = require("corestore")
|
|
27
|
-
const ParaQL = require("paraql")
|
|
27
|
+
const ParaQL = require("@bullet./paraql")
|
|
28
28
|
|
|
29
29
|
const store = new Corestore("./paraql")
|
|
30
30
|
const db = new ParaQL(store)
|
|
31
31
|
|
|
32
|
-
await db.ready()
|
|
33
|
-
|
|
34
32
|
await db.exec(`
|
|
35
33
|
CREATE TABLE people (id INTEGER PRIMARY KEY, name TEXT);
|
|
36
34
|
INSERT INTO people (name) VALUES ('Alice'), ('Bob');
|
|
@@ -42,6 +40,7 @@ for await (const row of select.iterate()) {
|
|
|
42
40
|
console.log(row)
|
|
43
41
|
}
|
|
44
42
|
|
|
43
|
+
await select.finalize()
|
|
45
44
|
await db.close()
|
|
46
45
|
```
|
|
47
46
|
|
|
@@ -62,28 +61,39 @@ Creates a new database or an instance of an existing database if key is provided
|
|
|
62
61
|
|
|
63
62
|
```javascript
|
|
64
63
|
options = {
|
|
65
|
-
cacheSize: 1024,
|
|
66
64
|
name: "paraql.db",
|
|
65
|
+
authorize: null,
|
|
67
66
|
keyPair: null,
|
|
68
67
|
encrypted: false,
|
|
69
68
|
encryptionKey: null,
|
|
70
|
-
compressed: false,
|
|
71
|
-
compressionLevel: 6,
|
|
72
69
|
}
|
|
73
70
|
```
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
The following options should only be used at database creation and set for every instance of the database. Option mismatch will lead to database corruption.
|
|
72
|
+
These options should only be used at database creation and set for every instance of the database. Option mismatch may lead to database corruption and/or sync issues.
|
|
78
73
|
|
|
79
74
|
`name` is the name of the database file, also used as a prefix for temporary files.
|
|
80
75
|
|
|
76
|
+
`authorize` is either null or an (asynchronous) authorizer callback with the following signature:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
;(
|
|
80
|
+
key: Buffer,
|
|
81
|
+
action: ParaQL.AUTHORIZE_ACTION,
|
|
82
|
+
param1: string | null,
|
|
83
|
+
param2: string | null,
|
|
84
|
+
param3: string | null,
|
|
85
|
+
param4: string | null,
|
|
86
|
+
) => Promise<boolean>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
where `key` is the local key of the writer attempting the operation, `action` is numeric action code defined in `ParaQL.AUTHORIZE_ACTION` enum, and params 1 through 4 are [action specific strings (e.g. table name) or null](https://sqlite.org/c3ref/c_alter_table.html).
|
|
90
|
+
|
|
91
|
+
Do note that operations initiated by initial instance (root node) and all read-only operations are always allowed and authorizer callback is **NOT** invoked for them.
|
|
92
|
+
|
|
81
93
|
`keyPair`, if provided, is the signing key pair for the local writer in the form `{ publicKey: <32-byte Buffer>, secretKey: <32-byte Buffer> }` .
|
|
82
94
|
|
|
83
95
|
If `encrypted` is true and `encryptionKey` is provided as 32-byte buffer, it is used to encrypt the database.
|
|
84
96
|
|
|
85
|
-
`compressed` enables database compression and `compressionLevel` controls how many resources are used for the compression. It should be between 0 and 9 (inclusive).
|
|
86
|
-
|
|
87
97
|
### `db.name`
|
|
88
98
|
|
|
89
99
|
The name of the database file.
|
|
@@ -130,11 +140,11 @@ Revoke write access from another instance of the database. `key` should be the l
|
|
|
130
140
|
|
|
131
141
|
### `db.replicate(isInitiatorOrStream)`
|
|
132
142
|
|
|
133
|
-
Creates a replication stream that can be piped over any streamable transport. `isInitiatorOrStream` can be a boolean indicating whether this instance initiated Noise handshake or another replication stream.
|
|
143
|
+
Creates a replication stream that can be piped over any streamable transport. `isInitiatorOrStream` can be a boolean indicating whether this instance initiated Noise handshake, or another replication stream.
|
|
134
144
|
|
|
135
145
|
### `await db.compact()`
|
|
136
146
|
|
|
137
|
-
Compacts database and removes stale data. This operation is local only and can reduce disk space usage by
|
|
147
|
+
Compacts database and removes stale data. This operation is local only and can reduce disk space usage by up to 30x or more depending on the data stored and settings of the database. Compaction happens automatically but you might want to run this periodically, when idle.
|
|
138
148
|
|
|
139
149
|
### `const info = await db.info()`
|
|
140
150
|
|
|
@@ -152,41 +162,19 @@ Get information about disk space usage. Returned object has all properties in by
|
|
|
152
162
|
|
|
153
163
|
Execute given SQL statement(s) without checking return values.
|
|
154
164
|
|
|
155
|
-
This is a convenience method.
|
|
165
|
+
This is a convenience method.
|
|
156
166
|
|
|
157
167
|
### `const stmt = await db.prepare(sql)`
|
|
158
168
|
|
|
159
169
|
Prepare SQL statement `stmt` from the first statement in `sql`. If `sql` contains more than one statement tailing statements are discarded.
|
|
160
170
|
|
|
161
|
-
Prepared statements are cached to improve performance, so calling `db.prepare()` twice with the same `sql` will return previously cached `stmt`.
|
|
162
|
-
|
|
163
|
-
All prepared statements are finalized when calling `db.close()`.
|
|
164
|
-
|
|
165
171
|
### `stmt.sourceSQL`
|
|
166
172
|
|
|
167
173
|
The SQL string used to initialize this prepared statement.
|
|
168
174
|
|
|
169
|
-
### `stmt.batching`
|
|
170
|
-
|
|
171
|
-
Whether this statement is in batching mode.
|
|
172
|
-
|
|
173
|
-
### `stmt.batch(on)`
|
|
174
|
-
|
|
175
|
-
Toggle batching mode, `on` is a boolean.
|
|
176
|
-
|
|
177
|
-
When in batching mode `stmt.run()` queries are queued up in memory and only executed when calling `stmt.flush()`. This can greatly speed up both write and sync performance. However since they aren't executed immediately, you can't query the data until you flush it.
|
|
178
|
-
|
|
179
|
-
### `const result = await stmt.flush()`
|
|
180
|
-
|
|
181
|
-
Flush queued up queries writing them to disk.
|
|
182
|
-
|
|
183
|
-
Returns an object in the form `{ changes: number, lastInsertRowid: number, errors: number }`.
|
|
184
|
-
|
|
185
175
|
### `await stmt.finalize()`
|
|
186
176
|
|
|
187
|
-
Finalize a statement freeing up resources used
|
|
188
|
-
|
|
189
|
-
You should generally not need to call this method in normal usage.
|
|
177
|
+
Finalize a statement freeing up resources used.
|
|
190
178
|
|
|
191
179
|
### `const rows = await stmt.all(...params)`
|
|
192
180
|
|
|
@@ -198,16 +186,22 @@ Returns an array of row objects keyed by column name.
|
|
|
198
186
|
|
|
199
187
|
Same as `stmt.all()` except it only returns the first row.
|
|
200
188
|
|
|
201
|
-
### `const result = stmt.run(...params)`
|
|
189
|
+
### `const result = await stmt.run(...params)`
|
|
202
190
|
|
|
203
191
|
Execute a statement with given params and return an object in the form `{ changes: number, lastInsertRowid: number }`.
|
|
204
192
|
|
|
205
|
-
In batching mode returns `null`.
|
|
206
|
-
|
|
207
193
|
### `for await (const row of stmt.iterate(...params))`
|
|
208
194
|
|
|
209
195
|
Execute a statement with given params and return rows one by one.
|
|
210
196
|
|
|
197
|
+
### `const buffer = await db.serialize()`
|
|
198
|
+
|
|
199
|
+
Return an unencrypted, uncompressed, serialized version of the database that can be (re-)used with any SQLite3 compatible application or library.
|
|
200
|
+
|
|
201
|
+
### `const db = await ParaQL.deserialize(buffer)`
|
|
202
|
+
|
|
203
|
+
Initiate a new ParaQL instance with any SQLite3 compatible database contained in the buffer.
|
|
204
|
+
|
|
211
205
|
## License
|
|
212
206
|
|
|
213
207
|
Apache-2.0
|