@naturalcycles/db-lib 9.15.1 → 9.16.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/dist/adapter/inmemory/queryInMemory.js +2 -1
- package/dist/base.common.db.d.ts +11 -11
- package/dist/base.common.db.js +11 -12
- package/dist/commondao/common.dao.d.ts +4 -0
- package/dist/commondao/common.dao.js +25 -0
- package/dist/testing/daoTest.js +0 -1
- package/dist/testing/dbTest.js +3 -4
- package/dist/testing/keyValueDBTest.js +0 -1
- package/dist/testing/keyValueDaoTest.js +0 -1
- package/package.json +2 -1
- package/src/adapter/inmemory/queryInMemory.ts +2 -1
- package/src/base.common.db.ts +16 -18
- package/src/commondao/common.dao.ts +30 -0
- package/src/testing/daoTest.ts +0 -1
- package/src/testing/dbTest.ts +4 -7
- package/src/testing/keyValueDBTest.ts +0 -2
- package/src/testing/keyValueDaoTest.ts +0 -2
- package/dist/testing/test.util.d.ts +0 -6
- package/dist/testing/test.util.js +0 -23
- package/src/testing/test.util.ts +0 -24
|
@@ -31,8 +31,9 @@ function queryInMemory(q, rows = []) {
|
|
|
31
31
|
if (order) {
|
|
32
32
|
const { name, descending } = order;
|
|
33
33
|
rows = rows.sort((a, b) => {
|
|
34
|
+
// biome-ignore lint/suspicious/noDoubleEquals: ok
|
|
34
35
|
if (a[name] == b[name])
|
|
35
|
-
return 0;
|
|
36
|
+
return 0;
|
|
36
37
|
if (descending) {
|
|
37
38
|
return a[name] < b[name] ? 1 : -1;
|
|
38
39
|
}
|
package/dist/base.common.db.d.ts
CHANGED
|
@@ -12,15 +12,15 @@ export declare class BaseCommonDB implements CommonDB {
|
|
|
12
12
|
support: CommonDBSupport;
|
|
13
13
|
ping(): Promise<void>;
|
|
14
14
|
getTables(): Promise<string[]>;
|
|
15
|
-
getTableSchema<ROW extends ObjectWithId>(
|
|
16
|
-
createTable<ROW extends ObjectWithId>(
|
|
17
|
-
getByIds<ROW extends ObjectWithId>(
|
|
18
|
-
deleteByQuery<ROW extends ObjectWithId>(
|
|
19
|
-
updateByQuery<ROW extends ObjectWithId>(
|
|
20
|
-
runQuery<ROW extends ObjectWithId>(
|
|
21
|
-
runQueryCount<ROW extends ObjectWithId>(
|
|
22
|
-
saveBatch<ROW extends ObjectWithId>(
|
|
23
|
-
streamQuery<ROW extends ObjectWithId>(
|
|
24
|
-
deleteByIds(
|
|
25
|
-
runInTransaction(fn: DBTransactionFn,
|
|
15
|
+
getTableSchema<ROW extends ObjectWithId>(_table: string): Promise<JsonSchemaRootObject<ROW>>;
|
|
16
|
+
createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>): Promise<void>;
|
|
17
|
+
getByIds<ROW extends ObjectWithId>(_table: string, _ids: string[]): Promise<ROW[]>;
|
|
18
|
+
deleteByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number>;
|
|
19
|
+
updateByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>, _patch: DBPatch<ROW>, _opt?: CommonDBOptions): Promise<number>;
|
|
20
|
+
runQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<RunQueryResult<ROW>>;
|
|
21
|
+
runQueryCount<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number>;
|
|
22
|
+
saveBatch<ROW extends ObjectWithId>(_table: string, _rows: ROW[], _opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
23
|
+
streamQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): ReadableTyped<ROW>;
|
|
24
|
+
deleteByIds(_table: string, _ids: string[], _opt?: CommonDBOptions): Promise<number>;
|
|
25
|
+
runInTransaction(fn: DBTransactionFn, _opt?: CommonDBTransactionOptions): Promise<void>;
|
|
26
26
|
}
|
package/dist/base.common.db.js
CHANGED
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BaseCommonDB = void 0;
|
|
4
4
|
const common_db_1 = require("./common.db");
|
|
5
5
|
const dbTransaction_util_1 = require("./transaction/dbTransaction.util");
|
|
6
|
-
/* eslint-disable unused-imports/no-unused-vars */
|
|
7
6
|
/**
|
|
8
7
|
* No-op implementation of CommonDB interface.
|
|
9
8
|
* To be extended by actual implementations.
|
|
@@ -19,37 +18,37 @@ class BaseCommonDB {
|
|
|
19
18
|
async getTables() {
|
|
20
19
|
throw new Error('getTables is not implemented');
|
|
21
20
|
}
|
|
22
|
-
async getTableSchema(
|
|
21
|
+
async getTableSchema(_table) {
|
|
23
22
|
throw new Error('getTableSchema is not implemented');
|
|
24
23
|
}
|
|
25
|
-
async createTable(
|
|
24
|
+
async createTable(_table, _schema) {
|
|
26
25
|
// no-op
|
|
27
26
|
}
|
|
28
|
-
async getByIds(
|
|
27
|
+
async getByIds(_table, _ids) {
|
|
29
28
|
throw new Error('getByIds is not implemented');
|
|
30
29
|
}
|
|
31
|
-
async deleteByQuery(
|
|
30
|
+
async deleteByQuery(_q) {
|
|
32
31
|
throw new Error('deleteByQuery is not implemented');
|
|
33
32
|
}
|
|
34
|
-
async updateByQuery(
|
|
33
|
+
async updateByQuery(_q, _patch, _opt) {
|
|
35
34
|
throw new Error('updateByQuery is not implemented');
|
|
36
35
|
}
|
|
37
|
-
async runQuery(
|
|
36
|
+
async runQuery(_q) {
|
|
38
37
|
throw new Error('runQuery is not implemented');
|
|
39
38
|
}
|
|
40
|
-
async runQueryCount(
|
|
39
|
+
async runQueryCount(_q) {
|
|
41
40
|
throw new Error('runQueryCount is not implemented');
|
|
42
41
|
}
|
|
43
|
-
async saveBatch(
|
|
42
|
+
async saveBatch(_table, _rows, _opt) {
|
|
44
43
|
throw new Error('saveBatch is not implemented');
|
|
45
44
|
}
|
|
46
|
-
streamQuery(
|
|
45
|
+
streamQuery(_q) {
|
|
47
46
|
throw new Error('streamQuery is not implemented');
|
|
48
47
|
}
|
|
49
|
-
async deleteByIds(
|
|
48
|
+
async deleteByIds(_table, _ids, _opt) {
|
|
50
49
|
throw new Error('deleteByIds is not implemented');
|
|
51
50
|
}
|
|
52
|
-
async runInTransaction(fn,
|
|
51
|
+
async runInTransaction(fn, _opt) {
|
|
53
52
|
const tx = new dbTransaction_util_1.FakeDBTransaction(this);
|
|
54
53
|
await fn(tx);
|
|
55
54
|
// there's no try/catch and rollback, as there's nothing to rollback
|
|
@@ -157,6 +157,10 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
157
157
|
*/
|
|
158
158
|
ping(): Promise<void>;
|
|
159
159
|
runInTransaction<T = void>(fn: CommonDaoTransactionFn<T>, opt?: CommonDBTransactionOptions): Promise<T>;
|
|
160
|
+
/**
|
|
161
|
+
* Throws if query uses a property that is in `excludeFromIndexes` list.
|
|
162
|
+
*/
|
|
163
|
+
private validateQueryIndexes;
|
|
160
164
|
protected logResult(started: number, op: string, res: any, table: string): void;
|
|
161
165
|
protected logSaveResult(started: number, op: string, table: string): void;
|
|
162
166
|
protected logStarted(op: string, table: string, force?: boolean): UnixTimestampMillisNumber;
|
|
@@ -202,6 +202,7 @@ class CommonDao {
|
|
|
202
202
|
return (0, js_lib_1._uniqBy)(results, r => r.id);
|
|
203
203
|
}
|
|
204
204
|
async runQueryExtended(q, opt = {}) {
|
|
205
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
205
206
|
q.table = opt.table || q.table;
|
|
206
207
|
const op = `runQuery(${q.pretty()})`;
|
|
207
208
|
const started = this.logStarted(op, q.table);
|
|
@@ -222,6 +223,7 @@ class CommonDao {
|
|
|
222
223
|
return rows;
|
|
223
224
|
}
|
|
224
225
|
async runQueryExtendedAsDBM(q, opt = {}) {
|
|
226
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
225
227
|
q.table = opt.table || q.table;
|
|
226
228
|
const op = `runQueryAsDBM(${q.pretty()})`;
|
|
227
229
|
const started = this.logStarted(op, q.table);
|
|
@@ -235,6 +237,7 @@ class CommonDao {
|
|
|
235
237
|
return { rows: dbms, ...queryResult };
|
|
236
238
|
}
|
|
237
239
|
async runQueryCount(q, opt = {}) {
|
|
240
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
238
241
|
q.table = opt.table || q.table;
|
|
239
242
|
const op = `runQueryCount(${q.pretty()})`;
|
|
240
243
|
const started = this.logStarted(op, q.table);
|
|
@@ -245,6 +248,7 @@ class CommonDao {
|
|
|
245
248
|
return count;
|
|
246
249
|
}
|
|
247
250
|
async streamQueryForEach(q, mapper, opt = {}) {
|
|
251
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
248
252
|
q.table = opt.table || q.table;
|
|
249
253
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
250
254
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
@@ -283,6 +287,7 @@ class CommonDao {
|
|
|
283
287
|
}
|
|
284
288
|
}
|
|
285
289
|
async streamQueryAsDBMForEach(q, mapper, opt = {}) {
|
|
290
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
286
291
|
q.table = opt.table || q.table;
|
|
287
292
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
288
293
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
@@ -324,6 +329,7 @@ class CommonDao {
|
|
|
324
329
|
* Stream as Readable, to be able to .pipe() it further with support of backpressure.
|
|
325
330
|
*/
|
|
326
331
|
streamQueryAsDBM(q, opt = {}) {
|
|
332
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
327
333
|
q.table = opt.table || q.table;
|
|
328
334
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
329
335
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
@@ -354,6 +360,7 @@ class CommonDao {
|
|
|
354
360
|
* You can do `.pipe(transformNoOp)` to make it "valid again".
|
|
355
361
|
*/
|
|
356
362
|
streamQuery(q, opt = {}) {
|
|
363
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
357
364
|
q.table = opt.table || q.table;
|
|
358
365
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
359
366
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
@@ -394,11 +401,13 @@ class CommonDao {
|
|
|
394
401
|
.pipe((0, nodejs_lib_1.transformNoOp)()));
|
|
395
402
|
}
|
|
396
403
|
async queryIds(q, opt = {}) {
|
|
404
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
397
405
|
q.table = opt.table || q.table;
|
|
398
406
|
const { rows } = await this.cfg.db.runQuery(q.select(['id']), opt);
|
|
399
407
|
return rows.map(r => r.id);
|
|
400
408
|
}
|
|
401
409
|
streamQueryIds(q, opt = {}) {
|
|
410
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
402
411
|
q.table = opt.table || q.table;
|
|
403
412
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
404
413
|
// Experimental: using `.map()`
|
|
@@ -417,6 +426,7 @@ class CommonDao {
|
|
|
417
426
|
return stream;
|
|
418
427
|
}
|
|
419
428
|
async streamQueryIdsForEach(q, mapper, opt = {}) {
|
|
429
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
420
430
|
q.table = opt.table || q.table;
|
|
421
431
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
422
432
|
const op = `streamQueryIdsForEach(${q.pretty()})`;
|
|
@@ -777,6 +787,7 @@ class CommonDao {
|
|
|
777
787
|
* This is expected to be more memory-efficient way of deleting large number of rows.
|
|
778
788
|
*/
|
|
779
789
|
async deleteByQuery(q, opt = {}) {
|
|
790
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
780
791
|
this.requireWriteAccess();
|
|
781
792
|
this.requireObjectMutability(opt);
|
|
782
793
|
q.table = opt.table || q.table;
|
|
@@ -820,6 +831,7 @@ class CommonDao {
|
|
|
820
831
|
return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt);
|
|
821
832
|
}
|
|
822
833
|
async updateByQuery(q, patch, opt = {}) {
|
|
834
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
823
835
|
this.requireWriteAccess();
|
|
824
836
|
this.requireObjectMutability(opt);
|
|
825
837
|
q.table = opt.table || q.table;
|
|
@@ -959,6 +971,19 @@ class CommonDao {
|
|
|
959
971
|
}, opt);
|
|
960
972
|
return r;
|
|
961
973
|
}
|
|
974
|
+
/**
|
|
975
|
+
* Throws if query uses a property that is in `excludeFromIndexes` list.
|
|
976
|
+
*/
|
|
977
|
+
validateQueryIndexes(q) {
|
|
978
|
+
const { excludeFromIndexes } = this.cfg;
|
|
979
|
+
if (!excludeFromIndexes)
|
|
980
|
+
return;
|
|
981
|
+
for (const f of q._filters) {
|
|
982
|
+
(0, js_lib_1._assert)(!excludeFromIndexes.includes(f.name), `cannot query on non-indexed property: ${this.cfg.table}.${f.name}`, {
|
|
983
|
+
query: q.pretty(),
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
}
|
|
962
987
|
logResult(started, op, res, table) {
|
|
963
988
|
if (!this.cfg.logLevel)
|
|
964
989
|
return;
|
package/dist/testing/daoTest.js
CHANGED
|
@@ -8,7 +8,6 @@ const __1 = require("..");
|
|
|
8
8
|
const common_dao_1 = require("../commondao/common.dao");
|
|
9
9
|
const dbTest_1 = require("./dbTest");
|
|
10
10
|
const test_model_1 = require("./test.model");
|
|
11
|
-
// eslint-disable-next-line jest/no-export
|
|
12
11
|
function runCommonDaoTest(db, quirks = {}) {
|
|
13
12
|
const { support } = db;
|
|
14
13
|
const dao = new common_dao_1.CommonDao({
|
package/dist/testing/dbTest.js
CHANGED
|
@@ -7,11 +7,10 @@ const common_db_1 = require("../common.db");
|
|
|
7
7
|
const db_model_1 = require("../db.model");
|
|
8
8
|
const dbQuery_1 = require("../query/dbQuery");
|
|
9
9
|
const test_model_1 = require("./test.model");
|
|
10
|
-
const test_util_1 = require("./test.util");
|
|
11
10
|
function runCommonDBTest(db, quirks = {}) {
|
|
12
11
|
const { support } = db;
|
|
13
12
|
const items = (0, test_model_1.createTestItemsDBM)(3);
|
|
14
|
-
(0,
|
|
13
|
+
(0, js_lib_1._deepFreeze)(items);
|
|
15
14
|
const item1 = items[0];
|
|
16
15
|
const queryAll = () => dbQuery_1.DBQuery.create(test_model_1.TEST_TABLE);
|
|
17
16
|
test('ping', async () => {
|
|
@@ -54,7 +53,7 @@ function runCommonDBTest(db, quirks = {}) {
|
|
|
54
53
|
...(0, test_model_1.createTestItemDBM)(3),
|
|
55
54
|
k2: null,
|
|
56
55
|
};
|
|
57
|
-
(0,
|
|
56
|
+
(0, js_lib_1._deepFreeze)(item3);
|
|
58
57
|
await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
|
|
59
58
|
const item3Loaded = (await db.getByIds(test_model_1.TEST_TABLE, [item3.id]))[0];
|
|
60
59
|
expectMatch([item3], [item3Loaded], quirks);
|
|
@@ -67,7 +66,7 @@ function runCommonDBTest(db, quirks = {}) {
|
|
|
67
66
|
...(0, test_model_1.createTestItemDBM)(3),
|
|
68
67
|
k2: undefined,
|
|
69
68
|
};
|
|
70
|
-
(0,
|
|
69
|
+
(0, js_lib_1._deepFreeze)(item3);
|
|
71
70
|
const expected = { ...item3 };
|
|
72
71
|
delete expected.k2;
|
|
73
72
|
await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runCommonKeyValueDBTest = runCommonKeyValueDBTest;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
const test_model_1 = require("./test.model");
|
|
6
|
-
/* eslint-disable jest/no-export */
|
|
7
6
|
const testIds = (0, js_lib_1._range)(1, 4).map(n => `id${n}`);
|
|
8
7
|
const testEntries = testIds.map(id => [id, Buffer.from(`${id}value`)]);
|
|
9
8
|
function runCommonKeyValueDBTest(db) {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runCommonKeyValueDaoTest = runCommonKeyValueDaoTest;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
|
-
/* eslint-disable jest/no-export */
|
|
6
5
|
const testIds = (0, js_lib_1._range)(1, 4).map(n => `id${n}`);
|
|
7
6
|
const testEntries = testIds.map(id => [id, Buffer.from(`${id}value`)]);
|
|
8
7
|
function runCommonKeyValueDaoTest(dao) {
|
package/package.json
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"@naturalcycles/nodejs-lib": "^13.1.1"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
|
+
"@biomejs/biome": "^1.8.3",
|
|
16
17
|
"@naturalcycles/bench-lib": "^3.0.0",
|
|
17
18
|
"@naturalcycles/dev-lib": "^15.4.1",
|
|
18
19
|
"@types/node": "^22.1.0",
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
"engines": {
|
|
46
47
|
"node": ">=20.13"
|
|
47
48
|
},
|
|
48
|
-
"version": "9.
|
|
49
|
+
"version": "9.16.0",
|
|
49
50
|
"description": "Lowest Common Denominator API to supported Databases",
|
|
50
51
|
"keywords": [
|
|
51
52
|
"db",
|
|
@@ -35,7 +35,8 @@ export function queryInMemory<ROW extends ObjectWithId>(q: DBQuery<ROW>, rows: R
|
|
|
35
35
|
if (order) {
|
|
36
36
|
const { name, descending } = order
|
|
37
37
|
rows = rows.sort((a, b) => {
|
|
38
|
-
|
|
38
|
+
// biome-ignore lint/suspicious/noDoubleEquals: ok
|
|
39
|
+
if (a[name] == b[name]) return 0
|
|
39
40
|
|
|
40
41
|
if (descending) {
|
|
41
42
|
return a[name] < b[name] ? 1 : -1
|
package/src/base.common.db.ts
CHANGED
|
@@ -12,8 +12,6 @@ import {
|
|
|
12
12
|
import { DBQuery } from './query/dbQuery'
|
|
13
13
|
import { FakeDBTransaction } from './transaction/dbTransaction.util'
|
|
14
14
|
|
|
15
|
-
/* eslint-disable unused-imports/no-unused-vars */
|
|
16
|
-
|
|
17
15
|
/**
|
|
18
16
|
* No-op implementation of CommonDB interface.
|
|
19
17
|
* To be extended by actual implementations.
|
|
@@ -32,59 +30,59 @@ export class BaseCommonDB implements CommonDB {
|
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
async getTableSchema<ROW extends ObjectWithId>(
|
|
35
|
-
|
|
33
|
+
_table: string,
|
|
36
34
|
): Promise<JsonSchemaRootObject<ROW>> {
|
|
37
35
|
throw new Error('getTableSchema is not implemented')
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
async createTable<ROW extends ObjectWithId>(
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
_table: string,
|
|
40
|
+
_schema: JsonSchemaObject<ROW>,
|
|
43
41
|
): Promise<void> {
|
|
44
42
|
// no-op
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
async getByIds<ROW extends ObjectWithId>(
|
|
45
|
+
async getByIds<ROW extends ObjectWithId>(_table: string, _ids: string[]): Promise<ROW[]> {
|
|
48
46
|
throw new Error('getByIds is not implemented')
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
async deleteByQuery<ROW extends ObjectWithId>(
|
|
49
|
+
async deleteByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number> {
|
|
52
50
|
throw new Error('deleteByQuery is not implemented')
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
async updateByQuery<ROW extends ObjectWithId>(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
_q: DBQuery<ROW>,
|
|
55
|
+
_patch: DBPatch<ROW>,
|
|
56
|
+
_opt?: CommonDBOptions,
|
|
59
57
|
): Promise<number> {
|
|
60
58
|
throw new Error('updateByQuery is not implemented')
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
async runQuery<ROW extends ObjectWithId>(
|
|
61
|
+
async runQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<RunQueryResult<ROW>> {
|
|
64
62
|
throw new Error('runQuery is not implemented')
|
|
65
63
|
}
|
|
66
64
|
|
|
67
|
-
async runQueryCount<ROW extends ObjectWithId>(
|
|
65
|
+
async runQueryCount<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number> {
|
|
68
66
|
throw new Error('runQueryCount is not implemented')
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
async saveBatch<ROW extends ObjectWithId>(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
_table: string,
|
|
71
|
+
_rows: ROW[],
|
|
72
|
+
_opt?: CommonDBSaveOptions<ROW>,
|
|
75
73
|
): Promise<void> {
|
|
76
74
|
throw new Error('saveBatch is not implemented')
|
|
77
75
|
}
|
|
78
76
|
|
|
79
|
-
streamQuery<ROW extends ObjectWithId>(
|
|
77
|
+
streamQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): ReadableTyped<ROW> {
|
|
80
78
|
throw new Error('streamQuery is not implemented')
|
|
81
79
|
}
|
|
82
80
|
|
|
83
|
-
async deleteByIds(
|
|
81
|
+
async deleteByIds(_table: string, _ids: string[], _opt?: CommonDBOptions): Promise<number> {
|
|
84
82
|
throw new Error('deleteByIds is not implemented')
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
async runInTransaction(fn: DBTransactionFn,
|
|
85
|
+
async runInTransaction(fn: DBTransactionFn, _opt?: CommonDBTransactionOptions): Promise<void> {
|
|
88
86
|
const tx = new FakeDBTransaction(this)
|
|
89
87
|
await fn(tx)
|
|
90
88
|
// there's no try/catch and rollback, as there's nothing to rollback
|
|
@@ -295,6 +295,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
async runQueryExtended(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<RunQueryResult<BM>> {
|
|
298
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
298
299
|
q.table = opt.table || q.table
|
|
299
300
|
const op = `runQuery(${q.pretty()})`
|
|
300
301
|
const started = this.logStarted(op, q.table)
|
|
@@ -323,6 +324,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
323
324
|
q: DBQuery<DBM>,
|
|
324
325
|
opt: CommonDaoOptions = {},
|
|
325
326
|
): Promise<RunQueryResult<DBM>> {
|
|
327
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
326
328
|
q.table = opt.table || q.table
|
|
327
329
|
const op = `runQueryAsDBM(${q.pretty()})`
|
|
328
330
|
const started = this.logStarted(op, q.table)
|
|
@@ -340,6 +342,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
340
342
|
}
|
|
341
343
|
|
|
342
344
|
async runQueryCount(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
|
|
345
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
343
346
|
q.table = opt.table || q.table
|
|
344
347
|
const op = `runQueryCount(${q.pretty()})`
|
|
345
348
|
const started = this.logStarted(op, q.table)
|
|
@@ -355,6 +358,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
355
358
|
mapper: AsyncMapper<BM, void>,
|
|
356
359
|
opt: CommonDaoStreamForEachOptions<BM> = {},
|
|
357
360
|
): Promise<void> {
|
|
361
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
358
362
|
q.table = opt.table || q.table
|
|
359
363
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
360
364
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
@@ -404,6 +408,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
404
408
|
mapper: AsyncMapper<DBM, void>,
|
|
405
409
|
opt: CommonDaoStreamForEachOptions<DBM> = {},
|
|
406
410
|
): Promise<void> {
|
|
411
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
407
412
|
q.table = opt.table || q.table
|
|
408
413
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
409
414
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
@@ -452,6 +457,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
452
457
|
* Stream as Readable, to be able to .pipe() it further with support of backpressure.
|
|
453
458
|
*/
|
|
454
459
|
streamQueryAsDBM(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<DBM> = {}): ReadableTyped<DBM> {
|
|
460
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
455
461
|
q.table = opt.table || q.table
|
|
456
462
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
457
463
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
@@ -490,6 +496,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
490
496
|
* You can do `.pipe(transformNoOp)` to make it "valid again".
|
|
491
497
|
*/
|
|
492
498
|
streamQuery(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<BM> = {}): ReadableTyped<BM> {
|
|
499
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
493
500
|
q.table = opt.table || q.table
|
|
494
501
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
495
502
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
@@ -540,12 +547,14 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
540
547
|
}
|
|
541
548
|
|
|
542
549
|
async queryIds(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<string[]> {
|
|
550
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
543
551
|
q.table = opt.table || q.table
|
|
544
552
|
const { rows } = await this.cfg.db.runQuery(q.select(['id']), opt)
|
|
545
553
|
return rows.map(r => r.id)
|
|
546
554
|
}
|
|
547
555
|
|
|
548
556
|
streamQueryIds(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<string> = {}): ReadableTyped<string> {
|
|
557
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
549
558
|
q.table = opt.table || q.table
|
|
550
559
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
551
560
|
|
|
@@ -572,6 +581,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
572
581
|
mapper: AsyncMapper<string, void>,
|
|
573
582
|
opt: CommonDaoStreamForEachOptions<string> = {},
|
|
574
583
|
): Promise<void> {
|
|
584
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
575
585
|
q.table = opt.table || q.table
|
|
576
586
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
577
587
|
|
|
@@ -1015,6 +1025,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1015
1025
|
q: DBQuery<DBM>,
|
|
1016
1026
|
opt: CommonDaoStreamDeleteOptions<DBM> = {},
|
|
1017
1027
|
): Promise<number> {
|
|
1028
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
1018
1029
|
this.requireWriteAccess()
|
|
1019
1030
|
this.requireObjectMutability(opt)
|
|
1020
1031
|
q.table = opt.table || q.table
|
|
@@ -1073,6 +1084,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1073
1084
|
patch: DBPatch<DBM>,
|
|
1074
1085
|
opt: CommonDaoOptions = {},
|
|
1075
1086
|
): Promise<number> {
|
|
1087
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
1076
1088
|
this.requireWriteAccess()
|
|
1077
1089
|
this.requireObjectMutability(opt)
|
|
1078
1090
|
q.table = opt.table || q.table
|
|
@@ -1259,6 +1271,24 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1259
1271
|
return r!
|
|
1260
1272
|
}
|
|
1261
1273
|
|
|
1274
|
+
/**
|
|
1275
|
+
* Throws if query uses a property that is in `excludeFromIndexes` list.
|
|
1276
|
+
*/
|
|
1277
|
+
private validateQueryIndexes(q: DBQuery<DBM>): void {
|
|
1278
|
+
const { excludeFromIndexes } = this.cfg
|
|
1279
|
+
if (!excludeFromIndexes) return
|
|
1280
|
+
|
|
1281
|
+
for (const f of q._filters) {
|
|
1282
|
+
_assert(
|
|
1283
|
+
!excludeFromIndexes.includes(f.name),
|
|
1284
|
+
`cannot query on non-indexed property: ${this.cfg.table}.${f.name as string}`,
|
|
1285
|
+
{
|
|
1286
|
+
query: q.pretty(),
|
|
1287
|
+
},
|
|
1288
|
+
)
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1262
1292
|
protected logResult(started: number, op: string, res: any, table: string): void {
|
|
1263
1293
|
if (!this.cfg.logLevel) return
|
|
1264
1294
|
|
package/src/testing/daoTest.ts
CHANGED
package/src/testing/dbTest.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _filterObject, _pick, _sortBy, pMap } from '@naturalcycles/js-lib'
|
|
1
|
+
import { _deepFreeze, _filterObject, _pick, _sortBy, pMap } from '@naturalcycles/js-lib'
|
|
2
2
|
import { CommonDB, CommonDBType } from '../common.db'
|
|
3
3
|
import { DBIncrement, DBPatch } from '../db.model'
|
|
4
4
|
import { DBQuery } from '../query/dbQuery'
|
|
@@ -9,9 +9,6 @@ import {
|
|
|
9
9
|
testItemBMJsonSchema,
|
|
10
10
|
TestItemDBM,
|
|
11
11
|
} from './test.model'
|
|
12
|
-
import { deepFreeze } from './test.util'
|
|
13
|
-
|
|
14
|
-
/* eslint-disable jest/no-export */
|
|
15
12
|
|
|
16
13
|
/**
|
|
17
14
|
* All options default to `false`.
|
|
@@ -31,7 +28,7 @@ export interface CommonDBImplementationQuirks {
|
|
|
31
28
|
export function runCommonDBTest(db: CommonDB, quirks: CommonDBImplementationQuirks = {}): void {
|
|
32
29
|
const { support } = db
|
|
33
30
|
const items = createTestItemsDBM(3)
|
|
34
|
-
|
|
31
|
+
_deepFreeze(items)
|
|
35
32
|
const item1 = items[0]!
|
|
36
33
|
|
|
37
34
|
const queryAll = (): DBQuery<TestItemDBM> => DBQuery.create<TestItemDBM>(TEST_TABLE)
|
|
@@ -88,7 +85,7 @@ export function runCommonDBTest(db: CommonDB, quirks: CommonDBImplementationQuir
|
|
|
88
85
|
...createTestItemDBM(3),
|
|
89
86
|
k2: null,
|
|
90
87
|
}
|
|
91
|
-
|
|
88
|
+
_deepFreeze(item3)
|
|
92
89
|
await db.saveBatch(TEST_TABLE, [item3])
|
|
93
90
|
const item3Loaded = (await db.getByIds<TestItemDBM>(TEST_TABLE, [item3.id]))[0]!
|
|
94
91
|
expectMatch([item3], [item3Loaded], quirks)
|
|
@@ -102,7 +99,7 @@ export function runCommonDBTest(db: CommonDB, quirks: CommonDBImplementationQuir
|
|
|
102
99
|
...createTestItemDBM(3),
|
|
103
100
|
k2: undefined,
|
|
104
101
|
}
|
|
105
|
-
|
|
102
|
+
_deepFreeze(item3)
|
|
106
103
|
const expected = { ...item3 }
|
|
107
104
|
delete expected.k2
|
|
108
105
|
|
|
@@ -2,8 +2,6 @@ import { _range, _sortBy } from '@naturalcycles/js-lib'
|
|
|
2
2
|
import { CommonKeyValueDB, KeyValueDBTuple } from '../kv/commonKeyValueDB'
|
|
3
3
|
import { TEST_TABLE } from './test.model'
|
|
4
4
|
|
|
5
|
-
/* eslint-disable jest/no-export */
|
|
6
|
-
|
|
7
5
|
const testIds = _range(1, 4).map(n => `id${n}`)
|
|
8
6
|
|
|
9
7
|
const testEntries: KeyValueDBTuple[] = testIds.map(id => [id, Buffer.from(`${id}value`)])
|
|
@@ -2,8 +2,6 @@ import { _range, _sortBy } from '@naturalcycles/js-lib'
|
|
|
2
2
|
import { CommonKeyValueDao } from '../kv/commonKeyValueDao'
|
|
3
3
|
import { KeyValueDBTuple } from '../kv/commonKeyValueDB'
|
|
4
4
|
|
|
5
|
-
/* eslint-disable jest/no-export */
|
|
6
|
-
|
|
7
5
|
const testIds = _range(1, 4).map(n => `id${n}`)
|
|
8
6
|
const testEntries: KeyValueDBTuple[] = testIds.map(id => [id, Buffer.from(`${id}value`)])
|
|
9
7
|
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// copy-pasted from test-lib to be able to not include devDependecies there
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.deepFreeze = deepFreeze;
|
|
5
|
-
/**
|
|
6
|
-
* Does Object.freeze recursively for given object.
|
|
7
|
-
*
|
|
8
|
-
* Based on: https://github.com/substack/deep-freeze/blob/master/index.js
|
|
9
|
-
*/
|
|
10
|
-
function deepFreeze(o) {
|
|
11
|
-
Object.freeze(o);
|
|
12
|
-
Object.getOwnPropertyNames(o).forEach(prop => {
|
|
13
|
-
if (
|
|
14
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
15
|
-
o.hasOwnProperty(prop) &&
|
|
16
|
-
o[prop] !== null &&
|
|
17
|
-
(typeof o[prop] === 'object' || typeof o[prop] === 'function') &&
|
|
18
|
-
!Object.isFrozen(o[prop])) {
|
|
19
|
-
deepFreeze(o[prop]);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
return o;
|
|
23
|
-
}
|
package/src/testing/test.util.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// copy-pasted from test-lib to be able to not include devDependecies there
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Does Object.freeze recursively for given object.
|
|
5
|
-
*
|
|
6
|
-
* Based on: https://github.com/substack/deep-freeze/blob/master/index.js
|
|
7
|
-
*/
|
|
8
|
-
export function deepFreeze(o: any): void {
|
|
9
|
-
Object.freeze(o)
|
|
10
|
-
|
|
11
|
-
Object.getOwnPropertyNames(o).forEach(prop => {
|
|
12
|
-
if (
|
|
13
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
14
|
-
o.hasOwnProperty(prop) &&
|
|
15
|
-
o[prop] !== null &&
|
|
16
|
-
(typeof o[prop] === 'object' || typeof o[prop] === 'function') &&
|
|
17
|
-
!Object.isFrozen(o[prop])
|
|
18
|
-
) {
|
|
19
|
-
deepFreeze(o[prop])
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
return o
|
|
24
|
-
}
|