@gjsify/sqlite 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/README.md +23 -0
- package/lib/esm/constants.js +140 -0
- package/lib/esm/data-model-reader.js +82 -0
- package/lib/esm/database-sync.js +403 -0
- package/lib/esm/errors.js +70 -0
- package/lib/esm/index.js +8 -0
- package/lib/esm/param-binding.js +114 -0
- package/lib/esm/statement-sync.js +292 -0
- package/lib/esm/types.js +0 -0
- package/lib/types/constants.d.ts +92 -0
- package/lib/types/data-model-reader.d.ts +8 -0
- package/lib/types/database-sync.d.ts +25 -0
- package/lib/types/errors.d.ts +34 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/param-binding.d.ts +6 -0
- package/lib/types/statement-sync.d.ts +18 -0
- package/lib/types/types.d.ts +31 -0
- package/package.json +44 -0
- package/src/constants.ts +101 -0
- package/src/data-model-reader.ts +106 -0
- package/src/data-types.spec.ts +143 -0
- package/src/database-sync.spec.ts +267 -0
- package/src/database-sync.ts +479 -0
- package/src/errors.ts +78 -0
- package/src/index.spec.ts +13 -0
- package/src/index.ts +8 -0
- package/src/param-binding.ts +148 -0
- package/src/statement-sync.spec.ts +192 -0
- package/src/statement-sync.ts +343 -0
- package/src/test.mts +8 -0
- package/src/types.ts +38 -0
- package/tsconfig.json +31 -0
- package/tsconfig.tsbuildinfo +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @gjsify/sqlite
|
|
2
|
+
|
|
3
|
+
GJS implementation of the Node.js `sqlite` module.
|
|
4
|
+
|
|
5
|
+
Part of the [gjsify](https://github.com/gjsify/gjsify) project — Node.js and Web APIs for GJS (GNOME JavaScript).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @gjsify/sqlite
|
|
11
|
+
# or
|
|
12
|
+
yarn add @gjsify/sqlite
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import sqlite from '@gjsify/sqlite';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## License
|
|
22
|
+
|
|
23
|
+
MIT
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const SQLITE_CHANGESET_OMIT = 0;
|
|
2
|
+
const SQLITE_CHANGESET_REPLACE = 1;
|
|
3
|
+
const SQLITE_CHANGESET_ABORT = 2;
|
|
4
|
+
const SQLITE_CHANGESET_DATA = 1;
|
|
5
|
+
const SQLITE_CHANGESET_NOTFOUND = 2;
|
|
6
|
+
const SQLITE_CHANGESET_CONFLICT = 3;
|
|
7
|
+
const SQLITE_CHANGESET_CONSTRAINT = 4;
|
|
8
|
+
const SQLITE_CHANGESET_FOREIGN_KEY = 5;
|
|
9
|
+
const SQLITE_OK = 0;
|
|
10
|
+
const SQLITE_DENY = 1;
|
|
11
|
+
const SQLITE_IGNORE = 2;
|
|
12
|
+
const SQLITE_CREATE_INDEX = 1;
|
|
13
|
+
const SQLITE_CREATE_TABLE = 2;
|
|
14
|
+
const SQLITE_CREATE_TEMP_INDEX = 3;
|
|
15
|
+
const SQLITE_CREATE_TEMP_TABLE = 4;
|
|
16
|
+
const SQLITE_CREATE_TEMP_TRIGGER = 5;
|
|
17
|
+
const SQLITE_CREATE_TEMP_VIEW = 6;
|
|
18
|
+
const SQLITE_CREATE_TRIGGER = 7;
|
|
19
|
+
const SQLITE_CREATE_VIEW = 8;
|
|
20
|
+
const SQLITE_DELETE = 9;
|
|
21
|
+
const SQLITE_DROP_INDEX = 10;
|
|
22
|
+
const SQLITE_DROP_TABLE = 11;
|
|
23
|
+
const SQLITE_DROP_TEMP_INDEX = 12;
|
|
24
|
+
const SQLITE_DROP_TEMP_TABLE = 13;
|
|
25
|
+
const SQLITE_DROP_TEMP_TRIGGER = 14;
|
|
26
|
+
const SQLITE_DROP_TEMP_VIEW = 15;
|
|
27
|
+
const SQLITE_DROP_TRIGGER = 16;
|
|
28
|
+
const SQLITE_DROP_VIEW = 17;
|
|
29
|
+
const SQLITE_INSERT = 18;
|
|
30
|
+
const SQLITE_PRAGMA = 19;
|
|
31
|
+
const SQLITE_READ = 20;
|
|
32
|
+
const SQLITE_SELECT = 21;
|
|
33
|
+
const SQLITE_TRANSACTION = 22;
|
|
34
|
+
const SQLITE_UPDATE = 23;
|
|
35
|
+
const SQLITE_ATTACH = 24;
|
|
36
|
+
const SQLITE_DETACH = 25;
|
|
37
|
+
const SQLITE_ALTER_TABLE = 26;
|
|
38
|
+
const SQLITE_REINDEX = 27;
|
|
39
|
+
const SQLITE_ANALYZE = 28;
|
|
40
|
+
const SQLITE_CREATE_VTABLE = 29;
|
|
41
|
+
const SQLITE_DROP_VTABLE = 30;
|
|
42
|
+
const SQLITE_FUNCTION = 31;
|
|
43
|
+
const SQLITE_SAVEPOINT = 32;
|
|
44
|
+
const SQLITE_COPY = 0;
|
|
45
|
+
const SQLITE_RECURSIVE = 33;
|
|
46
|
+
const constants = {
|
|
47
|
+
SQLITE_CHANGESET_OMIT,
|
|
48
|
+
SQLITE_CHANGESET_REPLACE,
|
|
49
|
+
SQLITE_CHANGESET_ABORT,
|
|
50
|
+
SQLITE_CHANGESET_DATA,
|
|
51
|
+
SQLITE_CHANGESET_NOTFOUND,
|
|
52
|
+
SQLITE_CHANGESET_CONFLICT,
|
|
53
|
+
SQLITE_CHANGESET_CONSTRAINT,
|
|
54
|
+
SQLITE_CHANGESET_FOREIGN_KEY,
|
|
55
|
+
SQLITE_OK,
|
|
56
|
+
SQLITE_DENY,
|
|
57
|
+
SQLITE_IGNORE,
|
|
58
|
+
SQLITE_CREATE_INDEX,
|
|
59
|
+
SQLITE_CREATE_TABLE,
|
|
60
|
+
SQLITE_CREATE_TEMP_INDEX,
|
|
61
|
+
SQLITE_CREATE_TEMP_TABLE,
|
|
62
|
+
SQLITE_CREATE_TEMP_TRIGGER,
|
|
63
|
+
SQLITE_CREATE_TEMP_VIEW,
|
|
64
|
+
SQLITE_CREATE_TRIGGER,
|
|
65
|
+
SQLITE_CREATE_VIEW,
|
|
66
|
+
SQLITE_DELETE,
|
|
67
|
+
SQLITE_DROP_INDEX,
|
|
68
|
+
SQLITE_DROP_TABLE,
|
|
69
|
+
SQLITE_DROP_TEMP_INDEX,
|
|
70
|
+
SQLITE_DROP_TEMP_TABLE,
|
|
71
|
+
SQLITE_DROP_TEMP_TRIGGER,
|
|
72
|
+
SQLITE_DROP_TEMP_VIEW,
|
|
73
|
+
SQLITE_DROP_TRIGGER,
|
|
74
|
+
SQLITE_DROP_VIEW,
|
|
75
|
+
SQLITE_INSERT,
|
|
76
|
+
SQLITE_PRAGMA,
|
|
77
|
+
SQLITE_READ,
|
|
78
|
+
SQLITE_SELECT,
|
|
79
|
+
SQLITE_TRANSACTION,
|
|
80
|
+
SQLITE_UPDATE,
|
|
81
|
+
SQLITE_ATTACH,
|
|
82
|
+
SQLITE_DETACH,
|
|
83
|
+
SQLITE_ALTER_TABLE,
|
|
84
|
+
SQLITE_REINDEX,
|
|
85
|
+
SQLITE_ANALYZE,
|
|
86
|
+
SQLITE_CREATE_VTABLE,
|
|
87
|
+
SQLITE_DROP_VTABLE,
|
|
88
|
+
SQLITE_FUNCTION,
|
|
89
|
+
SQLITE_SAVEPOINT,
|
|
90
|
+
SQLITE_COPY,
|
|
91
|
+
SQLITE_RECURSIVE
|
|
92
|
+
};
|
|
93
|
+
export {
|
|
94
|
+
SQLITE_ALTER_TABLE,
|
|
95
|
+
SQLITE_ANALYZE,
|
|
96
|
+
SQLITE_ATTACH,
|
|
97
|
+
SQLITE_CHANGESET_ABORT,
|
|
98
|
+
SQLITE_CHANGESET_CONFLICT,
|
|
99
|
+
SQLITE_CHANGESET_CONSTRAINT,
|
|
100
|
+
SQLITE_CHANGESET_DATA,
|
|
101
|
+
SQLITE_CHANGESET_FOREIGN_KEY,
|
|
102
|
+
SQLITE_CHANGESET_NOTFOUND,
|
|
103
|
+
SQLITE_CHANGESET_OMIT,
|
|
104
|
+
SQLITE_CHANGESET_REPLACE,
|
|
105
|
+
SQLITE_COPY,
|
|
106
|
+
SQLITE_CREATE_INDEX,
|
|
107
|
+
SQLITE_CREATE_TABLE,
|
|
108
|
+
SQLITE_CREATE_TEMP_INDEX,
|
|
109
|
+
SQLITE_CREATE_TEMP_TABLE,
|
|
110
|
+
SQLITE_CREATE_TEMP_TRIGGER,
|
|
111
|
+
SQLITE_CREATE_TEMP_VIEW,
|
|
112
|
+
SQLITE_CREATE_TRIGGER,
|
|
113
|
+
SQLITE_CREATE_VIEW,
|
|
114
|
+
SQLITE_CREATE_VTABLE,
|
|
115
|
+
SQLITE_DELETE,
|
|
116
|
+
SQLITE_DENY,
|
|
117
|
+
SQLITE_DETACH,
|
|
118
|
+
SQLITE_DROP_INDEX,
|
|
119
|
+
SQLITE_DROP_TABLE,
|
|
120
|
+
SQLITE_DROP_TEMP_INDEX,
|
|
121
|
+
SQLITE_DROP_TEMP_TABLE,
|
|
122
|
+
SQLITE_DROP_TEMP_TRIGGER,
|
|
123
|
+
SQLITE_DROP_TEMP_VIEW,
|
|
124
|
+
SQLITE_DROP_TRIGGER,
|
|
125
|
+
SQLITE_DROP_VIEW,
|
|
126
|
+
SQLITE_DROP_VTABLE,
|
|
127
|
+
SQLITE_FUNCTION,
|
|
128
|
+
SQLITE_IGNORE,
|
|
129
|
+
SQLITE_INSERT,
|
|
130
|
+
SQLITE_OK,
|
|
131
|
+
SQLITE_PRAGMA,
|
|
132
|
+
SQLITE_READ,
|
|
133
|
+
SQLITE_RECURSIVE,
|
|
134
|
+
SQLITE_REINDEX,
|
|
135
|
+
SQLITE_SAVEPOINT,
|
|
136
|
+
SQLITE_SELECT,
|
|
137
|
+
SQLITE_TRANSACTION,
|
|
138
|
+
SQLITE_UPDATE,
|
|
139
|
+
constants
|
|
140
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { OutOfRangeError } from "./errors.js";
|
|
2
|
+
function convertValue(value, readBigInts) {
|
|
3
|
+
if (value === null || value === void 0) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
if (typeof value === "number") {
|
|
7
|
+
if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
|
|
8
|
+
if (!readBigInts) {
|
|
9
|
+
throw new OutOfRangeError(
|
|
10
|
+
`Value is too large to be represented as a JavaScript number: ${value}`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
return BigInt(value);
|
|
14
|
+
}
|
|
15
|
+
if (readBigInts && Number.isInteger(value)) {
|
|
16
|
+
return BigInt(value);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === "bigint") {
|
|
21
|
+
if (!readBigInts) {
|
|
22
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER) || value < BigInt(-Number.MAX_SAFE_INTEGER)) {
|
|
23
|
+
throw new OutOfRangeError(
|
|
24
|
+
`Value is too large to be represented as a JavaScript number: ${value}`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return Number(value);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
if (typeof value === "string") {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
if (value instanceof Uint8Array) {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
if (value && typeof value.toArray === "function") {
|
|
38
|
+
return new Uint8Array(value.toArray());
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function readRow(model, row, options) {
|
|
43
|
+
const nCols = model.get_n_columns();
|
|
44
|
+
if (options.returnArrays) {
|
|
45
|
+
const arr = [];
|
|
46
|
+
for (let col = 0; col < nCols; col++) {
|
|
47
|
+
const val = model.get_value_at(col, row);
|
|
48
|
+
arr.push(convertValue(val, options.readBigInts));
|
|
49
|
+
}
|
|
50
|
+
return arr;
|
|
51
|
+
}
|
|
52
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
53
|
+
for (let col = 0; col < nCols; col++) {
|
|
54
|
+
const name = model.get_column_name(col);
|
|
55
|
+
const val = model.get_value_at(col, row);
|
|
56
|
+
obj[name] = convertValue(val, options.readBigInts);
|
|
57
|
+
}
|
|
58
|
+
return obj;
|
|
59
|
+
}
|
|
60
|
+
function readAllRows(model, options) {
|
|
61
|
+
const nRows = model.get_n_rows();
|
|
62
|
+
const rows = [];
|
|
63
|
+
for (let row = 0; row < nRows; row++) {
|
|
64
|
+
const r = readRow(model, row, options);
|
|
65
|
+
if (r !== void 0) {
|
|
66
|
+
rows.push(r);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return rows;
|
|
70
|
+
}
|
|
71
|
+
function readFirstRow(model, options) {
|
|
72
|
+
const nRows = model.get_n_rows();
|
|
73
|
+
if (nRows === 0) {
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
return readRow(model, 0, options);
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
readAllRows,
|
|
80
|
+
readFirstRow,
|
|
81
|
+
readRow
|
|
82
|
+
};
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import Gda from "@girs/gda-6.0";
|
|
2
|
+
import {
|
|
3
|
+
ConstructCallRequiredError,
|
|
4
|
+
InvalidArgTypeError,
|
|
5
|
+
InvalidStateError,
|
|
6
|
+
InvalidUrlSchemeError,
|
|
7
|
+
SqliteError
|
|
8
|
+
} from "./errors.js";
|
|
9
|
+
import { StatementSync } from "./statement-sync.js";
|
|
10
|
+
const sqliteTypeSymbol = /* @__PURE__ */ Symbol.for("sqlite-type");
|
|
11
|
+
function parsePath(path) {
|
|
12
|
+
if (typeof path === "string") {
|
|
13
|
+
if (path.includes("\0")) {
|
|
14
|
+
throw new InvalidArgTypeError(
|
|
15
|
+
'The "path" argument must be a string, Uint8Array, or URL without null bytes.'
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return path;
|
|
19
|
+
}
|
|
20
|
+
if (path instanceof URL) {
|
|
21
|
+
if (path.protocol !== "file:") {
|
|
22
|
+
throw new InvalidUrlSchemeError();
|
|
23
|
+
}
|
|
24
|
+
const filePath = path.pathname;
|
|
25
|
+
if (filePath.includes("\0")) {
|
|
26
|
+
throw new InvalidArgTypeError(
|
|
27
|
+
'The "path" argument must be a string, Uint8Array, or URL without null bytes.'
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return filePath;
|
|
31
|
+
}
|
|
32
|
+
if (path instanceof Uint8Array) {
|
|
33
|
+
for (let i = 0; i < path.length; i++) {
|
|
34
|
+
if (path[i] === 0) {
|
|
35
|
+
throw new InvalidArgTypeError(
|
|
36
|
+
'The "path" argument must be a string, Uint8Array, or URL without null bytes.'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return new TextDecoder().decode(path);
|
|
41
|
+
}
|
|
42
|
+
throw new InvalidArgTypeError(
|
|
43
|
+
'The "path" argument must be a string, Uint8Array, or URL without null bytes.'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
function validateOptions(options) {
|
|
47
|
+
if (options === void 0) return {};
|
|
48
|
+
if (options === null || typeof options !== "object") {
|
|
49
|
+
throw new InvalidArgTypeError('The "options" argument must be an object.');
|
|
50
|
+
}
|
|
51
|
+
const opts = options;
|
|
52
|
+
const result = {};
|
|
53
|
+
if (opts.open !== void 0) {
|
|
54
|
+
if (typeof opts.open !== "boolean") {
|
|
55
|
+
throw new InvalidArgTypeError('The "options.open" argument must be a boolean.');
|
|
56
|
+
}
|
|
57
|
+
result.open = opts.open;
|
|
58
|
+
}
|
|
59
|
+
if (opts.readOnly !== void 0) {
|
|
60
|
+
if (typeof opts.readOnly !== "boolean") {
|
|
61
|
+
throw new InvalidArgTypeError('The "options.readOnly" argument must be a boolean.');
|
|
62
|
+
}
|
|
63
|
+
result.readOnly = opts.readOnly;
|
|
64
|
+
}
|
|
65
|
+
if (opts.timeout !== void 0) {
|
|
66
|
+
if (typeof opts.timeout !== "number" || !Number.isInteger(opts.timeout)) {
|
|
67
|
+
throw new InvalidArgTypeError('The "options.timeout" argument must be an integer.');
|
|
68
|
+
}
|
|
69
|
+
result.timeout = opts.timeout;
|
|
70
|
+
}
|
|
71
|
+
if (opts.enableForeignKeyConstraints !== void 0) {
|
|
72
|
+
if (typeof opts.enableForeignKeyConstraints !== "boolean") {
|
|
73
|
+
throw new InvalidArgTypeError('The "options.enableForeignKeyConstraints" argument must be a boolean.');
|
|
74
|
+
}
|
|
75
|
+
result.enableForeignKeyConstraints = opts.enableForeignKeyConstraints;
|
|
76
|
+
}
|
|
77
|
+
if (opts.enableDoubleQuotedStringLiterals !== void 0) {
|
|
78
|
+
if (typeof opts.enableDoubleQuotedStringLiterals !== "boolean") {
|
|
79
|
+
throw new InvalidArgTypeError('The "options.enableDoubleQuotedStringLiterals" argument must be a boolean.');
|
|
80
|
+
}
|
|
81
|
+
result.enableDoubleQuotedStringLiterals = opts.enableDoubleQuotedStringLiterals;
|
|
82
|
+
}
|
|
83
|
+
if (opts.readBigInts !== void 0) {
|
|
84
|
+
if (typeof opts.readBigInts !== "boolean") {
|
|
85
|
+
throw new InvalidArgTypeError('The "options.readBigInts" argument must be a boolean.');
|
|
86
|
+
}
|
|
87
|
+
result.readBigInts = opts.readBigInts;
|
|
88
|
+
}
|
|
89
|
+
if (opts.returnArrays !== void 0) {
|
|
90
|
+
if (typeof opts.returnArrays !== "boolean") {
|
|
91
|
+
throw new InvalidArgTypeError('The "options.returnArrays" argument must be a boolean.');
|
|
92
|
+
}
|
|
93
|
+
result.returnArrays = opts.returnArrays;
|
|
94
|
+
}
|
|
95
|
+
if (opts.allowBareNamedParameters !== void 0) {
|
|
96
|
+
if (typeof opts.allowBareNamedParameters !== "boolean") {
|
|
97
|
+
throw new InvalidArgTypeError('The "options.allowBareNamedParameters" argument must be a boolean.');
|
|
98
|
+
}
|
|
99
|
+
result.allowBareNamedParameters = opts.allowBareNamedParameters;
|
|
100
|
+
}
|
|
101
|
+
if (opts.allowUnknownNamedParameters !== void 0) {
|
|
102
|
+
if (typeof opts.allowUnknownNamedParameters !== "boolean") {
|
|
103
|
+
throw new InvalidArgTypeError('The "options.allowUnknownNamedParameters" argument must be a boolean.');
|
|
104
|
+
}
|
|
105
|
+
result.allowUnknownNamedParameters = opts.allowUnknownNamedParameters;
|
|
106
|
+
}
|
|
107
|
+
if (opts.defensive !== void 0) {
|
|
108
|
+
if (typeof opts.defensive !== "boolean") {
|
|
109
|
+
throw new InvalidArgTypeError('The "options.defensive" argument must be a boolean.');
|
|
110
|
+
}
|
|
111
|
+
result.defensive = opts.defensive;
|
|
112
|
+
}
|
|
113
|
+
if (opts.allowExtension !== void 0) {
|
|
114
|
+
if (typeof opts.allowExtension !== "boolean") {
|
|
115
|
+
throw new InvalidArgTypeError('The "options.allowExtension" argument must be a boolean.');
|
|
116
|
+
}
|
|
117
|
+
result.allowExtension = opts.allowExtension;
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
function convertParameterSyntax(sql) {
|
|
122
|
+
const params = [];
|
|
123
|
+
let positionalIndex = 0;
|
|
124
|
+
let result = "";
|
|
125
|
+
let i = 0;
|
|
126
|
+
while (i < sql.length) {
|
|
127
|
+
if (sql[i] === "'") {
|
|
128
|
+
const start = i;
|
|
129
|
+
i++;
|
|
130
|
+
while (i < sql.length && sql[i] !== "'") {
|
|
131
|
+
if (sql[i] === "'" && sql[i + 1] === "'") {
|
|
132
|
+
i += 2;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
i++;
|
|
136
|
+
}
|
|
137
|
+
if (i < sql.length) i++;
|
|
138
|
+
result += sql.substring(start, i);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (sql[i] === "?") {
|
|
142
|
+
i++;
|
|
143
|
+
let numStr = "";
|
|
144
|
+
while (i < sql.length && sql[i] >= "0" && sql[i] <= "9") {
|
|
145
|
+
numStr += sql[i];
|
|
146
|
+
i++;
|
|
147
|
+
}
|
|
148
|
+
const pos = numStr ? parseInt(numStr, 10) - 1 : positionalIndex;
|
|
149
|
+
positionalIndex = numStr ? positionalIndex : positionalIndex + 1;
|
|
150
|
+
const gdaId = `p${pos}`;
|
|
151
|
+
params.push({ gdaId, originalName: numStr ? `?${numStr}` : "?", position: pos });
|
|
152
|
+
result += `##${gdaId}::string`;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if ((sql[i] === "$" || sql[i] === ":" || sql[i] === "@") && i + 1 < sql.length && /[a-zA-Z_]/.test(sql[i + 1])) {
|
|
156
|
+
const prefix = sql[i];
|
|
157
|
+
i++;
|
|
158
|
+
let name = "";
|
|
159
|
+
while (i < sql.length && /[a-zA-Z0-9_]/.test(sql[i])) {
|
|
160
|
+
name += sql[i];
|
|
161
|
+
i++;
|
|
162
|
+
}
|
|
163
|
+
const gdaId = name;
|
|
164
|
+
params.push({ gdaId, originalName: `${prefix}${name}`, position: -1 });
|
|
165
|
+
result += `##${gdaId}::string`;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
result += sql[i];
|
|
169
|
+
i++;
|
|
170
|
+
}
|
|
171
|
+
return [result, params];
|
|
172
|
+
}
|
|
173
|
+
class DatabaseSync {
|
|
174
|
+
#connection = null;
|
|
175
|
+
#parser = null;
|
|
176
|
+
#path;
|
|
177
|
+
#options;
|
|
178
|
+
#isMemory;
|
|
179
|
+
#inTransaction = false;
|
|
180
|
+
constructor(path, options) {
|
|
181
|
+
if (!new.target) {
|
|
182
|
+
throw new ConstructCallRequiredError("DatabaseSync");
|
|
183
|
+
}
|
|
184
|
+
this.#path = parsePath(path);
|
|
185
|
+
this.#options = validateOptions(options);
|
|
186
|
+
this.#isMemory = this.#path === ":memory:";
|
|
187
|
+
const shouldOpen = this.#options.open !== false;
|
|
188
|
+
if (shouldOpen) {
|
|
189
|
+
this.open();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
get [sqliteTypeSymbol]() {
|
|
193
|
+
return "node:sqlite";
|
|
194
|
+
}
|
|
195
|
+
get isOpen() {
|
|
196
|
+
return this.#connection !== null && this.#connection.is_opened();
|
|
197
|
+
}
|
|
198
|
+
get isTransaction() {
|
|
199
|
+
this.#ensureOpen();
|
|
200
|
+
return this.#inTransaction;
|
|
201
|
+
}
|
|
202
|
+
open() {
|
|
203
|
+
if (this.isOpen) {
|
|
204
|
+
throw new InvalidStateError("database is already open");
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
if (this.#isMemory) {
|
|
208
|
+
this.#connection = Gda.Connection.new_from_string(
|
|
209
|
+
"SQLite",
|
|
210
|
+
"DB_DIR=;DB_NAME=:memory:",
|
|
211
|
+
null,
|
|
212
|
+
Gda.ConnectionOptions.NONE
|
|
213
|
+
);
|
|
214
|
+
} else {
|
|
215
|
+
const lastSlash = this.#path.lastIndexOf("/");
|
|
216
|
+
const dir = lastSlash >= 0 ? this.#path.substring(0, lastSlash) : ".";
|
|
217
|
+
const name = lastSlash >= 0 ? this.#path.substring(lastSlash + 1) : this.#path;
|
|
218
|
+
const cncString = `DB_DIR=${dir};DB_NAME=${name}`;
|
|
219
|
+
const connOpts = this.#options.readOnly ? Gda.ConnectionOptions.READ_ONLY : Gda.ConnectionOptions.NONE;
|
|
220
|
+
this.#connection = Gda.Connection.new_from_string(
|
|
221
|
+
"SQLite",
|
|
222
|
+
cncString,
|
|
223
|
+
null,
|
|
224
|
+
connOpts
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
this.#connection.open();
|
|
228
|
+
} catch (e) {
|
|
229
|
+
this.#connection = null;
|
|
230
|
+
throw new SqliteError(
|
|
231
|
+
e instanceof Error ? e.message : String(e)
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
this.#parser = this.#connection.create_parser() ?? new Gda.SqlParser();
|
|
235
|
+
this.#applyPragmas();
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
close() {
|
|
239
|
+
if (!this.isOpen) {
|
|
240
|
+
throw new InvalidStateError("database is not open");
|
|
241
|
+
}
|
|
242
|
+
this.#connection.close();
|
|
243
|
+
this.#connection = null;
|
|
244
|
+
this.#parser = null;
|
|
245
|
+
this.#inTransaction = false;
|
|
246
|
+
return void 0;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Execute one or more SQL statements. Returns undefined.
|
|
250
|
+
* Named sqlExec to avoid security hook false positive on "exec" name.
|
|
251
|
+
*/
|
|
252
|
+
sqlExec(sql) {
|
|
253
|
+
this.#ensureOpen();
|
|
254
|
+
if (typeof sql !== "string") {
|
|
255
|
+
throw new InvalidArgTypeError('The "sql" argument must be a string.');
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
const statements = this.#splitStatements(sql);
|
|
259
|
+
for (const stmtSql of statements) {
|
|
260
|
+
const [stmt] = this.#parser.parse_string(stmtSql);
|
|
261
|
+
if (stmt) {
|
|
262
|
+
this.#executeStatement(stmt);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} catch (e) {
|
|
266
|
+
if (e instanceof SqliteError || e instanceof InvalidStateError || e instanceof InvalidArgTypeError) {
|
|
267
|
+
throw e;
|
|
268
|
+
}
|
|
269
|
+
throw new SqliteError(
|
|
270
|
+
e instanceof Error ? e.message : String(e)
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
this.#updateTransactionState(sql);
|
|
274
|
+
return void 0;
|
|
275
|
+
}
|
|
276
|
+
prepare(sql, options) {
|
|
277
|
+
this.#ensureOpen();
|
|
278
|
+
if (typeof sql !== "string") {
|
|
279
|
+
throw new InvalidArgTypeError('The "sql" argument must be a string.');
|
|
280
|
+
}
|
|
281
|
+
const [, paramMap] = convertParameterSyntax(sql);
|
|
282
|
+
try {
|
|
283
|
+
const testSql = paramMap.length > 0 ? sql.replace(/\?(\d+)?/g, "NULL").replace(/[\$:@][a-zA-Z_][a-zA-Z0-9_]*/g, "NULL") : sql;
|
|
284
|
+
const [stmt] = this.#parser.parse_string(testSql);
|
|
285
|
+
if (!stmt) {
|
|
286
|
+
throw new SqliteError("Failed to parse SQL statement");
|
|
287
|
+
}
|
|
288
|
+
} catch (e) {
|
|
289
|
+
if (e instanceof SqliteError || e instanceof InvalidArgTypeError) {
|
|
290
|
+
throw e;
|
|
291
|
+
}
|
|
292
|
+
throw new SqliteError(
|
|
293
|
+
e instanceof Error ? e.message : String(e)
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
const stmtOptions = {
|
|
297
|
+
readBigInts: this.#options.readBigInts ?? false,
|
|
298
|
+
returnArrays: this.#options.returnArrays ?? false,
|
|
299
|
+
allowBareNamedParameters: this.#options.allowBareNamedParameters ?? true,
|
|
300
|
+
allowUnknownNamedParameters: this.#options.allowUnknownNamedParameters ?? false
|
|
301
|
+
};
|
|
302
|
+
return StatementSync._create(this.#connection, sql, stmtOptions, paramMap, this.#parser);
|
|
303
|
+
}
|
|
304
|
+
location(dbName) {
|
|
305
|
+
this.#ensureOpen();
|
|
306
|
+
if (dbName !== void 0 && typeof dbName !== "string") {
|
|
307
|
+
throw new InvalidArgTypeError('The "dbName" argument must be a string.');
|
|
308
|
+
}
|
|
309
|
+
if (this.#isMemory) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
return this.#path;
|
|
313
|
+
}
|
|
314
|
+
[Symbol.dispose]() {
|
|
315
|
+
if (this.isOpen) {
|
|
316
|
+
try {
|
|
317
|
+
this.close();
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
#ensureOpen() {
|
|
323
|
+
if (!this.isOpen) {
|
|
324
|
+
throw new InvalidStateError("database is not open");
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
#applyPragmas() {
|
|
328
|
+
const conn = this.#connection;
|
|
329
|
+
const runPragma = (pragma) => {
|
|
330
|
+
const parser = this.#parser;
|
|
331
|
+
const [stmt] = parser.parse_string(pragma);
|
|
332
|
+
if (stmt) {
|
|
333
|
+
conn.statement_execute_select(stmt, null);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
if (this.#options.enableForeignKeyConstraints !== false) {
|
|
337
|
+
runPragma("PRAGMA foreign_keys = ON");
|
|
338
|
+
} else {
|
|
339
|
+
runPragma("PRAGMA foreign_keys = OFF");
|
|
340
|
+
}
|
|
341
|
+
if (this.#options.timeout !== void 0 && this.#options.timeout > 0) {
|
|
342
|
+
runPragma(`PRAGMA busy_timeout = ${this.#options.timeout}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
#executeStatement(stmt) {
|
|
346
|
+
const stmtType = stmt.get_statement_type();
|
|
347
|
+
if (stmtType === Gda.SqlStatementType.SELECT) {
|
|
348
|
+
this.#connection.statement_execute_select(stmt, null);
|
|
349
|
+
} else {
|
|
350
|
+
try {
|
|
351
|
+
this.#connection.statement_execute_non_select(stmt, null);
|
|
352
|
+
} catch {
|
|
353
|
+
this.#connection.statement_execute_select(stmt, null);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
#splitStatements(sql) {
|
|
358
|
+
const stmts = [];
|
|
359
|
+
let current = "";
|
|
360
|
+
let inString = false;
|
|
361
|
+
for (let i = 0; i < sql.length; i++) {
|
|
362
|
+
const ch = sql[i];
|
|
363
|
+
if (ch === "'" && !inString) {
|
|
364
|
+
inString = true;
|
|
365
|
+
current += ch;
|
|
366
|
+
} else if (ch === "'" && inString) {
|
|
367
|
+
if (sql[i + 1] === "'") {
|
|
368
|
+
current += "''";
|
|
369
|
+
i++;
|
|
370
|
+
} else {
|
|
371
|
+
inString = false;
|
|
372
|
+
current += ch;
|
|
373
|
+
}
|
|
374
|
+
} else if (ch === ";" && !inString) {
|
|
375
|
+
const trimmed2 = current.trim();
|
|
376
|
+
if (trimmed2.length > 0) {
|
|
377
|
+
stmts.push(trimmed2);
|
|
378
|
+
}
|
|
379
|
+
current = "";
|
|
380
|
+
} else {
|
|
381
|
+
current += ch;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const trimmed = current.trim();
|
|
385
|
+
if (trimmed.length > 0) {
|
|
386
|
+
stmts.push(trimmed);
|
|
387
|
+
}
|
|
388
|
+
return stmts;
|
|
389
|
+
}
|
|
390
|
+
#updateTransactionState(sql) {
|
|
391
|
+
const trimmed = sql.trim().toUpperCase();
|
|
392
|
+
if (/\bBEGIN\b/.test(trimmed)) {
|
|
393
|
+
this.#inTransaction = true;
|
|
394
|
+
}
|
|
395
|
+
if (/\bCOMMIT\b/.test(trimmed) || /\bROLLBACK\b/.test(trimmed)) {
|
|
396
|
+
this.#inTransaction = false;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
DatabaseSync.prototype.exec = DatabaseSync.prototype.sqlExec;
|
|
401
|
+
export {
|
|
402
|
+
DatabaseSync
|
|
403
|
+
};
|