ts-prorm-orm 1.2.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +331 -1
- package/README.md +32 -24
- package/dist/compliance/data-classifier.d.ts +6 -0
- package/dist/compliance/data-classifier.js +6 -0
- package/dist/compliance/data-lineage.d.ts +4 -0
- package/dist/compliance/data-lineage.js +4 -0
- package/dist/compliance/data-masker.d.ts +4 -0
- package/dist/compliance/data-masker.js +4 -0
- package/dist/compliance/index.d.ts +4 -0
- package/dist/compliance/index.js +15 -1
- package/dist/compliance/pseudonymization.d.ts +4 -0
- package/dist/compliance/pseudonymization.js +4 -0
- package/dist/compliance/query-firewall.d.ts +9 -9
- package/dist/compliance/query-firewall.js +9 -9
- package/dist/compliance/rate-limiter.d.ts +4 -0
- package/dist/compliance/rate-limiter.js +4 -0
- package/dist/compliance/row-level-security.d.ts +2 -0
- package/dist/compliance/row-level-security.js +2 -0
- package/dist/compliance/security-decorator.d.ts +8 -0
- package/dist/compliance/security-decorator.js +8 -0
- package/dist/compliance/session-isolation.d.ts +4 -0
- package/dist/compliance/session-isolation.js +4 -0
- package/dist/compliance/worm-storage.d.ts +4 -0
- package/dist/compliance/worm-storage.js +4 -0
- 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/audit.d.ts +3 -1
- package/dist/decorators/audit.js +3 -1
- package/dist/decorators/check.d.ts +15 -15
- package/dist/decorators/check.js +15 -15
- package/dist/decorators/collate.d.ts +16 -0
- package/dist/decorators/collate.js +16 -0
- package/dist/decorators/comment.d.ts +3 -1
- package/dist/decorators/comment.js +3 -1
- package/dist/decorators/database-settings.d.ts +6 -0
- package/dist/decorators/database-settings.js +6 -0
- package/dist/decorators/default.d.ts +2 -0
- package/dist/decorators/default.js +2 -0
- package/dist/decorators/encryption.d.ts +7 -1
- package/dist/decorators/encryption.js +7 -1
- package/dist/decorators/engine.d.ts +4 -0
- package/dist/decorators/engine.js +4 -0
- package/dist/decorators/fk-constraints.d.ts +32 -32
- package/dist/decorators/fk-constraints.js +32 -32
- package/dist/decorators/foreign-table.d.ts +1 -1
- package/dist/decorators/generated.d.ts +8 -0
- package/dist/decorators/generated.js +8 -0
- package/dist/decorators/hstore.d.ts +6 -0
- package/dist/decorators/hstore.js +6 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.js +11 -1
- package/dist/decorators/json-column.d.ts +2 -0
- package/dist/decorators/json-column.js +2 -0
- package/dist/decorators/jsonb.d.ts +6 -0
- package/dist/decorators/jsonb.js +6 -0
- package/dist/decorators/permissions.d.ts +12 -0
- package/dist/decorators/permissions.js +12 -0
- package/dist/decorators/procedure.d.ts +4 -2
- package/dist/decorators/procedure.js +4 -2
- package/dist/decorators/query-options.d.ts +24 -0
- package/dist/decorators/query-options.js +24 -0
- package/dist/decorators/range.d.ts +8 -0
- package/dist/decorators/range.js +8 -0
- package/dist/decorators/set-column.d.ts +2 -0
- package/dist/decorators/set-column.js +2 -0
- package/dist/decorators/spatial.d.ts +2 -0
- package/dist/decorators/spatial.js +2 -0
- package/dist/decorators/storage.d.ts +24 -0
- package/dist/decorators/storage.js +24 -0
- package/dist/decorators/timezone.d.ts +7 -7
- package/dist/decorators/timezone.js +7 -7
- package/dist/decorators/trigger.d.ts +3 -1
- package/dist/decorators/trigger.js +3 -1
- package/dist/decorators/uuid.d.ts +6 -0
- package/dist/decorators/uuid.js +6 -0
- package/dist/decorators/view.d.ts +7 -1
- package/dist/decorators/view.js +7 -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 +141 -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/external-fields/decorator.d.ts +2 -0
- package/dist/external-fields/decorator.js +2 -0
- 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 +12 -3
- package/dist/index.js +53 -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/associations.d.ts +38 -0
- package/dist/models/associations.js +16 -0
- package/dist/models/decorators.d.ts +55 -19
- package/dist/models/decorators.js +95 -19
- 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 +1132 -553
- 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/schema-objects.d.ts +42 -0
- package/dist/schema-objects.js +28 -0
- package/dist/sql-constants.d.ts +44 -0
- package/dist/sql-constants.js +138 -1
- package/dist/types/index.d.ts +387 -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 +39 -8
- 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
|
@@ -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;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Partial-index predicates (`IndexOptions.where`).
|
|
4
|
+
*
|
|
5
|
+
* A partial index predicate lives inside DDL, and DDL is executed without a
|
|
6
|
+
* parameter list. Compiling `where` through the ordinary WHERE builder
|
|
7
|
+
* therefore produced `CREATE INDEX ... WHERE "status" = ?` with the values
|
|
8
|
+
* silently dropped on the floor - SQLite rejects that outright ("parameters
|
|
9
|
+
* prohibited in partial index WHERE clauses") and PostgreSQL fails on the
|
|
10
|
+
* dangling `$1`. So predicates are compiled here instead, with every value
|
|
11
|
+
* inlined as a literal through the dialect's own `escape()`.
|
|
12
|
+
*
|
|
13
|
+
* That inlining makes `escape()` the security boundary for this path: nothing
|
|
14
|
+
* in this module hand-rolls quoting, and no user value ever reaches the SQL by
|
|
15
|
+
* concatenation. `escapeId()` does the same job for column names.
|
|
16
|
+
*
|
|
17
|
+
* Only the operator forms that mean something in a predicate are supported.
|
|
18
|
+
* Anything else throws with a reason rather than emitting SQL the server will
|
|
19
|
+
* reject - the same convention as {@link UnsupportedSchemaObjectError} in
|
|
20
|
+
* `src/schema-objects.ts`, which this module reuses for dialects that have no
|
|
21
|
+
* partial indexes at all.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.supportsPartialIndexes = supportsPartialIndexes;
|
|
25
|
+
exports.unsupportedPartialIndex = unsupportedPartialIndex;
|
|
26
|
+
exports.buildPartialIndexPredicate = buildPartialIndexPredicate;
|
|
27
|
+
const schema_objects_1 = require("../schema-objects");
|
|
28
|
+
const operators_1 = require("../operators");
|
|
29
|
+
/**
|
|
30
|
+
* Databases whose `CREATE INDEX` accepts a predicate.
|
|
31
|
+
*
|
|
32
|
+
* PostgreSQL calls them partial indexes, SQL Server calls them filtered
|
|
33
|
+
* indexes, SQLite copied PostgreSQL's syntax. Derived dialects report their own
|
|
34
|
+
* `name` (TimescaleDBDialect extends PostgresDialect but calls itself
|
|
35
|
+
* 'timescaledb'), so membership is listed explicitly rather than inferred from
|
|
36
|
+
* the class hierarchy - the same reasoning as the family sets in
|
|
37
|
+
* `src/schema-objects.ts`.
|
|
38
|
+
*
|
|
39
|
+
* Deliberately absent: MySQL/MariaDB/TiDB/SingleStore (no predicate on CREATE
|
|
40
|
+
* INDEX at all), Oracle (function-based index on a CASE expression is the
|
|
41
|
+
* workaround, not a predicate), DuckDB, ClickHouse, Redshift, Snowflake, HANA,
|
|
42
|
+
* DB2, Spanner, Vertica, Firebird, Exasol, CrateDB, QuestDB, Databricks and
|
|
43
|
+
* Trino.
|
|
44
|
+
*/
|
|
45
|
+
const PARTIAL_INDEX_DIALECTS = new Set([
|
|
46
|
+
// PostgreSQL family
|
|
47
|
+
'postgres',
|
|
48
|
+
'postgresql',
|
|
49
|
+
'cockroachdb',
|
|
50
|
+
'greenplum',
|
|
51
|
+
'timescaledb',
|
|
52
|
+
'yugabytedb',
|
|
53
|
+
// SQLite family (Turso/libSQL is SQLite over the wire)
|
|
54
|
+
'sqlite',
|
|
55
|
+
'turso',
|
|
56
|
+
// SQL Server filtered indexes
|
|
57
|
+
'mssql',
|
|
58
|
+
]);
|
|
59
|
+
/** Whether `dialect` supports a `WHERE` predicate on `CREATE INDEX`. */
|
|
60
|
+
function supportsPartialIndexes(dialect) {
|
|
61
|
+
return PARTIAL_INDEX_DIALECTS.has(String(dialect).toLowerCase());
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Raise the standard "this database has no partial indexes" error.
|
|
65
|
+
*
|
|
66
|
+
* Exported so a dialect can fail the same way from its own `createPartialIndex`.
|
|
67
|
+
*/
|
|
68
|
+
function unsupportedPartialIndex(dialect) {
|
|
69
|
+
return new schema_objects_1.UnsupportedSchemaObjectError('Partial (filtered) index', dialect, `${dialect} has no WHERE clause on CREATE INDEX - index the whole table, or index a generated/expression column that encodes the predicate.`);
|
|
70
|
+
}
|
|
71
|
+
function unsupportedOperator(op) {
|
|
72
|
+
return new Error(`Partial index predicate does not support the "${op}" operator. ` +
|
|
73
|
+
'Supported forms: { col: value }, { col: [..] }, Op.eq, Op.ne, Op.gt, Op.gte, Op.lt, Op.lte, ' +
|
|
74
|
+
'Op.in, Op.notIn, Op.between, Op.notBetween, Op.like, Op.notLike, Op.is, Op.not, Op.isNull, ' +
|
|
75
|
+
'Op.isNotNull, Op.col, Op.and, Op.or. ' +
|
|
76
|
+
'For anything else, pass the predicate as a literal SQL string (`where: "..."`) or use ' +
|
|
77
|
+
'createPartialIndex(table, name, fields, predicate).');
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Copy an object's Symbol operator keys onto their `$`-prefixed string
|
|
81
|
+
* equivalents, so `{ [Op.gt]: 5 }` and `{ $gt: 5 }` compile down one path.
|
|
82
|
+
*/
|
|
83
|
+
function normalizeOperatorKeys(input) {
|
|
84
|
+
const raw = input;
|
|
85
|
+
const normalized = { ...raw };
|
|
86
|
+
for (const sym of Object.getOwnPropertySymbols(raw)) {
|
|
87
|
+
const key = (0, operators_1.operatorToWhereKey)(sym);
|
|
88
|
+
if (key)
|
|
89
|
+
normalized[key] = raw[sym];
|
|
90
|
+
}
|
|
91
|
+
return normalized;
|
|
92
|
+
}
|
|
93
|
+
function isPlainValueObject(value) {
|
|
94
|
+
return (typeof value === 'object' &&
|
|
95
|
+
value !== null &&
|
|
96
|
+
!Array.isArray(value) &&
|
|
97
|
+
!(value instanceof Date) &&
|
|
98
|
+
!Buffer.isBuffer(value));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Compile one `IndexOptions.where` into a self-contained SQL predicate with all
|
|
102
|
+
* values inlined as escaped literals.
|
|
103
|
+
*
|
|
104
|
+
* A string `where` is a predicate the caller already wrote by hand and is
|
|
105
|
+
* returned unchanged, which is also what `createPartialIndex()` takes.
|
|
106
|
+
*
|
|
107
|
+
* @param where - Predicate object, or raw SQL predicate string
|
|
108
|
+
* @param dialect - Dialect name, used for the unsupported-database error
|
|
109
|
+
* @param escaper - The dialect itself; supplies `escape` / `escapeId`
|
|
110
|
+
* @returns The predicate SQL, or `''` when `where` is empty (emit no WHERE)
|
|
111
|
+
* @throws {UnsupportedSchemaObjectError} when the dialect has no partial indexes
|
|
112
|
+
*/
|
|
113
|
+
function buildPartialIndexPredicate(where, dialect, escaper) {
|
|
114
|
+
if (where === undefined || where === null) {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
if (!supportsPartialIndexes(dialect)) {
|
|
118
|
+
throw unsupportedPartialIndex(dialect);
|
|
119
|
+
}
|
|
120
|
+
// A hand-written predicate passes through untouched.
|
|
121
|
+
if (typeof where === 'string') {
|
|
122
|
+
return where.trim();
|
|
123
|
+
}
|
|
124
|
+
const escapeValue = (value) => {
|
|
125
|
+
if (value === undefined) {
|
|
126
|
+
throw new Error('Partial index predicate got `undefined` as a value. A predicate is baked into the ' +
|
|
127
|
+
'index definition, so every value must be known up front - use Op.isNull for "no value".');
|
|
128
|
+
}
|
|
129
|
+
return escaper.escape(value);
|
|
130
|
+
};
|
|
131
|
+
const column = (name) => escaper.escapeId(name);
|
|
132
|
+
const compileField = (field, value) => {
|
|
133
|
+
if (value === null) {
|
|
134
|
+
return `${column(field)} IS NULL`;
|
|
135
|
+
}
|
|
136
|
+
// `{ id: [1, 2, 3] }` is the shorthand for IN, matching the WHERE builders.
|
|
137
|
+
if (Array.isArray(value)) {
|
|
138
|
+
if (value.length === 0) {
|
|
139
|
+
// An empty IN list is a syntax error everywhere; say so rather than emit it.
|
|
140
|
+
throw new Error(`Partial index predicate for "${field}" got an empty IN list, which is not valid SQL.`);
|
|
141
|
+
}
|
|
142
|
+
return `${column(field)} IN (${value.map(escapeValue).join(', ')})`;
|
|
143
|
+
}
|
|
144
|
+
if (!isPlainValueObject(value)) {
|
|
145
|
+
return `${column(field)} = ${escapeValue(value)}`;
|
|
146
|
+
}
|
|
147
|
+
const ops = normalizeOperatorKeys(value);
|
|
148
|
+
const opKeys = Object.keys(ops).filter((k) => k.startsWith('$'));
|
|
149
|
+
// A field object with no operator keys is a plain value (e.g. a Literal, or
|
|
150
|
+
// a JSON payload) - escape it whole.
|
|
151
|
+
if (opKeys.length === 0) {
|
|
152
|
+
return `${column(field)} = ${escapeValue(value)}`;
|
|
153
|
+
}
|
|
154
|
+
// Several operators on one field (e.g. { age: { $gte: 18, $lt: 65 } }) AND together.
|
|
155
|
+
const parts = opKeys.map((op) => compileOperator(field, op, ops[op]));
|
|
156
|
+
return parts.length > 1 ? `(${parts.join(' AND ')})` : parts[0];
|
|
157
|
+
};
|
|
158
|
+
const compileOperator = (field, op, operand) => {
|
|
159
|
+
const col = column(field);
|
|
160
|
+
switch (op) {
|
|
161
|
+
case '$eq':
|
|
162
|
+
return operand === null ? `${col} IS NULL` : `${col} = ${escapeValue(operand)}`;
|
|
163
|
+
case '$ne':
|
|
164
|
+
return operand === null ? `${col} IS NOT NULL` : `${col} != ${escapeValue(operand)}`;
|
|
165
|
+
case '$is':
|
|
166
|
+
return operand === null ? `${col} IS NULL` : `${col} IS ${escapeValue(operand)}`;
|
|
167
|
+
case '$not':
|
|
168
|
+
return operand === null ? `${col} IS NOT NULL` : `${col} != ${escapeValue(operand)}`;
|
|
169
|
+
case '$gt':
|
|
170
|
+
return `${col} > ${escapeValue(operand)}`;
|
|
171
|
+
case '$gte':
|
|
172
|
+
return `${col} >= ${escapeValue(operand)}`;
|
|
173
|
+
case '$lt':
|
|
174
|
+
return `${col} < ${escapeValue(operand)}`;
|
|
175
|
+
case '$lte':
|
|
176
|
+
return `${col} <= ${escapeValue(operand)}`;
|
|
177
|
+
case '$like':
|
|
178
|
+
return `${col} LIKE ${escapeValue(operand)}`;
|
|
179
|
+
case '$notLike':
|
|
180
|
+
return `${col} NOT LIKE ${escapeValue(operand)}`;
|
|
181
|
+
case '$isNull':
|
|
182
|
+
return operand ? `${col} IS NULL` : `${col} IS NOT NULL`;
|
|
183
|
+
case '$isNotNull':
|
|
184
|
+
return operand ? `${col} IS NOT NULL` : `${col} IS NULL`;
|
|
185
|
+
case '$col':
|
|
186
|
+
return `${col} = ${column(String(operand))}`;
|
|
187
|
+
case '$in':
|
|
188
|
+
case '$notIn': {
|
|
189
|
+
const list = operand;
|
|
190
|
+
if (!Array.isArray(list) || list.length === 0) {
|
|
191
|
+
throw new Error(`Partial index predicate for "${field}" got an empty ${op} list, which is not valid SQL.`);
|
|
192
|
+
}
|
|
193
|
+
const keyword = op === '$in' ? 'IN' : 'NOT IN';
|
|
194
|
+
return `${col} ${keyword} (${list.map(escapeValue).join(', ')})`;
|
|
195
|
+
}
|
|
196
|
+
case '$between':
|
|
197
|
+
case '$notBetween': {
|
|
198
|
+
const range = operand;
|
|
199
|
+
if (!Array.isArray(range) || range.length !== 2) {
|
|
200
|
+
throw new Error(`Partial index predicate for "${field}": ${op} needs a [min, max] pair.`);
|
|
201
|
+
}
|
|
202
|
+
const keyword = op === '$between' ? 'BETWEEN' : 'NOT BETWEEN';
|
|
203
|
+
return `${col} ${keyword} ${escapeValue(range[0])} AND ${escapeValue(range[1])}`;
|
|
204
|
+
}
|
|
205
|
+
default:
|
|
206
|
+
throw unsupportedOperator(op);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
const compile = (condition) => {
|
|
210
|
+
if (condition === undefined || condition === null) {
|
|
211
|
+
return '';
|
|
212
|
+
}
|
|
213
|
+
// A raw predicate fragment nested inside $and/$or.
|
|
214
|
+
if (typeof condition === 'string') {
|
|
215
|
+
return condition.trim();
|
|
216
|
+
}
|
|
217
|
+
if (!isPlainValueObject(condition)) {
|
|
218
|
+
throw new Error(`Partial index predicate expected an object or a SQL string, got ${typeof condition}.`);
|
|
219
|
+
}
|
|
220
|
+
const cond = normalizeOperatorKeys(condition);
|
|
221
|
+
// Logical groups lead, then the sibling field conditions - the same order
|
|
222
|
+
// the dialects' own WHERE builders produce, so the two read alike.
|
|
223
|
+
const logical = [];
|
|
224
|
+
const fields = [];
|
|
225
|
+
for (const [key, value] of Object.entries(cond)) {
|
|
226
|
+
if (key === '$and' || key === '$or') {
|
|
227
|
+
const branches = (Array.isArray(value) ? value : [value])
|
|
228
|
+
.map(compile)
|
|
229
|
+
.filter((s) => s.length > 0);
|
|
230
|
+
if (branches.length === 0)
|
|
231
|
+
continue;
|
|
232
|
+
const joined = branches.join(key === '$and' ? ' AND ' : ' OR ');
|
|
233
|
+
logical.push(branches.length > 1 ? `(${joined})` : joined);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (key === '$not') {
|
|
237
|
+
const inner = compile(value);
|
|
238
|
+
if (inner)
|
|
239
|
+
logical.push(`NOT (${inner})`);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (key.startsWith('$')) {
|
|
243
|
+
// A bare operator at condition level has no column to apply to.
|
|
244
|
+
throw unsupportedOperator(key);
|
|
245
|
+
}
|
|
246
|
+
fields.push(compileField(key, value));
|
|
247
|
+
}
|
|
248
|
+
return [...logical, ...fields].join(' AND ');
|
|
249
|
+
};
|
|
250
|
+
return compile(where);
|
|
251
|
+
}
|
|
@@ -7,7 +7,10 @@ exports.PostgresTransaction = exports.PostgresDialect = void 0;
|
|
|
7
7
|
exports.createPostgresDialect = createPostgresDialect;
|
|
8
8
|
const operators_1 = require("../../operators");
|
|
9
9
|
const operators_2 = require("../../operators");
|
|
10
|
+
const lock_clause_helper_1 = require("../lock-clause-helper");
|
|
11
|
+
const partial_index_1 = require("../partial-index");
|
|
10
12
|
const prorm_1 = require("../../prorm");
|
|
13
|
+
const order_expression_helper_1 = require("../order-expression-helper");
|
|
11
14
|
// Lazily-loaded `pg` driver. Required only when a connection is opened, so
|
|
12
15
|
// importing this ORM never loads the pg driver.
|
|
13
16
|
let pgModule = null;
|
|
@@ -814,13 +817,18 @@ class PostgresDialect {
|
|
|
814
817
|
* Build constraint SQL for table creation
|
|
815
818
|
*/
|
|
816
819
|
buildConstraintSql(constraint) {
|
|
817
|
-
|
|
820
|
+
// A named table constraint is `CONSTRAINT "name" UNIQUE (...)`. The
|
|
821
|
+
// CONSTRAINT keyword was missing here, so every named constraint came out
|
|
822
|
+
// as `"name" UNIQUE (...)`, which PostgreSQL reads as a column definition
|
|
823
|
+
// and rejects with a syntax error - the constraint never reached the
|
|
824
|
+
// database. Every other dialect in this repo emits the keyword.
|
|
825
|
+
const name = constraint.name ? `CONSTRAINT "${constraint.name}" ` : '';
|
|
818
826
|
const fields = constraint.fields?.map((f) => this.escapeId(f)).join(', ') || '';
|
|
819
827
|
switch (constraint.type) {
|
|
820
828
|
case 'PRIMARY KEY':
|
|
821
|
-
return `${name
|
|
829
|
+
return `${name}PRIMARY KEY (${fields})`;
|
|
822
830
|
case 'UNIQUE':
|
|
823
|
-
return `${name
|
|
831
|
+
return `${name}UNIQUE (${fields})`;
|
|
824
832
|
case 'FOREIGN KEY':
|
|
825
833
|
if (!constraint.references)
|
|
826
834
|
return null;
|
|
@@ -829,7 +837,7 @@ class PostgresDialect {
|
|
|
829
837
|
const refFieldSql = Array.isArray(refField)
|
|
830
838
|
? `(${refField.map((f) => this.escapeId(f)).join(', ')})`
|
|
831
839
|
: `(${this.escapeId(refField)})`;
|
|
832
|
-
let fkSql = `${name
|
|
840
|
+
let fkSql = `${name}FOREIGN KEY (${fields}) `;
|
|
833
841
|
fkSql += `REFERENCES ${this.escapeId(constraint.references.table)}${refFieldSql}`;
|
|
834
842
|
if (constraint.references.onDelete) {
|
|
835
843
|
fkSql += ` ON DELETE ${constraint.references.onDelete}`;
|
|
@@ -841,7 +849,7 @@ class PostgresDialect {
|
|
|
841
849
|
case 'CHECK':
|
|
842
850
|
if (!constraint.check)
|
|
843
851
|
return null;
|
|
844
|
-
return `${name
|
|
852
|
+
return `${name}CHECK (${constraint.check})`;
|
|
845
853
|
default:
|
|
846
854
|
return null;
|
|
847
855
|
}
|
|
@@ -2615,9 +2623,14 @@ class PostgresDialect {
|
|
|
2615
2623
|
if (options?.include && options.include.length > 0) {
|
|
2616
2624
|
sql += ` INCLUDE (${options.include.map((f) => this.escapeId(f)).join(', ')})`;
|
|
2617
2625
|
}
|
|
2626
|
+
// Partial index predicate. The ordinary WHERE builder emits `$1`-style
|
|
2627
|
+
// placeholders and this DDL is executed without a value list, so the
|
|
2628
|
+
// predicate is compiled with its values inlined as escaped literals.
|
|
2618
2629
|
if (options?.where) {
|
|
2619
|
-
const
|
|
2620
|
-
|
|
2630
|
+
const predicate = (0, partial_index_1.buildPartialIndexPredicate)(options.where, this.name, this);
|
|
2631
|
+
if (predicate) {
|
|
2632
|
+
sql += ` WHERE ${predicate}`;
|
|
2633
|
+
}
|
|
2621
2634
|
}
|
|
2622
2635
|
await this.query(sql);
|
|
2623
2636
|
}
|
|
@@ -2658,9 +2671,12 @@ class PostgresDialect {
|
|
|
2658
2671
|
if (indexDef.include && indexDef.include.length > 0) {
|
|
2659
2672
|
sql += ` INCLUDE (${indexDef.include.map((f) => this.escapeId(f)).join(', ')})`;
|
|
2660
2673
|
}
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2674
|
+
// Partial index predicate - inlined literals, not placeholders. See addIndex.
|
|
2675
|
+
if (indexDef.where) {
|
|
2676
|
+
const predicate = (0, partial_index_1.buildPartialIndexPredicate)(indexDef.where, this.name, this);
|
|
2677
|
+
if (predicate) {
|
|
2678
|
+
sql += ` WHERE ${predicate}`;
|
|
2679
|
+
}
|
|
2664
2680
|
}
|
|
2665
2681
|
await this.query(sql);
|
|
2666
2682
|
}
|
|
@@ -3505,12 +3521,18 @@ class PostgresDialect {
|
|
|
3505
3521
|
* Build an ORDER BY clause
|
|
3506
3522
|
*/
|
|
3507
3523
|
buildOrderClause(order, options) {
|
|
3508
|
-
const orderArray = order;
|
|
3509
|
-
if (
|
|
3524
|
+
const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
|
|
3525
|
+
if (orderArray.length === 0) {
|
|
3510
3526
|
return '';
|
|
3511
3527
|
}
|
|
3512
3528
|
const orderParts = [];
|
|
3513
3529
|
for (const item of orderArray) {
|
|
3530
|
+
// Order helpers: asc('name') / desc('name') / random()
|
|
3531
|
+
const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
|
|
3532
|
+
if (orderExpr !== null) {
|
|
3533
|
+
orderParts.push(orderExpr);
|
|
3534
|
+
continue;
|
|
3535
|
+
}
|
|
3514
3536
|
if (Array.isArray(item)) {
|
|
3515
3537
|
const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
|
|
3516
3538
|
const direction = item[1] ? ` ${item[1]}` : '';
|
|
@@ -4326,35 +4348,11 @@ class PostgresDialect {
|
|
|
4326
4348
|
}
|
|
4327
4349
|
// Build LIMIT/OFFSET clause
|
|
4328
4350
|
sql += this.buildLimitOffset(options.limit, options.offset);
|
|
4329
|
-
// Handle row-level locking (
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
lockType = 'UPDATE';
|
|
4335
|
-
}
|
|
4336
|
-
else if (typeof options.lock === 'string') {
|
|
4337
|
-
lockType = options.lock;
|
|
4338
|
-
}
|
|
4339
|
-
else if (typeof options.lock === 'object' && options.lock.of) {
|
|
4340
|
-
// lock: { of: Model } - defaults to FOR UPDATE for table-specific locking
|
|
4341
|
-
lockType = 'UPDATE';
|
|
4342
|
-
}
|
|
4343
|
-
if (lockType === 'UPDATE') {
|
|
4344
|
-
sql += ' FOR UPDATE';
|
|
4345
|
-
}
|
|
4346
|
-
else if (lockType === 'SHARE') {
|
|
4347
|
-
sql += ' FOR SHARE';
|
|
4348
|
-
}
|
|
4349
|
-
else if (lockType === 'KEY SHARE') {
|
|
4350
|
-
sql += ' FOR KEY SHARE';
|
|
4351
|
-
}
|
|
4352
|
-
// Handle lock on specific table (PostgreSQL supports OF clause)
|
|
4353
|
-
if (typeof options.lock === 'object' && options.lock.of) {
|
|
4354
|
-
const model = options.lock.of;
|
|
4355
|
-
sql += ` OF ${this.escapeId(model.tableName || '')}`;
|
|
4356
|
-
}
|
|
4357
|
-
}
|
|
4351
|
+
// Handle row-level locking. Every documented `lock` shape (true, a level
|
|
4352
|
+
// string, `{ of }`, and the `{ level, nowait, skipLocked }` object form) is
|
|
4353
|
+
// parsed by the shared helper so none of them can silently compile to no
|
|
4354
|
+
// lock at all; see src/dialects/lock-clause-helper.ts.
|
|
4355
|
+
sql += (0, lock_clause_helper_1.appendLockClause)(options.lock, lock_clause_helper_1.POSTGRES_LOCK_SPEC, (id) => this.escapeId(id));
|
|
4358
4356
|
// Build UNION clause if provided
|
|
4359
4357
|
if (options.union && options.union.length > 0) {
|
|
4360
4358
|
const unionType = options.unionType || 'UNION';
|
|
@@ -4515,33 +4513,10 @@ class PostgresDialect {
|
|
|
4515
4513
|
* Get PostgreSQL specific lock options
|
|
4516
4514
|
*/
|
|
4517
4515
|
getLockOptions(lock) {
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
case 'UPDATE':
|
|
4523
|
-
sql = 'FOR UPDATE';
|
|
4524
|
-
break;
|
|
4525
|
-
case 'SHARE':
|
|
4526
|
-
sql = 'FOR SHARE';
|
|
4527
|
-
break;
|
|
4528
|
-
case 'KEY SHARE':
|
|
4529
|
-
sql = 'FOR KEY SHARE';
|
|
4530
|
-
break;
|
|
4531
|
-
default:
|
|
4532
|
-
return { sql: '', params };
|
|
4533
|
-
}
|
|
4534
|
-
if (typeof lock === 'object' && lock?.of) {
|
|
4535
|
-
const model = lock.of;
|
|
4536
|
-
sql += ` OF ${this.escapeId(model.tableName || '')}`;
|
|
4537
|
-
}
|
|
4538
|
-
if (typeof lock === 'object' && lock?.nowait) {
|
|
4539
|
-
sql += ' NOWAIT';
|
|
4540
|
-
}
|
|
4541
|
-
else if (typeof lock === 'object' && lock?.skipLocked) {
|
|
4542
|
-
sql += ' SKIP LOCKED';
|
|
4543
|
-
}
|
|
4544
|
-
return { sql, params };
|
|
4516
|
+
return {
|
|
4517
|
+
sql: (0, lock_clause_helper_1.buildLockClause)(lock, lock_clause_helper_1.POSTGRES_LOCK_SPEC, (id) => this.escapeId(id)),
|
|
4518
|
+
params: [],
|
|
4519
|
+
};
|
|
4545
4520
|
}
|
|
4546
4521
|
// ---------------------------------------------------------------------------
|
|
4547
4522
|
// Advisory locks — PostgreSQL dialect
|