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.
Files changed (129) hide show
  1. package/CHANGELOG.md +209 -0
  2. package/README.md +32 -24
  3. package/dist/compliance/index.d.ts +2 -0
  4. package/dist/compliance/index.js +13 -1
  5. package/dist/connection-manager.js +13 -1
  6. package/dist/core/expressions.d.ts +58 -0
  7. package/dist/core/expressions.js +83 -0
  8. package/dist/core/hooks-manager.d.ts +179 -0
  9. package/dist/core/hooks-manager.js +291 -0
  10. package/dist/core/types.d.ts +235 -0
  11. package/dist/core/types.js +9 -0
  12. package/dist/decorators/index.d.ts +2 -0
  13. package/dist/decorators/index.js +11 -1
  14. package/dist/diagrams/chen-diagram.d.ts +15 -12
  15. package/dist/diagrams/chen-diagram.js +76 -85
  16. package/dist/diagrams/class-diagram.d.ts +9 -8
  17. package/dist/diagrams/class-diagram.js +60 -82
  18. package/dist/diagrams/core/index.d.ts +13 -0
  19. package/dist/diagrams/core/index.js +33 -0
  20. package/dist/diagrams/core/layout.d.ts +104 -0
  21. package/dist/diagrams/core/layout.js +223 -0
  22. package/dist/diagrams/{svg-dom.d.ts → core/svg.d.ts} +8 -20
  23. package/dist/diagrams/{svg-dom.js → core/svg.js} +21 -29
  24. package/dist/diagrams/core/theme.d.ts +84 -0
  25. package/dist/diagrams/core/theme.js +62 -0
  26. package/dist/diagrams/dependency-diagram.d.ts +15 -14
  27. package/dist/diagrams/dependency-diagram.js +71 -105
  28. package/dist/diagrams/er-diagram.d.ts +13 -26
  29. package/dist/diagrams/er-diagram.js +180 -259
  30. package/dist/diagrams/flow-diagram.d.ts +16 -11
  31. package/dist/diagrams/flow-diagram.js +96 -91
  32. package/dist/diagrams/gantt-diagram.d.ts +10 -5
  33. package/dist/diagrams/gantt-diagram.js +77 -103
  34. package/dist/diagrams/index-diagram.d.ts +10 -10
  35. package/dist/diagrams/index-diagram.js +106 -116
  36. package/dist/diagrams/index.d.ts +6 -9
  37. package/dist/diagrams/index.js +9 -24
  38. package/dist/diagrams/migration-diagram.d.ts +8 -4
  39. package/dist/diagrams/migration-diagram.js +189 -161
  40. package/dist/diagrams/model-diagram.d.ts +34 -34
  41. package/dist/diagrams/model-diagram.js +106 -485
  42. package/dist/diagrams/package-diagram.d.ts +9 -6
  43. package/dist/diagrams/package-diagram.js +64 -101
  44. package/dist/diagrams/relational-diagram.d.ts +12 -10
  45. package/dist/diagrams/relational-diagram.js +87 -114
  46. package/dist/diagrams/schemadoc-diagram.d.ts +8 -8
  47. package/dist/diagrams/schemadoc-diagram.js +71 -93
  48. package/dist/diagrams/sequence-diagram.d.ts +10 -10
  49. package/dist/diagrams/sequence-diagram.js +64 -89
  50. package/dist/diagrams/state-diagram.d.ts +10 -9
  51. package/dist/diagrams/state-diagram.js +69 -92
  52. package/dist/diagrams/tree-diagram.d.ts +11 -5
  53. package/dist/diagrams/tree-diagram.js +41 -65
  54. package/dist/dialects/clickhouse/index.js +15 -0
  55. package/dist/dialects/cockroachdb/index.d.ts +1 -1
  56. package/dist/dialects/cockroachdb/index.js +32 -63
  57. package/dist/dialects/db2/index.js +23 -8
  58. package/dist/dialects/dialect.d.ts +29 -5
  59. package/dist/dialects/duckdb/index.js +9 -2
  60. package/dist/dialects/hana/index.js +22 -5
  61. package/dist/dialects/lock-clause-helper.d.ts +167 -0
  62. package/dist/dialects/lock-clause-helper.js +316 -0
  63. package/dist/dialects/mariadb/index.d.ts +7 -4
  64. package/dist/dialects/mariadb/index.js +36 -60
  65. package/dist/dialects/mssql/index.js +34 -3
  66. package/dist/dialects/mysql/index.d.ts +1 -1
  67. package/dist/dialects/mysql/index.js +17 -45
  68. package/dist/dialects/oracle/index.d.ts +14 -3
  69. package/dist/dialects/oracle/index.js +66 -36
  70. package/dist/dialects/order-expression-helper.d.ts +72 -0
  71. package/dist/dialects/order-expression-helper.js +154 -0
  72. package/dist/dialects/partial-index.d.ts +52 -0
  73. package/dist/dialects/partial-index.js +251 -0
  74. package/dist/dialects/postgres/index.d.ts +1 -1
  75. package/dist/dialects/postgres/index.js +43 -68
  76. package/dist/dialects/redshift/index.js +9 -2
  77. package/dist/dialects/snowflake/index.js +17 -0
  78. package/dist/dialects/spanner/index.js +7 -2
  79. package/dist/dialects/sqlite/driver.d.ts +55 -0
  80. package/dist/dialects/sqlite/driver.js +102 -0
  81. package/dist/dialects/sqlite/index.d.ts +1 -1
  82. package/dist/dialects/sqlite/index.js +49 -27
  83. package/dist/graph/base-graph-dialect.d.ts +24 -3
  84. package/dist/graph/base-graph-dialect.js +43 -2
  85. package/dist/graph/dgraph/index.js +6 -0
  86. package/dist/graph/gremlin/index.d.ts +6 -0
  87. package/dist/graph/gremlin/index.js +12 -1
  88. package/dist/graph/index.d.ts +1 -1
  89. package/dist/graph/neo4j/index.d.ts +2 -0
  90. package/dist/graph/neo4j/index.js +6 -1
  91. package/dist/graph/types.d.ts +12 -1
  92. package/dist/index.d.ts +13 -4
  93. package/dist/index.js +54 -7
  94. package/dist/logging/index.d.ts +10 -0
  95. package/dist/logging/index.js +23 -0
  96. package/dist/logging/query-logging.d.ts +82 -0
  97. package/dist/logging/query-logging.js +102 -0
  98. package/dist/models/decorators.d.ts +39 -3
  99. package/dist/models/decorators.js +74 -0
  100. package/dist/models/eager-load.d.ts +99 -0
  101. package/dist/models/eager-load.js +348 -5
  102. package/dist/models/indexes.d.ts +3 -2
  103. package/dist/models/indexes.js +7 -1
  104. package/dist/models/model.js +69 -43
  105. package/dist/prorm.d.ts +175 -409
  106. package/dist/prorm.js +1142 -557
  107. package/dist/query-builders/order-limit-builder.js +12 -10
  108. package/dist/query-builders/sql-compiler.d.ts +10 -3
  109. package/dist/query-builders/sql-compiler.js +14 -43
  110. package/dist/query-interface.d.ts +2 -1
  111. package/dist/sql-constants.d.ts +44 -0
  112. package/dist/sql-constants.js +138 -1
  113. package/dist/types/index.d.ts +53 -17
  114. package/dist/types/index.js +10 -15
  115. package/dist/types/query-types.d.ts +26 -0
  116. package/dist/types/query-types.js +30 -0
  117. package/package.json +32 -5
  118. package/dist/decorators/belongs-to-many.js +0 -115
  119. package/dist/decorators/belongs-to.js +0 -115
  120. package/dist/decorators/has-many.js +0 -127
  121. package/dist/decorators/has-one.js +0 -116
  122. package/dist/diagrams/palette.d.ts +0 -138
  123. package/dist/diagrams/palette.js +0 -194
  124. package/dist/hooks/hooks-manager.d.ts +0 -189
  125. package/dist/hooks/hooks-manager.js +0 -350
  126. package/dist/hooks/index.d.ts +0 -8
  127. package/dist/hooks/index.js +0 -37
  128. /package/dist/{logging.d.ts → logging/logger.d.ts} +0 -0
  129. /package/dist/{logging.js → logging/logger.js} +0 -0
