@bullet./paraql 0.1.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/CMakeLists.txt +103 -0
- package/LICENSE +201 -0
- package/NOTICE +13 -0
- package/README.md +213 -0
- package/binding.c +2646 -0
- package/binding.js +3 -0
- package/index.js +148 -0
- package/lib/codecs.js +138 -0
- package/lib/constants.js +10 -0
- package/lib/deferred.js +8 -0
- package/lib/encryption.js +39 -0
- package/lib/errors.js +35 -0
- package/lib/statement.d.ts +55 -0
- package/lib/statement.js +168 -0
- package/lib/vfs.js +558 -0
- package/package.json +98 -0
package/binding.js
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
const { Buffer } = require("buffer")
|
|
2
|
+
const ReadyResource = require("ready-resource")
|
|
3
|
+
|
|
4
|
+
const binding = require("./binding")
|
|
5
|
+
const errors = require("./lib/errors")
|
|
6
|
+
const ParaQLStatement = require("./lib/statement")
|
|
7
|
+
const ParaVFS = require("./lib/vfs")
|
|
8
|
+
|
|
9
|
+
module.exports = exports = class ParaQL extends ReadyResource {
|
|
10
|
+
constructor(store, key = null, options = {}) {
|
|
11
|
+
if (isObject(key)) {
|
|
12
|
+
options = key
|
|
13
|
+
key = null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { cacheSize = 1024, ...opts } = options
|
|
17
|
+
|
|
18
|
+
super()
|
|
19
|
+
|
|
20
|
+
this._handle = null
|
|
21
|
+
this._vfs = new ParaVFS(this, store, key, opts)
|
|
22
|
+
this._cacheSize = cacheSize
|
|
23
|
+
this._cache = new Map()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get name() {
|
|
27
|
+
return this._vfs.name
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get key() {
|
|
31
|
+
return this._vfs.key
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get local() {
|
|
35
|
+
return this._vfs.local
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get discoveryKey() {
|
|
39
|
+
return this._vfs.discoveryKey
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get encryptionKey() {
|
|
43
|
+
return this._vfs.encryptionKey
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get writable() {
|
|
47
|
+
return this._vfs.writable
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get encrypted() {
|
|
51
|
+
return this._vfs.encrypted
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async _open() {
|
|
55
|
+
await this._vfs.ready()
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
this._handle = await binding.open("paraql.db")
|
|
59
|
+
} catch (err) {
|
|
60
|
+
throw errors.from(err)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async _close() {
|
|
65
|
+
if (this.opened) {
|
|
66
|
+
try {
|
|
67
|
+
for (const stmt of this._cache.values()) {
|
|
68
|
+
await stmt.finalize()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this._cache.clear()
|
|
72
|
+
await binding.close(this._handle)
|
|
73
|
+
} catch (err) {
|
|
74
|
+
throw errors.from(err)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await this._vfs.close()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async addWriter(key) {
|
|
82
|
+
await this._vfs.addWriter(key)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async removeWriter(key) {
|
|
86
|
+
await this._vfs.removeWriter(key)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
replicate(isInitiator) {
|
|
90
|
+
return this._vfs.replicate(isInitiator)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async compact() {
|
|
94
|
+
return this._vfs.compact()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async info() {
|
|
98
|
+
return this._vfs.info()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async _exec(sql) {
|
|
102
|
+
try {
|
|
103
|
+
await binding.exec(this._handle, sql)
|
|
104
|
+
} catch (err) {
|
|
105
|
+
throw errors.from(err)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async exec(sql) {
|
|
110
|
+
if (!this.opened) await this.ready()
|
|
111
|
+
|
|
112
|
+
if (this.closed) throw errors.ALREADY_CLOSED()
|
|
113
|
+
|
|
114
|
+
await this._vfs.exec(sql)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async prepare(sql) {
|
|
118
|
+
if (!this.opened) await this.ready()
|
|
119
|
+
|
|
120
|
+
if (this.closed) throw errors.ALREADY_CLOSED()
|
|
121
|
+
|
|
122
|
+
let stmt = this._cache.get(sql)
|
|
123
|
+
|
|
124
|
+
if (stmt) {
|
|
125
|
+
this._cache.delete(sql)
|
|
126
|
+
this._cache.set(sql, stmt)
|
|
127
|
+
return stmt
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
stmt = new ParaQLStatement(this, sql)
|
|
131
|
+
|
|
132
|
+
await stmt.ready()
|
|
133
|
+
|
|
134
|
+
this._cache.set(sql, stmt)
|
|
135
|
+
|
|
136
|
+
if (this._cache.size > this._cacheSize) {
|
|
137
|
+
const oldest = this._cache.values().next().value
|
|
138
|
+
|
|
139
|
+
await oldest.finalize()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return stmt
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function isObject(o) {
|
|
147
|
+
return typeof o === "object" && o && !Buffer.isBuffer(o)
|
|
148
|
+
}
|
package/lib/codecs.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const c = require("compact-encoding")
|
|
2
|
+
const IndexEncoder = require("index-encoder")
|
|
3
|
+
const zlib = require("zlib")
|
|
4
|
+
|
|
5
|
+
const { OPERATION, PAGE_SIZE } = require("./constants")
|
|
6
|
+
|
|
7
|
+
const op = {
|
|
8
|
+
preencode(state, operation) {
|
|
9
|
+
c.uint8.preencode(state, operation.type)
|
|
10
|
+
|
|
11
|
+
switch (operation.type) {
|
|
12
|
+
case OPERATION.WRITER_ADD:
|
|
13
|
+
case OPERATION.WRITER_DEL: {
|
|
14
|
+
c.fixed32.preencode(state, operation.key)
|
|
15
|
+
break
|
|
16
|
+
}
|
|
17
|
+
case OPERATION.EXEC: {
|
|
18
|
+
c.string.preencode(state, operation.sql)
|
|
19
|
+
break
|
|
20
|
+
}
|
|
21
|
+
case OPERATION.RUN: {
|
|
22
|
+
c.string.preencode(state, operation.sql)
|
|
23
|
+
c.any.preencode(state, operation.named)
|
|
24
|
+
c.any.preencode(state, operation.positional)
|
|
25
|
+
break
|
|
26
|
+
}
|
|
27
|
+
case OPERATION.STMT: {
|
|
28
|
+
c.string.preencode(state, operation.sql)
|
|
29
|
+
}
|
|
30
|
+
case OPERATION.BATCH: {
|
|
31
|
+
c.any.preencode(state, operation.named)
|
|
32
|
+
c.any.preencode(state, operation.positional)
|
|
33
|
+
}
|
|
34
|
+
case OPERATION.FLUSH: {
|
|
35
|
+
break
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
encode(state, operation) {
|
|
40
|
+
c.uint8.encode(state, operation.type)
|
|
41
|
+
|
|
42
|
+
switch (operation.type) {
|
|
43
|
+
case OPERATION.WRITER_ADD:
|
|
44
|
+
case OPERATION.WRITER_DEL: {
|
|
45
|
+
c.fixed32.encode(state, operation.key)
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
case OPERATION.EXEC: {
|
|
49
|
+
c.string.encode(state, operation.sql)
|
|
50
|
+
break
|
|
51
|
+
}
|
|
52
|
+
case OPERATION.RUN: {
|
|
53
|
+
c.string.encode(state, operation.sql)
|
|
54
|
+
c.any.encode(state, operation.named)
|
|
55
|
+
c.any.encode(state, operation.positional)
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
case OPERATION.STMT: {
|
|
59
|
+
c.string.encode(state, operation.sql)
|
|
60
|
+
}
|
|
61
|
+
case OPERATION.BATCH: {
|
|
62
|
+
c.any.encode(state, operation.named)
|
|
63
|
+
c.any.encode(state, operation.positional)
|
|
64
|
+
}
|
|
65
|
+
case OPERATION.FLUSH: {
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
decode(state) {
|
|
71
|
+
const type = c.uint8.decode(state)
|
|
72
|
+
|
|
73
|
+
const operation = { type }
|
|
74
|
+
|
|
75
|
+
switch (type) {
|
|
76
|
+
case OPERATION.WRITER_ADD:
|
|
77
|
+
case OPERATION.WRITER_DEL: {
|
|
78
|
+
operation.key = c.fixed32.decode(state)
|
|
79
|
+
break
|
|
80
|
+
}
|
|
81
|
+
case OPERATION.EXEC: {
|
|
82
|
+
operation.sql = c.string.decode(state)
|
|
83
|
+
break
|
|
84
|
+
}
|
|
85
|
+
case OPERATION.RUN: {
|
|
86
|
+
operation.sql = c.string.decode(state)
|
|
87
|
+
operation.named = c.any.decode(state)
|
|
88
|
+
operation.positional = c.any.decode(state)
|
|
89
|
+
break
|
|
90
|
+
}
|
|
91
|
+
case OPERATION.STMT: {
|
|
92
|
+
operation.sql = c.string.decode(state)
|
|
93
|
+
}
|
|
94
|
+
case OPERATION.BATCH: {
|
|
95
|
+
operation.named = c.any.decode(state)
|
|
96
|
+
operation.positional = c.any.decode(state)
|
|
97
|
+
}
|
|
98
|
+
case OPERATION.FLUSH: {
|
|
99
|
+
break
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return operation
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports.PageKey = new IndexEncoder([IndexEncoder.STRING, IndexEncoder.UINT])
|
|
108
|
+
|
|
109
|
+
module.exports.Operation = {
|
|
110
|
+
encode(operation) {
|
|
111
|
+
return c.encode(op, operation)
|
|
112
|
+
},
|
|
113
|
+
decode(operation) {
|
|
114
|
+
return c.decode(op, operation)
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports.deflate = async function (buffer, options) {
|
|
119
|
+
return new Promise((resolve, reject) =>
|
|
120
|
+
zlib.deflate(buffer, { chunkSize: PAGE_SIZE, ...options }, (err, result) => {
|
|
121
|
+
if (err) {
|
|
122
|
+
return reject(result)
|
|
123
|
+
}
|
|
124
|
+
resolve(result)
|
|
125
|
+
}),
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports.inflate = async function (buffer, options) {
|
|
130
|
+
return new Promise((resolve, reject) =>
|
|
131
|
+
zlib.inflate(buffer, { chunkSize: PAGE_SIZE, ...options }, (err, result) => {
|
|
132
|
+
if (err) {
|
|
133
|
+
return reject(result)
|
|
134
|
+
}
|
|
135
|
+
resolve(result)
|
|
136
|
+
}),
|
|
137
|
+
)
|
|
138
|
+
}
|
package/lib/constants.js
ADDED
package/lib/deferred.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const { Buffer } = require("buffer")
|
|
2
|
+
const c = require("compact-encoding")
|
|
3
|
+
const sodium = require("sodium-native")
|
|
4
|
+
|
|
5
|
+
const errors = require("./errors")
|
|
6
|
+
|
|
7
|
+
module.exports = class ParaQLEncryption {
|
|
8
|
+
constructor(key, encryptionKey) {
|
|
9
|
+
if (
|
|
10
|
+
!Buffer.isBuffer(encryptionKey)
|
|
11
|
+
|| encryptionKey.byteLength !== sodium.crypto_stream_KEYBYTES
|
|
12
|
+
) {
|
|
13
|
+
throw errors.INVALID_ARGUMENT("`encryptioKey` must be 32 byte buffer")
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
this._key = key
|
|
17
|
+
this._encryptionKey = encryptionKey
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_nonce(name, index) {
|
|
21
|
+
const _name = Buffer.from(name)
|
|
22
|
+
const _index = c.encode(c.uint64, index)
|
|
23
|
+
const result = Buffer.alloc(sodium.crypto_stream_NONCEBYTES)
|
|
24
|
+
|
|
25
|
+
sodium.crypto_generichash(result, Buffer.concat([this._key, _name, _index]))
|
|
26
|
+
|
|
27
|
+
return result
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
encrypt(page, filename, index) {
|
|
31
|
+
sodium.crypto_stream_xor(page, page, this._nonce(filename, index), this._encryptionKey)
|
|
32
|
+
return page
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
decrypt(page, filename, index) {
|
|
36
|
+
sodium.crypto_stream_xor(page, page, this._nonce(filename, index), this._encryptionKey)
|
|
37
|
+
return page
|
|
38
|
+
}
|
|
39
|
+
}
|
package/lib/errors.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module.exports = class ParaQLError extends Error {
|
|
2
|
+
constructor(message, fn = ParaQLError, code = fn.name) {
|
|
3
|
+
super(`${code}: ${message}`)
|
|
4
|
+
|
|
5
|
+
this.code = code
|
|
6
|
+
|
|
7
|
+
if (Error.captureStackTrace) Error.captureStackTrace(this, fn)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get name() {
|
|
11
|
+
return "ParaQLError"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static INTERNAL(message) {
|
|
15
|
+
return new ParaQLError(message, ParaQLError.INTERNAL)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static ALREADY_CLOSED(message = "Database has already been closed") {
|
|
19
|
+
return new ParaQLError(message, ParaQLError.ALREADY_CLOSED)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static INVALID_ARGUMENT(message) {
|
|
23
|
+
return new ParaQLError(message, ParaQLError.INVALID_ARGUMENT)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static from(err) {
|
|
27
|
+
if (err instanceof ParaQLError) return err
|
|
28
|
+
|
|
29
|
+
if (err instanceof TypeError) {
|
|
30
|
+
return ParaQLError.INVALID_ARGUMENT(err.message)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return new ParaQLError(err.message, ParaQLError.from, err.code || "ERROR")
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import ReadyResource from "ready-resource"
|
|
2
|
+
import Buffer from "bare-buffer"
|
|
3
|
+
|
|
4
|
+
interface ParaQLStatement extends ReadyResource {
|
|
5
|
+
readonly sourceSQL: string
|
|
6
|
+
readonly batching: boolean
|
|
7
|
+
|
|
8
|
+
batch(on: boolean): void
|
|
9
|
+
|
|
10
|
+
flush(): Promise<ParaQLStatement.FlushResult>
|
|
11
|
+
|
|
12
|
+
finalize(): Promise<void>
|
|
13
|
+
|
|
14
|
+
all<T extends ParaQLStatement.Row = ParaQLStatement.Row>(
|
|
15
|
+
...params: ParaQLStatement.Parameters
|
|
16
|
+
): Promise<T[]>
|
|
17
|
+
|
|
18
|
+
get<T extends ParaQLStatement.Row = ParaQLStatement.Row>(
|
|
19
|
+
...params: ParaQLStatement.Parameters
|
|
20
|
+
): Promise<T | undefined>
|
|
21
|
+
|
|
22
|
+
run(...params: ParaQLStatement.Parameters): Promise<ParaQLStatement.RunResult | undefined>
|
|
23
|
+
|
|
24
|
+
iterate<T extends ParaQLStatement.Row = ParaQLStatement.Row>(
|
|
25
|
+
...params: ParaQLStatement.Parameters
|
|
26
|
+
): AsyncIterableIterator<T>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
declare class ParaQLStatement {}
|
|
30
|
+
|
|
31
|
+
declare namespace ParaQLStatement {
|
|
32
|
+
export type Value = null | number | string | Buffer
|
|
33
|
+
|
|
34
|
+
export type BindValue =
|
|
35
|
+
null | undefined | number | bigint | string | ArrayBuffer | ArrayBufferView
|
|
36
|
+
|
|
37
|
+
export type Row = Record<string, Value>
|
|
38
|
+
|
|
39
|
+
export type NamedParameters = Record<string, BindValue>
|
|
40
|
+
|
|
41
|
+
export type Parameters = [NamedParameters, ...BindValue[]] | BindValue[]
|
|
42
|
+
|
|
43
|
+
export interface RunResult {
|
|
44
|
+
changes: number
|
|
45
|
+
lastInsertRowid: number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface FlushResult {
|
|
49
|
+
changes: number
|
|
50
|
+
lastInsertRowid: number
|
|
51
|
+
errors: number
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export = ParaQLStatement
|
package/lib/statement.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const { Buffer } = require("buffer")
|
|
2
|
+
const ReadyResource = require("ready-resource")
|
|
3
|
+
|
|
4
|
+
const binding = require("../binding")
|
|
5
|
+
const errors = require("./errors")
|
|
6
|
+
|
|
7
|
+
module.exports = class ParaQLStatement extends ReadyResource {
|
|
8
|
+
constructor(db, sql) {
|
|
9
|
+
super()
|
|
10
|
+
|
|
11
|
+
this._db = db
|
|
12
|
+
this._sourceSQL = sql
|
|
13
|
+
this._handle = null
|
|
14
|
+
this._queue = null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async _open() {
|
|
18
|
+
try {
|
|
19
|
+
this._handle = await binding.prepare(this._db._handle, this._sourceSQL)
|
|
20
|
+
} catch (err) {
|
|
21
|
+
throw errors.from(err)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async _close() {
|
|
26
|
+
if (this.opened) {
|
|
27
|
+
try {
|
|
28
|
+
await binding.finalize(this._handle)
|
|
29
|
+
this._handle = null
|
|
30
|
+
this._queue = null
|
|
31
|
+
this._db._cache.delete(this._sourceSQL)
|
|
32
|
+
} catch (err) {
|
|
33
|
+
throw errors.from(err)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get sourceSQL() {
|
|
39
|
+
return this._sourceSQL
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get batching() {
|
|
43
|
+
return !!this._queue
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
batch(on) {
|
|
47
|
+
if (on === false) {
|
|
48
|
+
this._queue = null
|
|
49
|
+
} else {
|
|
50
|
+
this._queue = this._queue ?? []
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async finalize() {
|
|
55
|
+
await this.close()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async flush() {
|
|
59
|
+
const queue = this._queue
|
|
60
|
+
this._queue = []
|
|
61
|
+
return this._db._vfs.flush(this._sourceSQL, queue)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async _bind(named, positional) {
|
|
65
|
+
for (const key in named) {
|
|
66
|
+
if (Array.isArray(named[key])) named[key] = JSON.stringify(named[key])
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (let i = 0; i < positional.length; i++) {
|
|
70
|
+
if (Array.isArray(positional[i])) positional[i] = JSON.stringify(positional[i])
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return binding.bind(this._handle, named, positional)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async all(...params) {
|
|
77
|
+
if (this.closed) throw errors.ALREADY_CLOSED()
|
|
78
|
+
|
|
79
|
+
const rows = []
|
|
80
|
+
|
|
81
|
+
for await (const row of this.iterate(...params)) {
|
|
82
|
+
rows.push(row)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return rows
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async get(...params) {
|
|
89
|
+
if (this.closed) throw errors.ALREADY_CLOSED()
|
|
90
|
+
|
|
91
|
+
const [named, positional] = splitParameters(params)
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
await this._bind(named, positional)
|
|
95
|
+
const row = await binding.step(this._handle)
|
|
96
|
+
return row === undefined ? undefined : wrapBlobRow(row)
|
|
97
|
+
} catch (err) {
|
|
98
|
+
throw errors.from(err)
|
|
99
|
+
} finally {
|
|
100
|
+
await binding.reset(this._handle)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async _run(named, positional) {
|
|
105
|
+
try {
|
|
106
|
+
await this._bind(named, positional)
|
|
107
|
+
return await binding.run(this._handle)
|
|
108
|
+
} catch (err) {
|
|
109
|
+
throw errors.from(err)
|
|
110
|
+
} finally {
|
|
111
|
+
await binding.reset(this._handle)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async run(...params) {
|
|
116
|
+
if (this.closed) throw errors.ALREADY_CLOSED()
|
|
117
|
+
|
|
118
|
+
const [named, positional] = splitParameters(params)
|
|
119
|
+
|
|
120
|
+
if (this._queue) {
|
|
121
|
+
this._queue.push({ named, positional })
|
|
122
|
+
return null
|
|
123
|
+
} else {
|
|
124
|
+
return this._db._vfs.run(this._sourceSQL, named, positional)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async *iterate(...params) {
|
|
129
|
+
if (this.closed) throw errors.ALREADY_CLOSED()
|
|
130
|
+
|
|
131
|
+
const [named, positional] = splitParameters(params)
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
await this._bind(named, positional)
|
|
135
|
+
|
|
136
|
+
let row
|
|
137
|
+
while ((row = await binding.step(this._handle)) !== undefined) {
|
|
138
|
+
yield wrapBlobRow(row)
|
|
139
|
+
}
|
|
140
|
+
} catch (err) {
|
|
141
|
+
throw errors.from(err)
|
|
142
|
+
} finally {
|
|
143
|
+
await binding.reset(this._handle)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function wrapBlobRow(row) {
|
|
149
|
+
for (const key in row) {
|
|
150
|
+
if (row[key] instanceof ArrayBuffer) row[key] = Buffer.from(row[key])
|
|
151
|
+
}
|
|
152
|
+
return row
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function splitParameters(params) {
|
|
156
|
+
if (params.length === 0) return [null, params]
|
|
157
|
+
if (!isNamedParameters(params[0])) return [null, params]
|
|
158
|
+
return [params[0], params.slice(1)]
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function isNamedParameters(value) {
|
|
162
|
+
if (value === null) return false
|
|
163
|
+
if (typeof value !== "object") return false
|
|
164
|
+
if (Array.isArray(value)) return false
|
|
165
|
+
if (ArrayBuffer.isView(value)) return false
|
|
166
|
+
if (value instanceof ArrayBuffer) return false
|
|
167
|
+
return true
|
|
168
|
+
}
|