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
|
@@ -772,7 +772,14 @@ export declare class OracleDialect implements Dialect {
|
|
|
772
772
|
*/
|
|
773
773
|
private buildOperatorWhere;
|
|
774
774
|
/**
|
|
775
|
-
* Build ORDER BY clause
|
|
775
|
+
* Build ORDER BY clause.
|
|
776
|
+
*
|
|
777
|
+
* Every `Order` shape is handled explicitly. Previously this only ever
|
|
778
|
+
* called `Object.keys()`/`Object.entries()` on each item, which is correct
|
|
779
|
+
* for the `{ field: 'DESC' }` map form but silently mis-reads every other
|
|
780
|
+
* one: a `['name', 'DESC']` tuple yielded its indices (`ORDER BY "0" ASC`)
|
|
781
|
+
* and a bare `'name DESC'` string yielded its character indices — ordering
|
|
782
|
+
* by the wrong thing rather than failing.
|
|
776
783
|
*/
|
|
777
784
|
buildOrderClause(order: Order, _options?: BuildOptions): string;
|
|
778
785
|
/**
|
|
@@ -1020,9 +1027,13 @@ export declare class OracleDialect implements Dialect {
|
|
|
1020
1027
|
*/
|
|
1021
1028
|
commentColumn(tableName: string, columnName: string, comment: string): Promise<void>;
|
|
1022
1029
|
/**
|
|
1023
|
-
*
|
|
1030
|
+
* Partial indexes - not available on Oracle.
|
|
1031
|
+
*
|
|
1032
|
+
* Oracle's `CREATE INDEX` has no `WHERE` clause; the equivalent is a
|
|
1033
|
+
* function-based index on a CASE expression that yields NULL for the rows to
|
|
1034
|
+
* exclude, since a B-tree index does not store all-NULL keys.
|
|
1024
1035
|
*/
|
|
1025
|
-
createPartialIndex(
|
|
1036
|
+
createPartialIndex(_tableName: string, _indexName: string, _fields: string[], _where: string, _options?: IndexOptions): Promise<void>;
|
|
1026
1037
|
/**
|
|
1027
1038
|
* Create an expression index (functional index)
|
|
1028
1039
|
*/
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.OracleDialect = void 0;
|
|
8
8
|
exports.createOracleDialect = createOracleDialect;
|
|
9
|
+
const partial_index_1 = require("../partial-index");
|
|
9
10
|
// Lazily-loaded `oracledb` driver. The native Oracle client is only loaded the
|
|
10
11
|
// first time a connection is opened, so importing this ORM never dlopens the
|
|
11
12
|
// oracledb native addon.
|
|
@@ -24,8 +25,10 @@ function loadOracleDb() {
|
|
|
24
25
|
return oracledbModule;
|
|
25
26
|
}
|
|
26
27
|
const query_stream_helper_1 = require("../query-stream-helper");
|
|
28
|
+
const lock_clause_helper_1 = require("../lock-clause-helper");
|
|
27
29
|
const errors_1 = require("../../errors");
|
|
28
30
|
const crypto_1 = require("crypto");
|
|
31
|
+
const order_expression_helper_1 = require("../order-expression-helper");
|
|
29
32
|
/**
|
|
30
33
|
* Oracle-specific transaction class
|
|
31
34
|
*/
|
|
@@ -1295,9 +1298,12 @@ class OracleDialect {
|
|
|
1295
1298
|
sql += ` ${options.type.toUpperCase()}`;
|
|
1296
1299
|
}
|
|
1297
1300
|
sql += ` INDEX ${this.quoteIdentifier(indexName)} ON ${this.quoteTable(tableName)} (${fieldsSql})`;
|
|
1301
|
+
// Oracle's CREATE INDEX has no WHERE clause - there are no partial indexes.
|
|
1302
|
+
// This used to append one built by the ordinary WHERE builder, so the
|
|
1303
|
+
// statement reached the server both syntactically invalid and carrying
|
|
1304
|
+
// unbound placeholders. Fail with a reason instead.
|
|
1298
1305
|
if (options?.where) {
|
|
1299
|
-
|
|
1300
|
-
sql += ` WHERE ${whereClause.sql}`;
|
|
1306
|
+
throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
|
|
1301
1307
|
}
|
|
1302
1308
|
return sql;
|
|
1303
1309
|
}
|
|
@@ -1616,22 +1622,52 @@ class OracleDialect {
|
|
|
1616
1622
|
}
|
|
1617
1623
|
}
|
|
1618
1624
|
/**
|
|
1619
|
-
* Build ORDER BY clause
|
|
1625
|
+
* Build ORDER BY clause.
|
|
1626
|
+
*
|
|
1627
|
+
* Every `Order` shape is handled explicitly. Previously this only ever
|
|
1628
|
+
* called `Object.keys()`/`Object.entries()` on each item, which is correct
|
|
1629
|
+
* for the `{ field: 'DESC' }` map form but silently mis-reads every other
|
|
1630
|
+
* one: a `['name', 'DESC']` tuple yielded its indices (`ORDER BY "0" ASC`)
|
|
1631
|
+
* and a bare `'name DESC'` string yielded its character indices — ordering
|
|
1632
|
+
* by the wrong thing rather than failing.
|
|
1620
1633
|
*/
|
|
1621
1634
|
buildOrderClause(order, _options) {
|
|
1622
1635
|
if (!order || (Array.isArray(order) && order.length === 0)) {
|
|
1623
1636
|
return '';
|
|
1624
1637
|
}
|
|
1625
|
-
const orderParts =
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1638
|
+
const orderParts = [];
|
|
1639
|
+
const term = (field, direction) => `${this.quoteIdentifier(field)} ${String(direction).toUpperCase() === 'DESC' ? 'DESC' : 'ASC'}`;
|
|
1640
|
+
const processOrder = (orderItem) => {
|
|
1641
|
+
// Order helpers: asc('name') / desc('name') / random(). Handled first so
|
|
1642
|
+
// a bare `order: random()` works as well as one inside the order array.
|
|
1643
|
+
const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, orderItem);
|
|
1644
|
+
if (orderExpr !== null) {
|
|
1645
|
+
orderParts.push(orderExpr);
|
|
1646
|
+
}
|
|
1647
|
+
else if (typeof orderItem === 'string') {
|
|
1648
|
+
// 'name' or 'name DESC'
|
|
1649
|
+
const [field, direction] = orderItem.trim().split(/\s+/);
|
|
1650
|
+
orderParts.push(term(field, direction));
|
|
1651
|
+
}
|
|
1652
|
+
else if (Array.isArray(orderItem)) {
|
|
1653
|
+
// ['name', 'DESC'] or ['name']
|
|
1654
|
+
const [field, direction] = orderItem;
|
|
1655
|
+
orderParts.push(term(String(field), direction));
|
|
1656
|
+
}
|
|
1657
|
+
else if (orderItem && typeof orderItem === 'object') {
|
|
1658
|
+
// { field: 'DESC' } map form
|
|
1659
|
+
for (const [field, direction] of Object.entries(orderItem)) {
|
|
1660
|
+
orderParts.push(term(field, direction));
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
};
|
|
1664
|
+
if (Array.isArray(order)) {
|
|
1665
|
+
order.forEach(processOrder);
|
|
1666
|
+
}
|
|
1667
|
+
else {
|
|
1668
|
+
processOrder(order);
|
|
1669
|
+
}
|
|
1670
|
+
return orderParts.length > 0 ? `ORDER BY ${orderParts.join(', ')}` : '';
|
|
1635
1671
|
}
|
|
1636
1672
|
/**
|
|
1637
1673
|
* Build a list of ORDER BY items (without the leading `ORDER BY` keyword),
|
|
@@ -1899,13 +1935,11 @@ class OracleDialect {
|
|
|
1899
1935
|
sql += ` AND ROWNUM <= ${limit + offset}`;
|
|
1900
1936
|
}
|
|
1901
1937
|
}
|
|
1902
|
-
// FOR UPDATE
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
}
|
|
1908
|
-
}
|
|
1938
|
+
// Row-level locking: `FOR UPDATE [NOWAIT | SKIP LOCKED]`. Oracle has no
|
|
1939
|
+
// shared row lock, so `lock: 'SHARE'` now throws instead of quietly turning
|
|
1940
|
+
// into an exclusive `FOR UPDATE NOWAIT`. `lock.of` names *columns* in
|
|
1941
|
+
// Oracle, not a table, so a `{ of: Model }` lock stays a plain FOR UPDATE.
|
|
1942
|
+
sql += (0, lock_clause_helper_1.appendLockClause)(options.lock, lock_clause_helper_1.ORACLE_LOCK_SPEC);
|
|
1909
1943
|
return { sql, values };
|
|
1910
1944
|
}
|
|
1911
1945
|
/**
|
|
@@ -2705,22 +2739,18 @@ END;
|
|
|
2705
2739
|
}
|
|
2706
2740
|
// ==================== Advanced Indexes ====================
|
|
2707
2741
|
/**
|
|
2708
|
-
*
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
if (options?.compress) {
|
|
2721
|
-
sql += ` COMPRESS`;
|
|
2722
|
-
}
|
|
2723
|
-
await this.query(sql);
|
|
2742
|
+
* Partial indexes - not available on Oracle.
|
|
2743
|
+
*
|
|
2744
|
+
* Oracle's `CREATE INDEX` has no `WHERE` clause; the equivalent is a
|
|
2745
|
+
* function-based index on a CASE expression that yields NULL for the rows to
|
|
2746
|
+
* exclude, since a B-tree index does not store all-NULL keys.
|
|
2747
|
+
*/
|
|
2748
|
+
async createPartialIndex(_tableName, _indexName, _fields, _where, _options) {
|
|
2749
|
+
// This used to emit `CREATE INDEX ... WHERE ...`, which Oracle has never
|
|
2750
|
+
// accepted. The Oracle-shaped equivalent is a function-based index on a
|
|
2751
|
+
// CASE expression that returns NULL for the excluded rows (NULLs are not
|
|
2752
|
+
// stored in a B-tree index), i.e. createExpressionIndex.
|
|
2753
|
+
throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
|
|
2724
2754
|
}
|
|
2725
2755
|
/**
|
|
2726
2756
|
* Create an expression index (functional index)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared rendering for the `OrderExpression` objects produced by the public
|
|
3
|
+
* `asc()` / `desc()` / `random()` helpers exported from `src/operators.ts`.
|
|
4
|
+
*
|
|
5
|
+
* Every dialect implements its own `buildOrderClause()` with slightly
|
|
6
|
+
* different handling of the legacy `order` shapes it accepts (bare strings,
|
|
7
|
+
* `['field', 'DESC']` tuples, `{ field: 'DESC' }` maps, `[Model, 'field']`
|
|
8
|
+
* pairs, ...). Rather than teach ~27 dialect files how to recognise and
|
|
9
|
+
* render an `OrderExpression` — the same code copied 27 times — each dialect
|
|
10
|
+
* calls into this module for exactly that one item shape and keeps its own
|
|
11
|
+
* handling for everything else. This mirrors `./query-stream-helper.ts`,
|
|
12
|
+
* which factors one cross-cutting behaviour out of the dialects the same way.
|
|
13
|
+
*
|
|
14
|
+
* The one genuinely dialect-specific piece is `random()`: the function used
|
|
15
|
+
* to produce a random sort key differs per engine, so it is looked up from
|
|
16
|
+
* `RANDOM_FUNCTIONS` by the dialect's own `name`. Dialects with no known
|
|
17
|
+
* random-ordering function throw a descriptive error rather than silently
|
|
18
|
+
* emitting SQL that would not run (or, worse, no `ORDER BY` at all, which is
|
|
19
|
+
* what `order: random()` used to do everywhere).
|
|
20
|
+
*/
|
|
21
|
+
import { type OrderExpression } from '../operators';
|
|
22
|
+
import type { Order } from '../types';
|
|
23
|
+
/**
|
|
24
|
+
* The slice of a dialect this helper needs. Deliberately narrower than the
|
|
25
|
+
* full `Dialect` interface so it can be unit tested with a stub, and so
|
|
26
|
+
* `buildOrderClause()` implementations can pass `this` without a cast.
|
|
27
|
+
*/
|
|
28
|
+
export interface OrderExpressionDialect {
|
|
29
|
+
/** The dialect's registered name, e.g. `'sqlite'`, `'mysql'`, `'mssql'`. */
|
|
30
|
+
readonly name: string;
|
|
31
|
+
/** The dialect's own identifier quoting. */
|
|
32
|
+
escapeId(identifier: unknown): string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The SQL expression that orders rows randomly on `dialectName`.
|
|
36
|
+
*
|
|
37
|
+
* @throws if the dialect has no known random-ordering function — callers get
|
|
38
|
+
* an actionable error instead of SQL that silently does the wrong thing.
|
|
39
|
+
*/
|
|
40
|
+
export declare function randomOrderFunction(dialectName: string | undefined): string;
|
|
41
|
+
/**
|
|
42
|
+
* Render one `OrderExpression` as an `ORDER BY` list item (no leading
|
|
43
|
+
* `ORDER BY`), using the dialect's own identifier quoting.
|
|
44
|
+
*/
|
|
45
|
+
export declare function renderOrderExpression(dialect: OrderExpressionDialect, expression: OrderExpression): string;
|
|
46
|
+
/**
|
|
47
|
+
* Render `item` if — and only if — it is an `OrderExpression`; otherwise
|
|
48
|
+
* return `null` so the caller falls through to its own handling of the
|
|
49
|
+
* legacy order shapes.
|
|
50
|
+
*
|
|
51
|
+
* Usage inside a dialect's `buildOrderClause()`:
|
|
52
|
+
*
|
|
53
|
+
* ```ts
|
|
54
|
+
* const orderExpr = renderOrderExpressionItem(this, item);
|
|
55
|
+
* if (orderExpr !== null) {
|
|
56
|
+
* orderParts.push(orderExpr);
|
|
57
|
+
* continue;
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare function renderOrderExpressionItem(dialect: OrderExpressionDialect, item: unknown): string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Normalise a `FindOptions.order` value into the list of items a dialect
|
|
64
|
+
* should iterate over.
|
|
65
|
+
*
|
|
66
|
+
* Behaviour is intentionally conservative so no currently-working shape
|
|
67
|
+
* changes: arrays pass straight through, a **bare** `OrderExpression`
|
|
68
|
+
* (`order: random()`) is wrapped into a single-item list, and anything else
|
|
69
|
+
* non-array yields `[]` — exactly what the `!Array.isArray(order)` guard the
|
|
70
|
+
* dialects used to open with produced.
|
|
71
|
+
*/
|
|
72
|
+
export declare function normalizeOrderItems(order: Order | undefined | null): unknown[];
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared rendering for the `OrderExpression` objects produced by the public
|
|
4
|
+
* `asc()` / `desc()` / `random()` helpers exported from `src/operators.ts`.
|
|
5
|
+
*
|
|
6
|
+
* Every dialect implements its own `buildOrderClause()` with slightly
|
|
7
|
+
* different handling of the legacy `order` shapes it accepts (bare strings,
|
|
8
|
+
* `['field', 'DESC']` tuples, `{ field: 'DESC' }` maps, `[Model, 'field']`
|
|
9
|
+
* pairs, ...). Rather than teach ~27 dialect files how to recognise and
|
|
10
|
+
* render an `OrderExpression` — the same code copied 27 times — each dialect
|
|
11
|
+
* calls into this module for exactly that one item shape and keeps its own
|
|
12
|
+
* handling for everything else. This mirrors `./query-stream-helper.ts`,
|
|
13
|
+
* which factors one cross-cutting behaviour out of the dialects the same way.
|
|
14
|
+
*
|
|
15
|
+
* The one genuinely dialect-specific piece is `random()`: the function used
|
|
16
|
+
* to produce a random sort key differs per engine, so it is looked up from
|
|
17
|
+
* `RANDOM_FUNCTIONS` by the dialect's own `name`. Dialects with no known
|
|
18
|
+
* random-ordering function throw a descriptive error rather than silently
|
|
19
|
+
* emitting SQL that would not run (or, worse, no `ORDER BY` at all, which is
|
|
20
|
+
* what `order: random()` used to do everywhere).
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.randomOrderFunction = randomOrderFunction;
|
|
24
|
+
exports.renderOrderExpression = renderOrderExpression;
|
|
25
|
+
exports.renderOrderExpressionItem = renderOrderExpressionItem;
|
|
26
|
+
exports.normalizeOrderItems = normalizeOrderItems;
|
|
27
|
+
const operators_1 = require("../operators");
|
|
28
|
+
/**
|
|
29
|
+
* The expression each dialect uses to produce a per-row random sort key.
|
|
30
|
+
*
|
|
31
|
+
* Keyed by `Dialect.name`, so dialects that inherit `buildOrderClause()` from
|
|
32
|
+
* a parent (TiDB from MySQL, Turso from SQLite, TimescaleDB from Postgres,
|
|
33
|
+
* ...) still get their own function — the lookup happens at call time against
|
|
34
|
+
* the concrete instance's `name`.
|
|
35
|
+
*
|
|
36
|
+
* Notes on the less obvious entries:
|
|
37
|
+
* - SQL Server: `RAND()` is evaluated **once per query** in T-SQL, so
|
|
38
|
+
* `ORDER BY RAND()` does not shuffle anything. `NEWID()` is the documented
|
|
39
|
+
* way to order rows randomly.
|
|
40
|
+
* - Oracle has no `RANDOM()`/`RAND()`; the built-in package function
|
|
41
|
+
* `DBMS_RANDOM.VALUE` is the standard idiom.
|
|
42
|
+
* - ClickHouse's function is `rand()` (lower case is always accepted).
|
|
43
|
+
*
|
|
44
|
+
* Dialects deliberately absent (they throw instead of guessing):
|
|
45
|
+
* - `questdb`: QuestDB's random generators (`rnd_double()` and friends) are
|
|
46
|
+
* row-generator functions, not a general random sort key.
|
|
47
|
+
*/
|
|
48
|
+
const RANDOM_FUNCTIONS = {
|
|
49
|
+
// RANDOM()
|
|
50
|
+
sqlite: 'RANDOM()',
|
|
51
|
+
sqlite3: 'RANDOM()',
|
|
52
|
+
turso: 'RANDOM()',
|
|
53
|
+
postgres: 'RANDOM()',
|
|
54
|
+
postgresql: 'RANDOM()',
|
|
55
|
+
cockroachdb: 'RANDOM()',
|
|
56
|
+
cratedb: 'RANDOM()',
|
|
57
|
+
greenplum: 'RANDOM()',
|
|
58
|
+
timescaledb: 'RANDOM()',
|
|
59
|
+
yugabytedb: 'RANDOM()',
|
|
60
|
+
redshift: 'RANDOM()',
|
|
61
|
+
vertica: 'RANDOM()',
|
|
62
|
+
exasol: 'RANDOM()',
|
|
63
|
+
duckdb: 'RANDOM()',
|
|
64
|
+
snowflake: 'RANDOM()',
|
|
65
|
+
trino: 'RANDOM()',
|
|
66
|
+
// RAND()
|
|
67
|
+
mysql: 'RAND()',
|
|
68
|
+
mariadb: 'RAND()',
|
|
69
|
+
tidb: 'RAND()',
|
|
70
|
+
singlestore: 'RAND()',
|
|
71
|
+
databricks: 'RAND()',
|
|
72
|
+
firebird: 'RAND()',
|
|
73
|
+
hana: 'RAND()',
|
|
74
|
+
db2: 'RAND()',
|
|
75
|
+
spanner: 'RAND()',
|
|
76
|
+
// Dialect-specific spellings
|
|
77
|
+
clickhouse: 'rand()',
|
|
78
|
+
mssql: 'NEWID()',
|
|
79
|
+
sqlserver: 'NEWID()',
|
|
80
|
+
oracle: 'DBMS_RANDOM.VALUE',
|
|
81
|
+
oracledb: 'DBMS_RANDOM.VALUE',
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* The SQL expression that orders rows randomly on `dialectName`.
|
|
85
|
+
*
|
|
86
|
+
* @throws if the dialect has no known random-ordering function — callers get
|
|
87
|
+
* an actionable error instead of SQL that silently does the wrong thing.
|
|
88
|
+
*/
|
|
89
|
+
function randomOrderFunction(dialectName) {
|
|
90
|
+
const key = String(dialectName ?? '').toLowerCase();
|
|
91
|
+
const fn = RANDOM_FUNCTIONS[key];
|
|
92
|
+
if (!fn) {
|
|
93
|
+
throw new Error(`random() ordering is not supported by the '${dialectName ?? 'unknown'}' dialect: ` +
|
|
94
|
+
`no known random-ordering function. Pass a raw ORDER BY expression instead, ` +
|
|
95
|
+
`e.g. order: ['<dialect random expression>'].`);
|
|
96
|
+
}
|
|
97
|
+
return fn;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Render one `OrderExpression` as an `ORDER BY` list item (no leading
|
|
101
|
+
* `ORDER BY`), using the dialect's own identifier quoting.
|
|
102
|
+
*/
|
|
103
|
+
function renderOrderExpression(dialect, expression) {
|
|
104
|
+
const direction = String(expression.direction ?? '').toUpperCase();
|
|
105
|
+
if (direction === 'RANDOM') {
|
|
106
|
+
return randomOrderFunction(dialect.name);
|
|
107
|
+
}
|
|
108
|
+
if (direction !== 'ASC' && direction !== 'DESC') {
|
|
109
|
+
// `direction` is typed as a union, but `order` values routinely arrive
|
|
110
|
+
// from untyped callers (JS consumers, JSON config). Interpolating an
|
|
111
|
+
// arbitrary string straight into ORDER BY would be an injection point,
|
|
112
|
+
// so reject anything that is not a real sort direction.
|
|
113
|
+
throw new Error(`Invalid order direction '${String(expression.direction)}'. ` +
|
|
114
|
+
`Expected 'ASC', 'DESC' or 'RANDOM'.`);
|
|
115
|
+
}
|
|
116
|
+
return `${dialect.escapeId(expression.field)} ${direction}`;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Render `item` if — and only if — it is an `OrderExpression`; otherwise
|
|
120
|
+
* return `null` so the caller falls through to its own handling of the
|
|
121
|
+
* legacy order shapes.
|
|
122
|
+
*
|
|
123
|
+
* Usage inside a dialect's `buildOrderClause()`:
|
|
124
|
+
*
|
|
125
|
+
* ```ts
|
|
126
|
+
* const orderExpr = renderOrderExpressionItem(this, item);
|
|
127
|
+
* if (orderExpr !== null) {
|
|
128
|
+
* orderParts.push(orderExpr);
|
|
129
|
+
* continue;
|
|
130
|
+
* }
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
function renderOrderExpressionItem(dialect, item) {
|
|
134
|
+
return (0, operators_1.isOrderExpression)(item) ? renderOrderExpression(dialect, item) : null;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Normalise a `FindOptions.order` value into the list of items a dialect
|
|
138
|
+
* should iterate over.
|
|
139
|
+
*
|
|
140
|
+
* Behaviour is intentionally conservative so no currently-working shape
|
|
141
|
+
* changes: arrays pass straight through, a **bare** `OrderExpression`
|
|
142
|
+
* (`order: random()`) is wrapped into a single-item list, and anything else
|
|
143
|
+
* non-array yields `[]` — exactly what the `!Array.isArray(order)` guard the
|
|
144
|
+
* dialects used to open with produced.
|
|
145
|
+
*/
|
|
146
|
+
function normalizeOrderItems(order) {
|
|
147
|
+
if (Array.isArray(order)) {
|
|
148
|
+
return order;
|
|
149
|
+
}
|
|
150
|
+
if ((0, operators_1.isOrderExpression)(order)) {
|
|
151
|
+
return [order];
|
|
152
|
+
}
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Partial-index predicates (`IndexOptions.where`).
|
|
3
|
+
*
|
|
4
|
+
* A partial index predicate lives inside DDL, and DDL is executed without a
|
|
5
|
+
* parameter list. Compiling `where` through the ordinary WHERE builder
|
|
6
|
+
* therefore produced `CREATE INDEX ... WHERE "status" = ?` with the values
|
|
7
|
+
* silently dropped on the floor - SQLite rejects that outright ("parameters
|
|
8
|
+
* prohibited in partial index WHERE clauses") and PostgreSQL fails on the
|
|
9
|
+
* dangling `$1`. So predicates are compiled here instead, with every value
|
|
10
|
+
* inlined as a literal through the dialect's own `escape()`.
|
|
11
|
+
*
|
|
12
|
+
* That inlining makes `escape()` the security boundary for this path: nothing
|
|
13
|
+
* in this module hand-rolls quoting, and no user value ever reaches the SQL by
|
|
14
|
+
* concatenation. `escapeId()` does the same job for column names.
|
|
15
|
+
*
|
|
16
|
+
* Only the operator forms that mean something in a predicate are supported.
|
|
17
|
+
* Anything else throws with a reason rather than emitting SQL the server will
|
|
18
|
+
* reject - the same convention as {@link UnsupportedSchemaObjectError} in
|
|
19
|
+
* `src/schema-objects.ts`, which this module reuses for dialects that have no
|
|
20
|
+
* partial indexes at all.
|
|
21
|
+
*/
|
|
22
|
+
import { UnsupportedSchemaObjectError } from '../schema-objects';
|
|
23
|
+
/**
|
|
24
|
+
* The escaping surface a predicate needs. Every dialect implements both
|
|
25
|
+
* (they are abstract on `BaseDialect`), so any dialect can be passed straight in.
|
|
26
|
+
*/
|
|
27
|
+
export interface PartialIndexEscaper {
|
|
28
|
+
escape(value: any): string;
|
|
29
|
+
escapeId(identifier: string): string;
|
|
30
|
+
}
|
|
31
|
+
/** Whether `dialect` supports a `WHERE` predicate on `CREATE INDEX`. */
|
|
32
|
+
export declare function supportsPartialIndexes(dialect: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Raise the standard "this database has no partial indexes" error.
|
|
35
|
+
*
|
|
36
|
+
* Exported so a dialect can fail the same way from its own `createPartialIndex`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function unsupportedPartialIndex(dialect: string): UnsupportedSchemaObjectError;
|
|
39
|
+
/**
|
|
40
|
+
* Compile one `IndexOptions.where` into a self-contained SQL predicate with all
|
|
41
|
+
* values inlined as escaped literals.
|
|
42
|
+
*
|
|
43
|
+
* A string `where` is a predicate the caller already wrote by hand and is
|
|
44
|
+
* returned unchanged, which is also what `createPartialIndex()` takes.
|
|
45
|
+
*
|
|
46
|
+
* @param where - Predicate object, or raw SQL predicate string
|
|
47
|
+
* @param dialect - Dialect name, used for the unsupported-database error
|
|
48
|
+
* @param escaper - The dialect itself; supplies `escape` / `escapeId`
|
|
49
|
+
* @returns The predicate SQL, or `''` when `where` is empty (emit no WHERE)
|
|
50
|
+
* @throws {UnsupportedSchemaObjectError} when the dialect has no partial indexes
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildPartialIndexPredicate(where: unknown, dialect: string, escaper: PartialIndexEscaper): string;
|