@@ -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
+ }
@@ -850,7 +850,7 @@ export declare class PostgresDialect implements Dialect {
850
850
  type?: string;
851
851
  using?: string;
852
852
  fields: string[];
853
- where?: WhereOptions;
853
+ where?: WhereOptions | string;
854
854
  expression?: string;
855
855
  include?: string[];
856
856
  }): Promise<void>;
@@ -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
- const name = constraint.name ? `"${constraint.name}"` : '';
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 ? name + ' ' : ''}PRIMARY KEY (${fields})`;
829
+ return `${name}PRIMARY KEY (${fields})`;
822
830
  case 'UNIQUE':
823
- return `${name ? name + ' ' : ''}UNIQUE (${fields})`;
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 ? name + ' ' : ''}FOREIGN KEY (${fields}) `;
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 ? name + ' ' : ''}CHECK (${constraint.check})`;
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 whereClause = this.buildWhereClause(options.where);
2620
- sql += ` WHERE ${whereClause.sql}`;
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
- if (indexDef.where && Object.keys(indexDef.where).length > 0) {
2662
- const whereClause = this.buildWhereClause(indexDef.where);
2663
- sql += ` WHERE ${whereClause.sql}`;
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 (!orderArray || !Array.isArray(orderArray) || orderArray.length === 0) {
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 (PostgreSQL specific)
4330
- if (options.lock) {
4331
- let lockType;
4332
- // lock: true is equivalent to 'UPDATE'
4333
- if (options.lock === true) {
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
- const params = [];
4519
- let sql = '';
4520
- const lockType = typeof lock === 'string' ? lock : lock?.level;
4521
- switch (lockType) {
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
@@ -74,6 +74,7 @@ const prorm_1 = require("../../prorm");
74
74
  const index_1 = require("../../errors/index");
75
75
  const index_2 = require("../../utils/index");
76
76
  const operators_1 = require("../../operators");
77
+ const order_expression_helper_1 = require("../order-expression-helper");
77
78
  const DEFAULT_RETRY_OPTIONS = {
78
79
  max: 3,
79
80
  timeout: 1000,
@@ -1564,11 +1565,17 @@ class RedshiftDialect {
1564
1565
  return { sql: buildCondition(where), values };
1565
1566
  }
1566
1567
  buildOrderClause(order, _options) {
1567
- const orderArray = order;
1568
- if (!orderArray || !Array.isArray(orderArray) || orderArray.length === 0)
1568
+ const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
1569
+ if (orderArray.length === 0)
1569
1570
  return '';
1570
1571
  const orderParts = [];
1571
1572
  for (const item of orderArray) {
1573
+ // Order helpers: asc('name') / desc('name') / random()
1574
+ const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
1575
+ if (orderExpr !== null) {
1576
+ orderParts.push(orderExpr);
1577
+ continue;
1578
+ }
1572
1579
  if (Array.isArray(item)) {
1573
1580
  const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
1574
1581
  const direction = item[1] ? ` ${item[1]}` : '';
@@ -46,6 +46,7 @@ function loadSnowflake() {
46
46
  const types_1 = require("../../types");
47
47
  const query_stream_helper_1 = require("../query-stream-helper");
48
48
  const prorm_1 = require("../../prorm");
49
+ const order_expression_helper_1 = require("../order-expression-helper");
49
50
  /**
50
51
  * Default retry options for connection/query errors
51
52
  */
@@ -1090,6 +1091,12 @@ class SnowflakeDialect {
1090
1091
  return '';
1091
1092
  const orderParts = [];
1092
1093
  const processOrder = (orderItem) => {
1094
+ // Order helpers: asc('name') / desc('name') / random()
1095
+ const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, orderItem);
1096
+ if (orderExpr !== null) {
1097
+ orderParts.push(orderExpr);
1098
+ return;
1099
+ }
1093
1100
  if (typeof orderItem === 'string') {
1094
1101
  const parts = orderItem.split(' ');
1095
1102
  if (parts.length === 1) {
@@ -1099,6 +1106,16 @@ class SnowflakeDialect {
1099
1106
  orderParts.push(`${this.escapeId(parts[0])} ${parts[1].toUpperCase()}`);
1100
1107
  }
1101
1108
  }
1109
+ else if (Array.isArray(orderItem)) {
1110
+ // Tuple form — `['name', 'DESC']` / `['name']`, the elements of the
1111
+ // primary documented `[['name', 'DESC'], ['id', 'ASC']]` shape. An
1112
+ // array is also an object, so without this branch each tuple fell
1113
+ // through to the map branch below and its *indices* were emitted as
1114
+ // column names (`ORDER BY "0" NAME, "1" DESC`).
1115
+ const [field, direction] = orderItem;
1116
+ const column = typeof field === 'string' ? this.escapeId(field) : String(field);
1117
+ orderParts.push(direction ? `${column} ${String(direction).toUpperCase()}` : column);
1118
+ }
1102
1119
  else if (typeof orderItem === 'object' && orderItem !== null) {
1103
1120
  for (const [key, value] of Object.entries(orderItem)) {
1104
1121
  if (key === '$raw') {
@@ -106,6 +106,7 @@ const { Spanner } = require('@google-cloud/spanner');
106
106
  const types_1 = require("../../types");
107
107
  const query_stream_helper_1 = require("../query-stream-helper");
108
108
  const prorm_1 = require("../../prorm");
109
+ const order_expression_helper_1 = require("../order-expression-helper");
109
110
  /**
110
111
  * A Spanner read-write transaction, bridging the driver's callback-based
111
112
  * `database.runTransaction()` API to the imperative
@@ -1490,10 +1491,14 @@ class SpannerDialect {
1490
1491
  return { sql, values };
1491
1492
  }
1492
1493
  buildOrderClause(order) {
1493
- const orderArray = order;
1494
- if (!orderArray || !Array.isArray(orderArray) || orderArray.length === 0)
1494
+ const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
1495
+ if (orderArray.length === 0)
1495
1496
  return '';
1496
1497
  const parts = orderArray.map((item) => {
1498
+ // Order helpers: asc('name') / desc('name') / random()
1499
+ const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
1500
+ if (orderExpr !== null)
1501
+ return orderExpr;
1497
1502
  if (Array.isArray(item)) {
1498
1503
  const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
1499
1504
  const direction = item[1] ? ` ${item[1]}` : '';
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Runtime-appropriate SQLite driver.
3
+ *
4
+ * Node uses `better-sqlite3`, a native addon. Bun ships its own SQLite as the
5
+ * built-in `bun:sqlite`, which is faster there and needs no native build step —
6
+ * and under Bun the `better-sqlite3` addon may not be installable at all.
7
+ *
8
+ * The two APIs were designed to look alike, so the dialect can talk to either
9
+ * one unchanged. They differ in exactly one place: better-sqlite3 has
10
+ * `db.pragma()`, and bun:sqlite does not. This module adds it, so the dialect
11
+ * has a single shape to code against and no `if (isBun)` branches leak into the
12
+ * SQL layer.
13
+ *
14
+ * The driver is loaded lazily on first connect, so importing the ORM never
15
+ * dlopens a native binding or touches a `bun:` module on Node.
16
+ */
17
+ /** The slice of better-sqlite3's Statement the dialect relies on. */
18
+ export interface SqliteStatement {
19
+ all(...params: unknown[]): unknown[];
20
+ get(...params: unknown[]): unknown;
21
+ run(...params: unknown[]): {
22
+ changes: number | bigint;
23
+ lastInsertRowid: number | bigint;
24
+ };
25
+ iterate(...params: unknown[]): IterableIterator<unknown>;
26
+ }
27
+ /** The slice of better-sqlite3's Database the dialect relies on. */
28
+ export interface SqliteDatabase {
29
+ prepare(sql: string): SqliteStatement;
30
+ exec(sql: string): unknown;
31
+ pragma(source: string, options?: {
32
+ simple?: boolean;
33
+ }): unknown;
34
+ close(): void;
35
+ }
36
+ export interface SqliteDriverOptions {
37
+ readonly?: boolean;
38
+ fileMustExist?: boolean;
39
+ timeout?: number;
40
+ verbose?: ((message?: unknown, ...args: unknown[]) => void) | undefined;
41
+ }
42
+ /** True when running under Bun. */
43
+ export declare function isBun(): boolean;
44
+ type DatabaseCtor = new (filename: string, options?: SqliteDriverOptions) => SqliteDatabase;
45
+ /**
46
+ * Resolve the SQLite driver for the current runtime.
47
+ *
48
+ * Under Bun this prefers `bun:sqlite` but falls back to `better-sqlite3` if it
49
+ * is installed and the built-in cannot be loaded, so an existing Bun project
50
+ * that already depends on better-sqlite3 keeps working.
51
+ */
52
+ export declare function loadSqliteDriver(): DatabaseCtor;
53
+ /** Test seam: forget the resolved driver. */
54
+ export declare function resetSqliteDriver(): void;
55
+ export {};