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