ts-prorm-orm 1.2.2 → 2.0.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/CHANGELOG.md +209 -0
- package/README.md +32 -24
- package/dist/compliance/index.d.ts +2 -0
- package/dist/compliance/index.js +13 -1
- package/dist/connection-manager.js +13 -1
- package/dist/core/expressions.d.ts +58 -0
- package/dist/core/expressions.js +83 -0
- package/dist/core/hooks-manager.d.ts +179 -0
- package/dist/core/hooks-manager.js +291 -0
- package/dist/core/types.d.ts +235 -0
- package/dist/core/types.js +9 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.js +11 -1
- package/dist/diagrams/chen-diagram.d.ts +15 -12
- package/dist/diagrams/chen-diagram.js +76 -85
- package/dist/diagrams/class-diagram.d.ts +9 -8
- package/dist/diagrams/class-diagram.js +60 -82
- package/dist/diagrams/core/index.d.ts +13 -0
- package/dist/diagrams/core/index.js +33 -0
- package/dist/diagrams/core/layout.d.ts +104 -0
- package/dist/diagrams/core/layout.js +223 -0
- package/dist/diagrams/{svg-dom.d.ts → core/svg.d.ts} +8 -20
- package/dist/diagrams/{svg-dom.js → core/svg.js} +21 -29
- package/dist/diagrams/core/theme.d.ts +84 -0
- package/dist/diagrams/core/theme.js +62 -0
- package/dist/diagrams/dependency-diagram.d.ts +15 -14
- package/dist/diagrams/dependency-diagram.js +71 -105
- package/dist/diagrams/er-diagram.d.ts +13 -26
- package/dist/diagrams/er-diagram.js +180 -259
- package/dist/diagrams/flow-diagram.d.ts +16 -11
- package/dist/diagrams/flow-diagram.js +96 -91
- package/dist/diagrams/gantt-diagram.d.ts +10 -5
- package/dist/diagrams/gantt-diagram.js +77 -103
- package/dist/diagrams/index-diagram.d.ts +10 -10
- package/dist/diagrams/index-diagram.js +106 -116
- package/dist/diagrams/index.d.ts +6 -9
- package/dist/diagrams/index.js +9 -24
- package/dist/diagrams/migration-diagram.d.ts +8 -4
- package/dist/diagrams/migration-diagram.js +189 -161
- package/dist/diagrams/model-diagram.d.ts +34 -34
- package/dist/diagrams/model-diagram.js +106 -485
- package/dist/diagrams/package-diagram.d.ts +9 -6
- package/dist/diagrams/package-diagram.js +64 -101
- package/dist/diagrams/relational-diagram.d.ts +12 -10
- package/dist/diagrams/relational-diagram.js +87 -114
- package/dist/diagrams/schemadoc-diagram.d.ts +8 -8
- package/dist/diagrams/schemadoc-diagram.js +71 -93
- package/dist/diagrams/sequence-diagram.d.ts +10 -10
- package/dist/diagrams/sequence-diagram.js +64 -89
- package/dist/diagrams/state-diagram.d.ts +10 -9
- package/dist/diagrams/state-diagram.js +69 -92
- package/dist/diagrams/tree-diagram.d.ts +11 -5
- package/dist/diagrams/tree-diagram.js +41 -65
- package/dist/dialects/clickhouse/index.js +15 -0
- package/dist/dialects/cockroachdb/index.d.ts +1 -1
- package/dist/dialects/cockroachdb/index.js +32 -63
- package/dist/dialects/db2/index.js +23 -8
- package/dist/dialects/dialect.d.ts +29 -5
- package/dist/dialects/duckdb/index.js +9 -2
- package/dist/dialects/hana/index.js +22 -5
- package/dist/dialects/lock-clause-helper.d.ts +167 -0
- package/dist/dialects/lock-clause-helper.js +316 -0
- package/dist/dialects/mariadb/index.d.ts +7 -4
- package/dist/dialects/mariadb/index.js +36 -60
- package/dist/dialects/mssql/index.js +34 -3
- package/dist/dialects/mysql/index.d.ts +1 -1
- package/dist/dialects/mysql/index.js +17 -45
- package/dist/dialects/oracle/index.d.ts +14 -3
- package/dist/dialects/oracle/index.js +66 -36
- package/dist/dialects/order-expression-helper.d.ts +72 -0
- package/dist/dialects/order-expression-helper.js +154 -0
- package/dist/dialects/partial-index.d.ts +52 -0
- package/dist/dialects/partial-index.js +251 -0
- package/dist/dialects/postgres/index.d.ts +1 -1
- package/dist/dialects/postgres/index.js +43 -68
- package/dist/dialects/redshift/index.js +9 -2
- package/dist/dialects/snowflake/index.js +17 -0
- package/dist/dialects/spanner/index.js +7 -2
- package/dist/dialects/sqlite/driver.d.ts +55 -0
- package/dist/dialects/sqlite/driver.js +102 -0
- package/dist/dialects/sqlite/index.d.ts +1 -1
- package/dist/dialects/sqlite/index.js +49 -27
- package/dist/graph/base-graph-dialect.d.ts +24 -3
- package/dist/graph/base-graph-dialect.js +43 -2
- package/dist/graph/dgraph/index.js +6 -0
- package/dist/graph/gremlin/index.d.ts +6 -0
- package/dist/graph/gremlin/index.js +12 -1
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/neo4j/index.d.ts +2 -0
- package/dist/graph/neo4j/index.js +6 -1
- package/dist/graph/types.d.ts +12 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.js +54 -7
- package/dist/logging/index.d.ts +10 -0
- package/dist/logging/index.js +23 -0
- package/dist/logging/query-logging.d.ts +82 -0
- package/dist/logging/query-logging.js +102 -0
- package/dist/models/decorators.d.ts +39 -3
- package/dist/models/decorators.js +74 -0
- package/dist/models/eager-load.d.ts +99 -0
- package/dist/models/eager-load.js +348 -5
- package/dist/models/indexes.d.ts +3 -2
- package/dist/models/indexes.js +7 -1
- package/dist/models/model.js +69 -43
- package/dist/prorm.d.ts +175 -409
- package/dist/prorm.js +1142 -557
- package/dist/query-builders/order-limit-builder.js +12 -10
- package/dist/query-builders/sql-compiler.d.ts +10 -3
- package/dist/query-builders/sql-compiler.js +14 -43
- package/dist/query-interface.d.ts +2 -1
- package/dist/sql-constants.d.ts +44 -0
- package/dist/sql-constants.js +138 -1
- package/dist/types/index.d.ts +53 -17
- package/dist/types/index.js +10 -15
- package/dist/types/query-types.d.ts +26 -0
- package/dist/types/query-types.js +30 -0
- package/package.json +32 -5
- package/dist/decorators/belongs-to-many.js +0 -115
- package/dist/decorators/belongs-to.js +0 -115
- package/dist/decorators/has-many.js +0 -127
- package/dist/decorators/has-one.js +0 -116
- package/dist/diagrams/palette.d.ts +0 -138
- package/dist/diagrams/palette.js +0 -194
- package/dist/hooks/hooks-manager.d.ts +0 -189
- package/dist/hooks/hooks-manager.js +0 -350
- package/dist/hooks/index.d.ts +0 -8
- package/dist/hooks/index.js +0 -37
- /package/dist/{logging.d.ts → logging/logger.d.ts} +0 -0
- /package/dist/{logging.js → logging/logger.js} +0 -0
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
41
|
exports.ClickHouseDialect = void 0;
|
|
42
42
|
exports.createClickHouseDialect = createClickHouseDialect;
|
|
43
|
+
const partial_index_1 = require("../partial-index");
|
|
43
44
|
// Lazily-loaded `@clickhouse/client` module. The native/runtime driver is only
|
|
44
45
|
// required the first time a connection is opened, so merely importing this ORM
|
|
45
46
|
// never pulls in the ClickHouse driver.
|
|
@@ -59,6 +60,7 @@ function loadClickHouse() {
|
|
|
59
60
|
}
|
|
60
61
|
const query_stream_helper_1 = require("../query-stream-helper");
|
|
61
62
|
const prorm_1 = require("../../prorm");
|
|
63
|
+
const order_expression_helper_1 = require("../order-expression-helper");
|
|
62
64
|
/**
|
|
63
65
|
* ClickHouse dialect class that implements the Dialect interface.
|
|
64
66
|
*
|
|
@@ -950,6 +952,12 @@ class ClickHouseDialect {
|
|
|
950
952
|
if (options?.ifNotExists)
|
|
951
953
|
sql += ' IF NOT EXISTS';
|
|
952
954
|
sql += ` ${this.escapeId(indexName)} ${expr} TYPE ${type} GRANULARITY ${granularity}`;
|
|
955
|
+
// ClickHouse indexes are data-skipping indexes on the whole part set; there
|
|
956
|
+
// is no predicate to restrict them. `where` was accepted and dropped on the
|
|
957
|
+
// floor here, so the caller silently got an unfiltered index.
|
|
958
|
+
if (options?.where) {
|
|
959
|
+
throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
|
|
960
|
+
}
|
|
953
961
|
await this.query(sql);
|
|
954
962
|
}
|
|
955
963
|
async removeIndex(tableName, indexName) {
|
|
@@ -959,6 +967,7 @@ class ClickHouseDialect {
|
|
|
959
967
|
await this.addIndex(tableName, indexDef.name, indexDef.fields, {
|
|
960
968
|
type: indexDef.type,
|
|
961
969
|
using: indexDef.using,
|
|
970
|
+
where: indexDef.where,
|
|
962
971
|
});
|
|
963
972
|
}
|
|
964
973
|
async dropIndex(tableName, indexName, options) {
|
|
@@ -1359,6 +1368,12 @@ class ClickHouseDialect {
|
|
|
1359
1368
|
return '';
|
|
1360
1369
|
const parts = [];
|
|
1361
1370
|
const processItem = (item) => {
|
|
1371
|
+
// Order helpers: asc('name') / desc('name') / random()
|
|
1372
|
+
const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
|
|
1373
|
+
if (orderExpr !== null) {
|
|
1374
|
+
parts.push(orderExpr);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1362
1377
|
if (typeof item === 'string') {
|
|
1363
1378
|
const [field, dir] = item.split(' ');
|
|
1364
1379
|
parts.push(dir ? `${this.escapeId(field)} ${dir.toUpperCase()}` : this.escapeId(field));
|
|
@@ -1013,7 +1013,7 @@ export declare class CockroachDBDialect implements Dialect {
|
|
|
1013
1013
|
type?: string;
|
|
1014
1014
|
using?: string;
|
|
1015
1015
|
fields: string[];
|
|
1016
|
-
where?: WhereOptions;
|
|
1016
|
+
where?: WhereOptions | string;
|
|
1017
1017
|
expression?: string;
|
|
1018
1018
|
include?: string[];
|
|
1019
1019
|
concurrently?: boolean;
|
|
@@ -36,7 +36,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.CockroachDBTransaction = exports.CockroachDBDialect = void 0;
|
|
37
37
|
exports.createCockroachDBDialect = createCockroachDBDialect;
|
|
38
38
|
const operators_1 = require("../../operators");
|
|
39
|
+
const lock_clause_helper_1 = require("../lock-clause-helper");
|
|
40
|
+
const partial_index_1 = require("../partial-index");
|
|
39
41
|
const prorm_1 = require("../../prorm");
|
|
42
|
+
const order_expression_helper_1 = require("../order-expression-helper");
|
|
40
43
|
// Lazily-loaded `pg` driver. Required only when a connection is opened, so
|
|
41
44
|
// importing this ORM never loads the pg driver.
|
|
42
45
|
let pgModule = null;
|
|
@@ -2327,9 +2330,14 @@ class CockroachDBDialect {
|
|
|
2327
2330
|
if (options?.include && options.include.length > 0) {
|
|
2328
2331
|
sql += ` INCLUDE (${options.include.map((f) => this.escapeId(f)).join(', ')})`;
|
|
2329
2332
|
}
|
|
2333
|
+
// Partial index predicate. The ordinary WHERE builder emits `$1`-style
|
|
2334
|
+
// placeholders and this DDL is executed without a value list, so the
|
|
2335
|
+
// predicate is compiled with its values inlined as escaped literals.
|
|
2330
2336
|
if (options?.where) {
|
|
2331
|
-
const
|
|
2332
|
-
|
|
2337
|
+
const predicate = (0, partial_index_1.buildPartialIndexPredicate)(options.where, this.name, this);
|
|
2338
|
+
if (predicate) {
|
|
2339
|
+
sql += ` WHERE ${predicate}`;
|
|
2340
|
+
}
|
|
2333
2341
|
}
|
|
2334
2342
|
await this.query(sql);
|
|
2335
2343
|
}
|
|
@@ -2385,9 +2393,12 @@ class CockroachDBDialect {
|
|
|
2385
2393
|
if (indexDef.include && indexDef.include.length > 0) {
|
|
2386
2394
|
sql += ` INCLUDE (${indexDef.include.map((f) => this.escapeId(f)).join(', ')})`;
|
|
2387
2395
|
}
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2396
|
+
// Partial index predicate - inlined literals, not placeholders. See addIndex.
|
|
2397
|
+
if (indexDef.where) {
|
|
2398
|
+
const predicate = (0, partial_index_1.buildPartialIndexPredicate)(indexDef.where, this.name, this);
|
|
2399
|
+
if (predicate) {
|
|
2400
|
+
sql += ` WHERE ${predicate}`;
|
|
2401
|
+
}
|
|
2391
2402
|
}
|
|
2392
2403
|
await this.query(sql);
|
|
2393
2404
|
}
|
|
@@ -3252,12 +3263,18 @@ class CockroachDBDialect {
|
|
|
3252
3263
|
* Build an ORDER BY clause
|
|
3253
3264
|
*/
|
|
3254
3265
|
buildOrderClause(order, options) {
|
|
3255
|
-
const orderArray = order;
|
|
3256
|
-
if (
|
|
3266
|
+
const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
|
|
3267
|
+
if (orderArray.length === 0) {
|
|
3257
3268
|
return '';
|
|
3258
3269
|
}
|
|
3259
3270
|
const orderParts = [];
|
|
3260
3271
|
for (const item of orderArray) {
|
|
3272
|
+
// Order helpers: asc('name') / desc('name') / random()
|
|
3273
|
+
const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
|
|
3274
|
+
if (orderExpr !== null) {
|
|
3275
|
+
orderParts.push(orderExpr);
|
|
3276
|
+
continue;
|
|
3277
|
+
}
|
|
3261
3278
|
if (Array.isArray(item)) {
|
|
3262
3279
|
const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
|
|
3263
3280
|
const direction = item[1] ? ` ${item[1]}` : '';
|
|
@@ -3977,35 +3994,10 @@ class CockroachDBDialect {
|
|
|
3977
3994
|
}
|
|
3978
3995
|
// Build LIMIT/OFFSET clause
|
|
3979
3996
|
sql += this.buildLimitOffset(options.limit, options.offset);
|
|
3980
|
-
// Handle row-level locking
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
if (options.lock === true) {
|
|
3985
|
-
lockType = 'UPDATE';
|
|
3986
|
-
}
|
|
3987
|
-
else if (typeof options.lock === 'string') {
|
|
3988
|
-
lockType = options.lock;
|
|
3989
|
-
}
|
|
3990
|
-
else if (typeof options.lock === 'object' && options.lock.of) {
|
|
3991
|
-
// lock: { of: Model } - defaults to FOR UPDATE for table-specific locking
|
|
3992
|
-
lockType = 'UPDATE';
|
|
3993
|
-
}
|
|
3994
|
-
if (lockType === 'UPDATE') {
|
|
3995
|
-
sql += ' FOR UPDATE';
|
|
3996
|
-
}
|
|
3997
|
-
else if (lockType === 'SHARE') {
|
|
3998
|
-
sql += ' FOR SHARE';
|
|
3999
|
-
}
|
|
4000
|
-
else if (lockType === 'KEY SHARE') {
|
|
4001
|
-
sql += ' FOR KEY SHARE';
|
|
4002
|
-
}
|
|
4003
|
-
// Handle lock on specific table (CockroachDB supports OF clause)
|
|
4004
|
-
if (typeof options.lock === 'object' && options.lock.of) {
|
|
4005
|
-
const model = options.lock.of;
|
|
4006
|
-
sql += ` OF ${this.escapeId(model.tableName || '')}`;
|
|
4007
|
-
}
|
|
4008
|
-
}
|
|
3997
|
+
// Handle row-level locking. CockroachDB implements the PostgreSQL locking
|
|
3998
|
+
// grammar; the shared helper compiles every documented `lock` shape so the
|
|
3999
|
+
// object forms can't silently produce an unlocked SELECT.
|
|
4000
|
+
sql += (0, lock_clause_helper_1.appendLockClause)(options.lock, lock_clause_helper_1.COCKROACHDB_LOCK_SPEC, (id) => this.escapeId(id));
|
|
4009
4001
|
// Build UNION clause if provided
|
|
4010
4002
|
if (options.union && options.union.length > 0) {
|
|
4011
4003
|
const unionType = options.unionType || 'UNION';
|
|
@@ -4156,33 +4148,10 @@ class CockroachDBDialect {
|
|
|
4156
4148
|
* Get CockroachDB specific lock options
|
|
4157
4149
|
*/
|
|
4158
4150
|
getLockOptions(lock) {
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
case 'UPDATE':
|
|
4164
|
-
sql = 'FOR UPDATE';
|
|
4165
|
-
break;
|
|
4166
|
-
case 'SHARE':
|
|
4167
|
-
sql = 'FOR SHARE';
|
|
4168
|
-
break;
|
|
4169
|
-
case 'KEY SHARE':
|
|
4170
|
-
sql = 'FOR KEY SHARE';
|
|
4171
|
-
break;
|
|
4172
|
-
default:
|
|
4173
|
-
return { sql: '', params };
|
|
4174
|
-
}
|
|
4175
|
-
if (typeof lock === 'object' && lock?.of) {
|
|
4176
|
-
const model = lock.of;
|
|
4177
|
-
sql += ` OF ${this.escapeId(model.tableName || '')}`;
|
|
4178
|
-
}
|
|
4179
|
-
if (typeof lock === 'object' && lock?.nowait) {
|
|
4180
|
-
sql += ' NOWAIT';
|
|
4181
|
-
}
|
|
4182
|
-
else if (typeof lock === 'object' && lock?.skipLocked) {
|
|
4183
|
-
sql += ' SKIP LOCKED';
|
|
4184
|
-
}
|
|
4185
|
-
return { sql, params };
|
|
4151
|
+
return {
|
|
4152
|
+
sql: (0, lock_clause_helper_1.buildLockClause)(lock, lock_clause_helper_1.COCKROACHDB_LOCK_SPEC, (id) => this.escapeId(id)),
|
|
4153
|
+
params: [],
|
|
4154
|
+
};
|
|
4186
4155
|
}
|
|
4187
4156
|
// ---------------------------------------------------------------------------
|
|
4188
4157
|
// User / Privilege management — CockroachDB dialect
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
16
|
exports.DB2Dialect = void 0;
|
|
17
|
+
const partial_index_1 = require("../partial-index");
|
|
17
18
|
// Lazily-loaded `ibm_db` driver. This native (ODBC-based) addon is only loaded
|
|
18
19
|
// the first time a connection is opened, so importing this ORM never dlopens
|
|
19
20
|
// the ibm_db native binding.
|
|
@@ -32,7 +33,9 @@ function loadIbmDb() {
|
|
|
32
33
|
return ibmDbModule;
|
|
33
34
|
}
|
|
34
35
|
const query_stream_helper_1 = require("../query-stream-helper");
|
|
36
|
+
const lock_clause_helper_1 = require("../lock-clause-helper");
|
|
35
37
|
const prorm_1 = require("../../prorm");
|
|
38
|
+
const order_expression_helper_1 = require("../order-expression-helper");
|
|
36
39
|
/**
|
|
37
40
|
* Db2-specific transaction class.
|
|
38
41
|
* Db2 transactions (via ibm_db) are tied to a specific connection - the
|
|
@@ -1031,6 +1034,13 @@ class DB2Dialect {
|
|
|
1031
1034
|
if (options?.include && options.include.length > 0) {
|
|
1032
1035
|
sql += ` INCLUDE (${options.include.map((f) => this.escapeId(f)).join(', ')})`;
|
|
1033
1036
|
}
|
|
1037
|
+
// Db2 has no partial/filtered index predicate (only `EXCLUDE NULL KEYS` on
|
|
1038
|
+
// a unique index, which is not a general WHERE). `where` used to be
|
|
1039
|
+
// accepted by createIndex and then silently dropped here, so the caller got
|
|
1040
|
+
// a full index without being told; say so instead.
|
|
1041
|
+
if (options?.where) {
|
|
1042
|
+
throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
|
|
1043
|
+
}
|
|
1034
1044
|
return sql;
|
|
1035
1045
|
}
|
|
1036
1046
|
async addIndex(tableName, indexName, fields, options) {
|
|
@@ -1671,12 +1681,18 @@ class DB2Dialect {
|
|
|
1671
1681
|
return { sql, values };
|
|
1672
1682
|
}
|
|
1673
1683
|
buildOrderClause(order, _options) {
|
|
1674
|
-
const orderArray = order;
|
|
1675
|
-
if (
|
|
1684
|
+
const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
|
|
1685
|
+
if (orderArray.length === 0) {
|
|
1676
1686
|
return '';
|
|
1677
1687
|
}
|
|
1678
1688
|
const orderParts = [];
|
|
1679
1689
|
for (const item of orderArray) {
|
|
1690
|
+
// Order helpers: asc('name') / desc('name') / random()
|
|
1691
|
+
const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
|
|
1692
|
+
if (orderExpr !== null) {
|
|
1693
|
+
orderParts.push(orderExpr);
|
|
1694
|
+
continue;
|
|
1695
|
+
}
|
|
1680
1696
|
if (Array.isArray(item)) {
|
|
1681
1697
|
const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
|
|
1682
1698
|
const direction = item[1] ? ` ${item[1]}` : '';
|
|
@@ -1902,12 +1918,11 @@ class DB2Dialect {
|
|
|
1902
1918
|
if (options.limit !== undefined || options.offset !== undefined) {
|
|
1903
1919
|
sql += this.buildLimitOffset(options.limit, options.offset);
|
|
1904
1920
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
}
|
|
1921
|
+
// Row-level locking: `FOR UPDATE`, with the read-stability isolation clause
|
|
1922
|
+
// for a shared lock. Db2 has no `NOWAIT` and spells lock-avoidance as
|
|
1923
|
+
// `SKIP LOCKED DATA` inside an isolation clause, so both modifiers throw
|
|
1924
|
+
// rather than being silently dropped.
|
|
1925
|
+
sql += (0, lock_clause_helper_1.appendLockClause)(options.lock, lock_clause_helper_1.DB2_LOCK_SPEC);
|
|
1911
1926
|
return { sql, values };
|
|
1912
1927
|
}
|
|
1913
1928
|
/**
|
|
@@ -627,8 +627,18 @@ export interface TableOptions {
|
|
|
627
627
|
rowFormat?: string;
|
|
628
628
|
indexes?: TableIndex[];
|
|
629
629
|
constraints?: TableConstraint[];
|
|
630
|
-
/**
|
|
631
|
-
|
|
630
|
+
/**
|
|
631
|
+
* Unique keys to create with the table.
|
|
632
|
+
*
|
|
633
|
+
* Both shapes are accepted because both have always been passed: the legacy
|
|
634
|
+
* `{ name: fields[] }` map, and the `UniqueKeyOptions`-style array that
|
|
635
|
+
* `ModelOptions.uniqueKeys` is declared as. The dialects already branch on
|
|
636
|
+
* `Array.isArray`; the type now says so.
|
|
637
|
+
*/
|
|
638
|
+
uniqueKeys?: Record<string, string[]> | Array<{
|
|
639
|
+
name?: string;
|
|
640
|
+
fields: string[];
|
|
641
|
+
}>;
|
|
632
642
|
/** PostgreSQL tablespace */
|
|
633
643
|
tablespace?: string;
|
|
634
644
|
/** PostgreSQL: inherit from parent table */
|
|
@@ -663,7 +673,8 @@ export interface TableIndex {
|
|
|
663
673
|
type?: string;
|
|
664
674
|
using?: string;
|
|
665
675
|
fields: string[];
|
|
666
|
-
where
|
|
676
|
+
/** See {@link IndexOptions.where}. */
|
|
677
|
+
where?: WhereOptions | string;
|
|
667
678
|
/** Expression for expression-based indexes (e.g., LOWER(column)) */
|
|
668
679
|
expression?: string;
|
|
669
680
|
/** Include columns for covering index (PostgreSQL 11+, MySQL 8.0.17+) */
|
|
@@ -700,7 +711,19 @@ export interface IndexOptions {
|
|
|
700
711
|
unique?: boolean;
|
|
701
712
|
type?: string;
|
|
702
713
|
using?: string;
|
|
703
|
-
|
|
714
|
+
/**
|
|
715
|
+
* Partial (filtered) index predicate.
|
|
716
|
+
*
|
|
717
|
+
* Compiled into the `CREATE INDEX ... WHERE ...` clause with every value
|
|
718
|
+
* inlined as an escaped literal, because DDL is executed without a parameter
|
|
719
|
+
* list - see `src/dialects/partial-index.ts`. A string is taken as a
|
|
720
|
+
* predicate the caller already wrote and passed through unchanged.
|
|
721
|
+
*
|
|
722
|
+
* Only databases with real partial indexes accept this (PostgreSQL family,
|
|
723
|
+
* SQLite/Turso, SQL Server filtered indexes); the rest throw an
|
|
724
|
+
* `UnsupportedSchemaObjectError` rather than emit SQL the server rejects.
|
|
725
|
+
*/
|
|
726
|
+
where?: WhereOptions | string;
|
|
704
727
|
/** Expression for expression-based indexes (e.g., LOWER(column)) */
|
|
705
728
|
expression?: string;
|
|
706
729
|
/** Include columns for covering index (PostgreSQL 11+, MySQL 8.0.17+) */
|
|
@@ -727,7 +750,8 @@ export interface IndexDefinition {
|
|
|
727
750
|
type?: string;
|
|
728
751
|
using?: string;
|
|
729
752
|
fields: string[];
|
|
730
|
-
where
|
|
753
|
+
/** See {@link IndexOptions.where}. */
|
|
754
|
+
where?: WhereOptions | string;
|
|
731
755
|
/** See {@link IndexOptions.invisible}. */
|
|
732
756
|
invisible?: boolean;
|
|
733
757
|
}
|
|
@@ -36,6 +36,7 @@ function loadDuckDb() {
|
|
|
36
36
|
const query_stream_helper_1 = require("../query-stream-helper");
|
|
37
37
|
const prorm_1 = require("../../prorm");
|
|
38
38
|
const operators_1 = require("../../operators");
|
|
39
|
+
const order_expression_helper_1 = require("../order-expression-helper");
|
|
39
40
|
function promisifyAll(target, sql, params) {
|
|
40
41
|
return new Promise((resolve, reject) => {
|
|
41
42
|
const cb = (err, rows) => {
|
|
@@ -1437,12 +1438,18 @@ class DuckDBDialect {
|
|
|
1437
1438
|
return { sql: result.sql, values };
|
|
1438
1439
|
}
|
|
1439
1440
|
buildOrderClause(order) {
|
|
1440
|
-
const orderArray = order;
|
|
1441
|
-
if (
|
|
1441
|
+
const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
|
|
1442
|
+
if (orderArray.length === 0) {
|
|
1442
1443
|
return '';
|
|
1443
1444
|
}
|
|
1444
1445
|
const orderParts = [];
|
|
1445
1446
|
for (const item of orderArray) {
|
|
1447
|
+
// Order helpers: asc('name') / desc('name') / random()
|
|
1448
|
+
const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
|
|
1449
|
+
if (orderExpr !== null) {
|
|
1450
|
+
orderParts.push(orderExpr);
|
|
1451
|
+
continue;
|
|
1452
|
+
}
|
|
1446
1453
|
if (Array.isArray(item)) {
|
|
1447
1454
|
const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
|
|
1448
1455
|
const direction = item[1] ? ` ${item[1]}` : '';
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
*/
|
|
49
49
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
50
|
exports.HanaDialect = void 0;
|
|
51
|
+
const partial_index_1 = require("../partial-index");
|
|
51
52
|
// Lazily-loaded `hdb` driver. Required only when a connection is opened, so
|
|
52
53
|
// importing this ORM never loads the hdb driver.
|
|
53
54
|
let hdbModule = null;
|
|
@@ -65,7 +66,9 @@ function loadHdb() {
|
|
|
65
66
|
return hdbModule;
|
|
66
67
|
}
|
|
67
68
|
const query_stream_helper_1 = require("../query-stream-helper");
|
|
69
|
+
const lock_clause_helper_1 = require("../lock-clause-helper");
|
|
68
70
|
const prorm_1 = require("../../prorm");
|
|
71
|
+
const order_expression_helper_1 = require("../order-expression-helper");
|
|
69
72
|
/** HANA requires a LIMIT whenever OFFSET is used; this sentinel stands in for "no limit". */
|
|
70
73
|
const HANA_MAX_LIMIT = 2147483647;
|
|
71
74
|
/**
|
|
@@ -829,6 +832,12 @@ class HanaDialect {
|
|
|
829
832
|
if (options?.unique)
|
|
830
833
|
sql += ' UNIQUE';
|
|
831
834
|
sql += ` INDEX ${this.escapeId(indexName)} ON ${this.quoteTable(tableName)} (${fieldsSql})`;
|
|
835
|
+
// SAP HANA has no partial/filtered indexes (createPartialIndex already
|
|
836
|
+
// throws). `where` was silently dropped here, handing back a full index;
|
|
837
|
+
// fail the same way the dedicated method does.
|
|
838
|
+
if (options?.where) {
|
|
839
|
+
throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
|
|
840
|
+
}
|
|
832
841
|
return sql;
|
|
833
842
|
}
|
|
834
843
|
async addIndex(tableName, indexName, fields, options) {
|
|
@@ -842,6 +851,7 @@ class HanaDialect {
|
|
|
842
851
|
unique: indexDef.unique,
|
|
843
852
|
type: indexDef.type,
|
|
844
853
|
using: indexDef.using,
|
|
854
|
+
where: indexDef.where,
|
|
845
855
|
});
|
|
846
856
|
}
|
|
847
857
|
async dropIndex(tableName, indexName, _options) {
|
|
@@ -1183,12 +1193,18 @@ class HanaDialect {
|
|
|
1183
1193
|
return { sql, values };
|
|
1184
1194
|
}
|
|
1185
1195
|
buildOrderClause(order, _options) {
|
|
1186
|
-
const orderArray = order;
|
|
1187
|
-
if (
|
|
1196
|
+
const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
|
|
1197
|
+
if (orderArray.length === 0) {
|
|
1188
1198
|
return '';
|
|
1189
1199
|
}
|
|
1190
1200
|
const orderParts = [];
|
|
1191
1201
|
for (const item of orderArray) {
|
|
1202
|
+
// Order helpers: asc('name') / desc('name') / random()
|
|
1203
|
+
const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
|
|
1204
|
+
if (orderExpr !== null) {
|
|
1205
|
+
orderParts.push(orderExpr);
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1192
1208
|
if (Array.isArray(item)) {
|
|
1193
1209
|
const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
|
|
1194
1210
|
const direction = item[1] ? ` ${item[1]}` : '';
|
|
@@ -1386,9 +1402,10 @@ class HanaDialect {
|
|
|
1386
1402
|
if (options.limit !== undefined || options.offset !== undefined) {
|
|
1387
1403
|
sql += this.buildLimitOffset(options.limit, options.offset);
|
|
1388
1404
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1405
|
+
// Row-level locking: HANA supports `FOR UPDATE [NOWAIT]` only - no shared
|
|
1406
|
+
// row lock and no SKIP LOCKED, both of which now throw instead of silently
|
|
1407
|
+
// degrading to a plain exclusive lock.
|
|
1408
|
+
sql += (0, lock_clause_helper_1.appendLockClause)(options.lock, lock_clause_helper_1.HANA_LOCK_SPEC);
|
|
1392
1409
|
return { sql, values };
|
|
1393
1410
|
}
|
|
1394
1411
|
/**
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared compiler for `FindOptions.lock` (row-level locking).
|
|
3
|
+
*
|
|
4
|
+
* Every dialect used to re-implement the `lock` option inline inside its own
|
|
5
|
+
* `buildSelectQuery()`, and every one of those copies understood a slightly
|
|
6
|
+
* different subset of the documented `LockOptions` shapes. The object forms
|
|
7
|
+
* (`{ level }`, `{ level, nowait }`, `{ level, skipLocked }`, `{ skipLocked }`)
|
|
8
|
+
* fell through most of them and compiled to *nothing* - the caller asked for a
|
|
9
|
+
* lock, got a plain `SELECT`, and only found out under concurrency. This module
|
|
10
|
+
* centralises the parsing (`normalizeLockOptions`) and the SQL assembly
|
|
11
|
+
* (`buildLockClause`) so a shape can only be dropped by deliberately leaving it
|
|
12
|
+
* out of a dialect's {@link LockClauseSpec} - and a spec that cannot express
|
|
13
|
+
* what was asked for throws instead of silently emitting a weaker lock.
|
|
14
|
+
*
|
|
15
|
+
* Dialects with no row-level locking at all (SQLite and friends) simply never
|
|
16
|
+
* call in here: ignoring `lock` is a deliberate, documented decision for them
|
|
17
|
+
* (SQLite serialises writers anyway), not an oversight.
|
|
18
|
+
*
|
|
19
|
+
* SQL Server is the odd one out - it expresses row locking as a table hint
|
|
20
|
+
* (`FROM t WITH (UPDLOCK, ROWLOCK)`) rather than a trailing clause - so it
|
|
21
|
+
* shares the parsing/validation via {@link buildSqlServerLockHint} while
|
|
22
|
+
* rendering its own syntax.
|
|
23
|
+
*/
|
|
24
|
+
/** The lock levels this ORM knows how to talk about, after normalization. */
|
|
25
|
+
export type NormalizedLockLevel = 'UPDATE' | 'SHARE' | 'KEY SHARE' | 'NO KEY UPDATE';
|
|
26
|
+
/** Model shape (`lock.of`) reduced to the only fields the SQL needs. */
|
|
27
|
+
type LockOfModel = {
|
|
28
|
+
tableName?: string;
|
|
29
|
+
name?: string;
|
|
30
|
+
};
|
|
31
|
+
/** A `lock` option after every documented shape has been collapsed into one struct. */
|
|
32
|
+
export interface NormalizedLock {
|
|
33
|
+
level: NormalizedLockLevel;
|
|
34
|
+
nowait: boolean;
|
|
35
|
+
skipLocked: boolean;
|
|
36
|
+
/** The model passed as `lock.of`, if any. Only some dialects can express it. */
|
|
37
|
+
of?: LockOfModel;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* How one dialect renders row locks. A level that is absent from `levels` is
|
|
41
|
+
* one this dialect cannot express, and asking for it throws.
|
|
42
|
+
*/
|
|
43
|
+
export interface LockClauseSpec {
|
|
44
|
+
/** Human-readable dialect name, used in error messages. */
|
|
45
|
+
name: string;
|
|
46
|
+
/**
|
|
47
|
+
* SQL for each supported lock level. A function form lets a dialect pick its
|
|
48
|
+
* syntax based on the requested modifiers (MySQL/MariaDB switch between the
|
|
49
|
+
* legacy `LOCK IN SHARE MODE` and `FOR SHARE`, which is the only one of the
|
|
50
|
+
* two that accepts `NOWAIT`/`SKIP LOCKED`).
|
|
51
|
+
*/
|
|
52
|
+
levels: Partial<Record<NormalizedLockLevel, string | ((lock: NormalizedLock) => string)>>;
|
|
53
|
+
/** Levels that accept `NOWAIT`. Omit entirely if the dialect has no `NOWAIT`. */
|
|
54
|
+
nowaitLevels?: NormalizedLockLevel[];
|
|
55
|
+
/** Levels that accept `SKIP LOCKED`. Omit entirely if the dialect has none. */
|
|
56
|
+
skipLockedLevels?: NormalizedLockLevel[];
|
|
57
|
+
/**
|
|
58
|
+
* Renders `lock.of`. Omitted when the dialect has no table-scoped locking, in
|
|
59
|
+
* which case `of` only signals that a lock was wanted at all (which is
|
|
60
|
+
* exactly how those dialects have always behaved).
|
|
61
|
+
*/
|
|
62
|
+
renderOf?: (model: LockOfModel, escapeId: (id: string) => string) => string;
|
|
63
|
+
/** Levels for which `renderOf` applies. Defaults to every supported level. */
|
|
64
|
+
ofLevels?: NormalizedLockLevel[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Collapse any documented `lock` shape into a {@link NormalizedLock}.
|
|
68
|
+
*
|
|
69
|
+
* Returns `undefined` for a falsy lock (`undefined`, `false`, `null`), which
|
|
70
|
+
* every caller treats as "no locking clause".
|
|
71
|
+
*
|
|
72
|
+
* Accepted shapes:
|
|
73
|
+
* - `true` -> `FOR UPDATE`
|
|
74
|
+
* - `'UPDATE' | 'SHARE' | 'KEY SHARE' | 'NO KEY UPDATE'` (case-insensitive)
|
|
75
|
+
* - `{ of: Model }` -> `FOR UPDATE` scoped to that table
|
|
76
|
+
* - `{ level?, nowait?, skipLocked?, of? }` -> level defaults to `UPDATE`
|
|
77
|
+
*
|
|
78
|
+
* `mode` is accepted as an alias for `level` and `noWait` for `nowait`, since
|
|
79
|
+
* the MariaDB dialect has always documented those spellings.
|
|
80
|
+
*
|
|
81
|
+
* Throws on a level it doesn't recognise rather than quietly emitting no lock.
|
|
82
|
+
*/
|
|
83
|
+
export declare function normalizeLockOptions(lock: unknown): NormalizedLock | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Build the trailing locking clause for a dialect, e.g. `FOR UPDATE OF "users" NOWAIT`.
|
|
86
|
+
*
|
|
87
|
+
* Returns `''` when there is no lock to apply. Throws when the requested lock
|
|
88
|
+
* cannot be expressed by this dialect - a lock the caller believes they hold
|
|
89
|
+
* but don't is far worse than a loud failure at query-build time.
|
|
90
|
+
*
|
|
91
|
+
* @param lock - the raw `options.lock` value
|
|
92
|
+
* @param spec - the dialect's locking capabilities
|
|
93
|
+
* @param escapeId - the dialect's identifier quoter, used for `lock.of`
|
|
94
|
+
*/
|
|
95
|
+
export declare function buildLockClause(lock: unknown, spec: LockClauseSpec, escapeId?: (id: string) => string): string;
|
|
96
|
+
/**
|
|
97
|
+
* Same as {@link buildLockClause} but returns the clause with a leading space
|
|
98
|
+
* (or `''`), which is how every `buildSelectQuery()` wants to append it.
|
|
99
|
+
*/
|
|
100
|
+
export declare function appendLockClause(lock: unknown, spec: LockClauseSpec, escapeId?: (id: string) => string): string;
|
|
101
|
+
/**
|
|
102
|
+
* PostgreSQL: `FOR UPDATE | NO KEY UPDATE | SHARE | KEY SHARE [OF table] [NOWAIT | SKIP LOCKED]`.
|
|
103
|
+
* The full matrix - every level accepts both modifiers and the `OF` clause.
|
|
104
|
+
*/
|
|
105
|
+
export declare const POSTGRES_LOCK_SPEC: LockClauseSpec;
|
|
106
|
+
/** CockroachDB implements the PostgreSQL locking grammar. */
|
|
107
|
+
export declare const COCKROACHDB_LOCK_SPEC: LockClauseSpec;
|
|
108
|
+
/**
|
|
109
|
+
* MySQL 8.0+: `FOR UPDATE [OF tbl] [NOWAIT | SKIP LOCKED]` and
|
|
110
|
+
* `FOR SHARE [OF tbl] [NOWAIT | SKIP LOCKED]`.
|
|
111
|
+
*
|
|
112
|
+
* A bare shared lock still compiles to the pre-8.0 `LOCK IN SHARE MODE`
|
|
113
|
+
* spelling (which every supported MySQL understands, and which existing
|
|
114
|
+
* `lock: 'SHARE'` callers already get); it only switches to the 8.0 `FOR SHARE`
|
|
115
|
+
* form when a modifier is requested, because `LOCK IN SHARE MODE` accepts
|
|
116
|
+
* neither `NOWAIT` nor `SKIP LOCKED`.
|
|
117
|
+
*/
|
|
118
|
+
export declare const MYSQL_LOCK_SPEC: LockClauseSpec;
|
|
119
|
+
/**
|
|
120
|
+
* MariaDB: `FOR UPDATE` / `LOCK IN SHARE MODE` on every version, with
|
|
121
|
+
* `FOR SHARE` and the `SKIP LOCKED` / `NOWAIT` modifiers from 10.6.
|
|
122
|
+
*
|
|
123
|
+
* MariaDB has no `FOR UPDATE OF <table>`, so `lock.of` only signals that a lock
|
|
124
|
+
* was wanted (matching this dialect's long-standing behaviour).
|
|
125
|
+
*/
|
|
126
|
+
export declare const MARIADB_LOCK_SPEC: LockClauseSpec;
|
|
127
|
+
/**
|
|
128
|
+
* Oracle: `FOR UPDATE [OF column] [NOWAIT | SKIP LOCKED]`.
|
|
129
|
+
*
|
|
130
|
+
* Oracle has no shared row lock (`SHARE`/`KEY SHARE` have no equivalent), and
|
|
131
|
+
* its `OF` names *columns*, not a table - a `lock.of` model can't be turned into
|
|
132
|
+
* one, so `of` is treated as a plain `FOR UPDATE` request exactly as it always
|
|
133
|
+
* has been.
|
|
134
|
+
*/
|
|
135
|
+
export declare const ORACLE_LOCK_SPEC: LockClauseSpec;
|
|
136
|
+
/**
|
|
137
|
+
* Db2: `FOR UPDATE`, plus the `WITH RS` isolation clause for a read-stability
|
|
138
|
+
* (shared) lock. Db2 spells lock-avoidance as `SKIP LOCKED DATA` inside an
|
|
139
|
+
* isolation clause and has no `NOWAIT` at all, so both modifiers throw rather
|
|
140
|
+
* than being approximated.
|
|
141
|
+
*/
|
|
142
|
+
export declare const DB2_LOCK_SPEC: LockClauseSpec;
|
|
143
|
+
/** SAP HANA: `FOR UPDATE [NOWAIT]`. No shared row lock and no `SKIP LOCKED`. */
|
|
144
|
+
export declare const HANA_LOCK_SPEC: LockClauseSpec;
|
|
145
|
+
/**
|
|
146
|
+
* Build the SQL Server locking table hint, e.g. `WITH (UPDLOCK, ROWLOCK)`.
|
|
147
|
+
*
|
|
148
|
+
* T-SQL has no `FOR UPDATE`; row locking is requested with hints on the table
|
|
149
|
+
* reference: `UPDLOCK`/`HOLDLOCK` for the lock itself, `ROWLOCK` to pin the
|
|
150
|
+
* granularity to rows, `READPAST` for `SKIP LOCKED` and `NOWAIT` for `NOWAIT`.
|
|
151
|
+
*
|
|
152
|
+
* Returns `''` when there is no lock to apply.
|
|
153
|
+
*
|
|
154
|
+
* @param lock - the raw `options.lock` value
|
|
155
|
+
* @param tableName - the table the hint will be attached to, so a `lock.of`
|
|
156
|
+
* naming some *other* table can be rejected instead of silently locking the
|
|
157
|
+
* wrong one (a hint only applies to the table reference it sits on).
|
|
158
|
+
*/
|
|
159
|
+
export declare function buildSqlServerLockHint(lock: unknown, tableName?: string): string;
|
|
160
|
+
/**
|
|
161
|
+
* Look up the locking spec for a dialect name. Returns `undefined` for a
|
|
162
|
+
* dialect with no row-level locking (SQLite and friends) and for SQL Server,
|
|
163
|
+
* whose locking is a table hint rather than a trailing clause - callers should
|
|
164
|
+
* treat `undefined` as "emit no trailing lock clause".
|
|
165
|
+
*/
|
|
166
|
+
export declare function getLockSpecForDialect(dialect: string): LockClauseSpec | undefined;
|
|
167
|
+
export {};
|