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.
Files changed (204) hide show
  1. package/CHANGELOG.md +331 -1
  2. package/README.md +32 -24
  3. package/dist/compliance/data-classifier.d.ts +6 -0
  4. package/dist/compliance/data-classifier.js +6 -0
  5. package/dist/compliance/data-lineage.d.ts +4 -0
  6. package/dist/compliance/data-lineage.js +4 -0
  7. package/dist/compliance/data-masker.d.ts +4 -0
  8. package/dist/compliance/data-masker.js +4 -0
  9. package/dist/compliance/index.d.ts +4 -0
  10. package/dist/compliance/index.js +15 -1
  11. package/dist/compliance/pseudonymization.d.ts +4 -0
  12. package/dist/compliance/pseudonymization.js +4 -0
  13. package/dist/compliance/query-firewall.d.ts +9 -9
  14. package/dist/compliance/query-firewall.js +9 -9
  15. package/dist/compliance/rate-limiter.d.ts +4 -0
  16. package/dist/compliance/rate-limiter.js +4 -0
  17. package/dist/compliance/row-level-security.d.ts +2 -0
  18. package/dist/compliance/row-level-security.js +2 -0
  19. package/dist/compliance/security-decorator.d.ts +8 -0
  20. package/dist/compliance/security-decorator.js +8 -0
  21. package/dist/compliance/session-isolation.d.ts +4 -0
  22. package/dist/compliance/session-isolation.js +4 -0
  23. package/dist/compliance/worm-storage.d.ts +4 -0
  24. package/dist/compliance/worm-storage.js +4 -0
  25. package/dist/connection-manager.js +13 -1
  26. package/dist/core/expressions.d.ts +58 -0
  27. package/dist/core/expressions.js +83 -0
  28. package/dist/core/hooks-manager.d.ts +179 -0
  29. package/dist/core/hooks-manager.js +291 -0
  30. package/dist/core/types.d.ts +235 -0
  31. package/dist/core/types.js +9 -0
  32. package/dist/decorators/audit.d.ts +3 -1
  33. package/dist/decorators/audit.js +3 -1
  34. package/dist/decorators/check.d.ts +15 -15
  35. package/dist/decorators/check.js +15 -15
  36. package/dist/decorators/collate.d.ts +16 -0
  37. package/dist/decorators/collate.js +16 -0
  38. package/dist/decorators/comment.d.ts +3 -1
  39. package/dist/decorators/comment.js +3 -1
  40. package/dist/decorators/database-settings.d.ts +6 -0
  41. package/dist/decorators/database-settings.js +6 -0
  42. package/dist/decorators/default.d.ts +2 -0
  43. package/dist/decorators/default.js +2 -0
  44. package/dist/decorators/encryption.d.ts +7 -1
  45. package/dist/decorators/encryption.js +7 -1
  46. package/dist/decorators/engine.d.ts +4 -0
  47. package/dist/decorators/engine.js +4 -0
  48. package/dist/decorators/fk-constraints.d.ts +32 -32
  49. package/dist/decorators/fk-constraints.js +32 -32
  50. package/dist/decorators/foreign-table.d.ts +1 -1
  51. package/dist/decorators/generated.d.ts +8 -0
  52. package/dist/decorators/generated.js +8 -0
  53. package/dist/decorators/hstore.d.ts +6 -0
  54. package/dist/decorators/hstore.js +6 -0
  55. package/dist/decorators/index.d.ts +2 -0
  56. package/dist/decorators/index.js +11 -1
  57. package/dist/decorators/json-column.d.ts +2 -0
  58. package/dist/decorators/json-column.js +2 -0
  59. package/dist/decorators/jsonb.d.ts +6 -0
  60. package/dist/decorators/jsonb.js +6 -0
  61. package/dist/decorators/permissions.d.ts +12 -0
  62. package/dist/decorators/permissions.js +12 -0
  63. package/dist/decorators/procedure.d.ts +4 -2
  64. package/dist/decorators/procedure.js +4 -2
  65. package/dist/decorators/query-options.d.ts +24 -0
  66. package/dist/decorators/query-options.js +24 -0
  67. package/dist/decorators/range.d.ts +8 -0
  68. package/dist/decorators/range.js +8 -0
  69. package/dist/decorators/set-column.d.ts +2 -0
  70. package/dist/decorators/set-column.js +2 -0
  71. package/dist/decorators/spatial.d.ts +2 -0
  72. package/dist/decorators/spatial.js +2 -0
  73. package/dist/decorators/storage.d.ts +24 -0
  74. package/dist/decorators/storage.js +24 -0
  75. package/dist/decorators/timezone.d.ts +7 -7
  76. package/dist/decorators/timezone.js +7 -7
  77. package/dist/decorators/trigger.d.ts +3 -1
  78. package/dist/decorators/trigger.js +3 -1
  79. package/dist/decorators/uuid.d.ts +6 -0
  80. package/dist/decorators/uuid.js +6 -0
  81. package/dist/decorators/view.d.ts +7 -1
  82. package/dist/decorators/view.js +7 -1
  83. package/dist/diagrams/chen-diagram.d.ts +15 -12
  84. package/dist/diagrams/chen-diagram.js +76 -85
  85. package/dist/diagrams/class-diagram.d.ts +9 -8
  86. package/dist/diagrams/class-diagram.js +60 -82
  87. package/dist/diagrams/core/index.d.ts +13 -0
  88. package/dist/diagrams/core/index.js +33 -0
  89. package/dist/diagrams/core/layout.d.ts +104 -0
  90. package/dist/diagrams/core/layout.js +223 -0
  91. package/dist/diagrams/{svg-dom.d.ts → core/svg.d.ts} +8 -20
  92. package/dist/diagrams/{svg-dom.js → core/svg.js} +21 -29
  93. package/dist/diagrams/core/theme.d.ts +84 -0
  94. package/dist/diagrams/core/theme.js +62 -0
  95. package/dist/diagrams/dependency-diagram.d.ts +15 -14
  96. package/dist/diagrams/dependency-diagram.js +71 -105
  97. package/dist/diagrams/er-diagram.d.ts +13 -26
  98. package/dist/diagrams/er-diagram.js +180 -259
  99. package/dist/diagrams/flow-diagram.d.ts +16 -11
  100. package/dist/diagrams/flow-diagram.js +96 -91
  101. package/dist/diagrams/gantt-diagram.d.ts +10 -5
  102. package/dist/diagrams/gantt-diagram.js +77 -103
  103. package/dist/diagrams/index-diagram.d.ts +10 -10
  104. package/dist/diagrams/index-diagram.js +106 -116
  105. package/dist/diagrams/index.d.ts +6 -9
  106. package/dist/diagrams/index.js +9 -24
  107. package/dist/diagrams/migration-diagram.d.ts +8 -4
  108. package/dist/diagrams/migration-diagram.js +189 -161
  109. package/dist/diagrams/model-diagram.d.ts +34 -34
  110. package/dist/diagrams/model-diagram.js +106 -485
  111. package/dist/diagrams/package-diagram.d.ts +9 -6
  112. package/dist/diagrams/package-diagram.js +64 -101
  113. package/dist/diagrams/relational-diagram.d.ts +12 -10
  114. package/dist/diagrams/relational-diagram.js +87 -114
  115. package/dist/diagrams/schemadoc-diagram.d.ts +8 -8
  116. package/dist/diagrams/schemadoc-diagram.js +71 -93
  117. package/dist/diagrams/sequence-diagram.d.ts +10 -10
  118. package/dist/diagrams/sequence-diagram.js +64 -89
  119. package/dist/diagrams/state-diagram.d.ts +10 -9
  120. package/dist/diagrams/state-diagram.js +69 -92
  121. package/dist/diagrams/tree-diagram.d.ts +11 -5
  122. package/dist/diagrams/tree-diagram.js +41 -65
  123. package/dist/dialects/clickhouse/index.js +15 -0
  124. package/dist/dialects/cockroachdb/index.d.ts +1 -1
  125. package/dist/dialects/cockroachdb/index.js +32 -63
  126. package/dist/dialects/db2/index.js +23 -8
  127. package/dist/dialects/dialect.d.ts +141 -5
  128. package/dist/dialects/duckdb/index.js +9 -2
  129. package/dist/dialects/hana/index.js +22 -5
  130. package/dist/dialects/lock-clause-helper.d.ts +167 -0
  131. package/dist/dialects/lock-clause-helper.js +316 -0
  132. package/dist/dialects/mariadb/index.d.ts +7 -4
  133. package/dist/dialects/mariadb/index.js +36 -60
  134. package/dist/dialects/mssql/index.js +34 -3
  135. package/dist/dialects/mysql/index.d.ts +1 -1
  136. package/dist/dialects/mysql/index.js +17 -45
  137. package/dist/dialects/oracle/index.d.ts +14 -3
  138. package/dist/dialects/oracle/index.js +66 -36
  139. package/dist/dialects/order-expression-helper.d.ts +72 -0
  140. package/dist/dialects/order-expression-helper.js +154 -0
  141. package/dist/dialects/partial-index.d.ts +52 -0
  142. package/dist/dialects/partial-index.js +251 -0
  143. package/dist/dialects/postgres/index.d.ts +1 -1
  144. package/dist/dialects/postgres/index.js +43 -68
  145. package/dist/dialects/redshift/index.js +9 -2
  146. package/dist/dialects/snowflake/index.js +17 -0
  147. package/dist/dialects/spanner/index.js +7 -2
  148. package/dist/dialects/sqlite/driver.d.ts +55 -0
  149. package/dist/dialects/sqlite/driver.js +102 -0
  150. package/dist/dialects/sqlite/index.d.ts +1 -1
  151. package/dist/dialects/sqlite/index.js +49 -27
  152. package/dist/external-fields/decorator.d.ts +2 -0
  153. package/dist/external-fields/decorator.js +2 -0
  154. package/dist/graph/base-graph-dialect.d.ts +24 -3
  155. package/dist/graph/base-graph-dialect.js +43 -2
  156. package/dist/graph/dgraph/index.js +6 -0
  157. package/dist/graph/gremlin/index.d.ts +6 -0
  158. package/dist/graph/gremlin/index.js +12 -1
  159. package/dist/graph/index.d.ts +1 -1
  160. package/dist/graph/neo4j/index.d.ts +2 -0
  161. package/dist/graph/neo4j/index.js +6 -1
  162. package/dist/graph/types.d.ts +12 -1
  163. package/dist/index.d.ts +12 -3
  164. package/dist/index.js +53 -7
  165. package/dist/logging/index.d.ts +10 -0
  166. package/dist/logging/index.js +23 -0
  167. package/dist/logging/query-logging.d.ts +82 -0
  168. package/dist/logging/query-logging.js +102 -0
  169. package/dist/models/associations.d.ts +38 -0
  170. package/dist/models/associations.js +16 -0
  171. package/dist/models/decorators.d.ts +55 -19
  172. package/dist/models/decorators.js +95 -19
  173. package/dist/models/eager-load.d.ts +99 -0
  174. package/dist/models/eager-load.js +348 -5
  175. package/dist/models/indexes.d.ts +3 -2
  176. package/dist/models/indexes.js +7 -1
  177. package/dist/models/model.js +69 -43
  178. package/dist/prorm.d.ts +175 -409
  179. package/dist/prorm.js +1132 -553
  180. package/dist/query-builders/order-limit-builder.js +12 -10
  181. package/dist/query-builders/sql-compiler.d.ts +10 -3
  182. package/dist/query-builders/sql-compiler.js +14 -43
  183. package/dist/query-interface.d.ts +2 -1
  184. package/dist/schema-objects.d.ts +42 -0
  185. package/dist/schema-objects.js +28 -0
  186. package/dist/sql-constants.d.ts +44 -0
  187. package/dist/sql-constants.js +138 -1
  188. package/dist/types/index.d.ts +387 -17
  189. package/dist/types/index.js +10 -15
  190. package/dist/types/query-types.d.ts +26 -0
  191. package/dist/types/query-types.js +30 -0
  192. package/package.json +39 -8
  193. package/dist/decorators/belongs-to-many.js +0 -115
  194. package/dist/decorators/belongs-to.js +0 -115
  195. package/dist/decorators/has-many.js +0 -127
  196. package/dist/decorators/has-one.js +0 -116
  197. package/dist/diagrams/palette.d.ts +0 -138
  198. package/dist/diagrams/palette.js +0 -194
  199. package/dist/hooks/hooks-manager.d.ts +0 -189
  200. package/dist/hooks/hooks-manager.js +0 -350
  201. package/dist/hooks/index.d.ts +0 -8
  202. package/dist/hooks/index.js +0 -37
  203. /package/dist/{logging.d.ts → logging/logger.d.ts} +0 -0
  204. /package/dist/{logging.js → logging/logger.js} +0 -0
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.MariaDBRowFormats = exports.MariaDBConnectTypes = exports.MariaDBStorageEngines = exports.MariaDBTransaction = exports.MariaDBDialect = void 0;
16
16
  exports.createMariaDBDialect = createMariaDBDialect;
17
17
  const operators_1 = require("../../operators");
18
+ const partial_index_1 = require("../partial-index");
18
19
  // Lazily-loaded `mariadb` driver. Required only when a connection is opened,
19
20
  // so importing this ORM never loads the mariadb driver.
20
21
  let mariadbModule = null;
@@ -32,8 +33,10 @@ function loadMariaDb() {
32
33
  return mariadbModule;
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");
36
38
  const operators_2 = require("../../operators");
39
+ const order_expression_helper_1 = require("../order-expression-helper");
37
40
  /**
38
41
  * MariaDB dialect class that implements the Dialect interface
39
42
  * Extends MySQL compatibility with MariaDB-specific features
@@ -1835,10 +1838,12 @@ class MariaDBDialect {
1835
1838
  if (options?.include && options.include.length > 0) {
1836
1839
  sql += ` INCLUDE (${options.include.map((f) => this.escapeId(f)).join(', ')})`;
1837
1840
  }
1838
- // Add WHERE clause for partial index (MariaDB 10.0.3+)
1839
- if (options?.where && Object.keys(options.where).length > 0) {
1840
- const whereClause = this.buildWhereClause(options.where);
1841
- sql += ` WHERE ${whereClause.sql}`;
1841
+ // MariaDB has no partial/filtered indexes at any version - `CREATE INDEX`
1842
+ // takes no WHERE clause. This used to compile the predicate through the
1843
+ // ordinary WHERE builder and append it, producing SQL the server rejects
1844
+ // (with unbound `?` placeholders in it, no less), so fail with a reason.
1845
+ if (options?.where) {
1846
+ throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
1842
1847
  }
1843
1848
  // Index visibility (MariaDB 10.6+). Unlike MySQL, MariaDB uses the
1844
1849
  // `IGNORED` keyword for indexes (not `INVISIBLE` — that keyword is
@@ -1858,15 +1863,16 @@ class MariaDBDialect {
1858
1863
  await this.query(sql);
1859
1864
  }
1860
1865
  /**
1861
- * Create a partial index (index with WHERE clause)
1862
- * MariaDB supports partial indexes since version 10.0.3
1866
+ * Partial (filtered) indexes - not available on MariaDB.
1867
+ *
1868
+ * The claim that MariaDB 10.0.3 added them is wrong: `CREATE INDEX` has never
1869
+ * taken a `WHERE` clause on MariaDB (a live mariadb:11 server answers
1870
+ * ER_PARSE_ERROR), so this throws rather than build that statement.
1863
1871
  */
1864
- async createPartialIndex(tableName, indexName, fields, where, options) {
1865
- const fieldsSql = fields.map((f) => this.escapeId(f)).join(', ');
1866
- let sql = `CREATE INDEX ${this.escapeId(indexName)} ON ${this.quoteTable(tableName)} (${fieldsSql}) WHERE ${where}`;
1867
- if (options?.unique)
1868
- sql = sql.replace('CREATE INDEX', 'CREATE UNIQUE INDEX');
1869
- await this.query(sql);
1872
+ async createPartialIndex(_tableName, _indexName, _fields, _where, _options) {
1873
+ // This used to emit `CREATE INDEX ... WHERE ...`, which MariaDB has never
1874
+ // accepted; the statement failed at the server with a syntax error.
1875
+ throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
1870
1876
  }
1871
1877
  /**
1872
1878
  * Create an index on a table with full options support
@@ -1891,10 +1897,9 @@ class MariaDBDialect {
1891
1897
  if (indexDef.using) {
1892
1898
  sql += ` USING ${indexDef.using}`;
1893
1899
  }
1894
- // Add WHERE clause for partial index (MariaDB 10.0.3+)
1895
- if (indexDef.where && Object.keys(indexDef.where).length > 0) {
1896
- const whereClause = this.buildWhereClause(indexDef.where);
1897
- sql += ` WHERE ${whereClause.sql}`;
1900
+ // No partial/filtered indexes on MariaDB - see addIndex.
1901
+ if (indexDef.where) {
1902
+ throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
1898
1903
  }
1899
1904
  // Index visibility (MariaDB 10.6+). Unlike MySQL, MariaDB uses
1900
1905
  // `IGNORED` (not `INVISIBLE`) for indexes. Not ignored by default.
@@ -2368,12 +2373,18 @@ class MariaDBDialect {
2368
2373
  * Build an ORDER BY clause
2369
2374
  */
2370
2375
  buildOrderClause(order, options) {
2371
- const orderArray = order;
2372
- if (!orderArray || !Array.isArray(orderArray) || orderArray.length === 0) {
2376
+ const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
2377
+ if (orderArray.length === 0) {
2373
2378
  return '';
2374
2379
  }
2375
2380
  const orderParts = [];
2376
2381
  for (const item of orderArray) {
2382
+ // Order helpers: asc('name') / desc('name') / random()
2383
+ const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
2384
+ if (orderExpr !== null) {
2385
+ orderParts.push(orderExpr);
2386
+ continue;
2387
+ }
2377
2388
  if (Array.isArray(item)) {
2378
2389
  const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
2379
2390
  const direction = item[1] ? ` ${item[1]}` : '';
@@ -3025,48 +3036,13 @@ class MariaDBDialect {
3025
3036
  }
3026
3037
  // Build LIMIT/OFFSET clause
3027
3038
  sql += this.buildLimitOffset(options.limit, options.offset);
3028
- // Handle row-level locking
3029
- if (options.lock) {
3030
- // lock: true is equivalent to 'UPDATE'
3031
- let lockType;
3032
- let skipLocked = false;
3033
- let noWait = false;
3034
- if (options.lock === true) {
3035
- lockType = 'UPDATE';
3036
- }
3037
- else if (typeof options.lock === 'string') {
3038
- lockType = options.lock;
3039
- }
3040
- else if (typeof options.lock === 'object' && options.lock !== null) {
3041
- // lock: { of: Model, mode: 'UPDATE' | 'SHARE', skipLocked?, noWait? }
3042
- lockType = options.lock.mode || (options.lock.of ? 'UPDATE' : undefined);
3043
- skipLocked = !!options.lock.skipLocked;
3044
- noWait = !!options.lock.noWait;
3045
- }
3046
- if (lockType === 'UPDATE') {
3047
- sql += ' FOR UPDATE';
3048
- }
3049
- else if (lockType === 'SHARE') {
3050
- // MariaDB 10.6+ prefers `FOR SHARE` over the legacy `LOCK IN SHARE MODE`
3051
- // syntax, but only `LOCK IN SHARE MODE` supports no modifiers, so use
3052
- // `FOR SHARE` whenever a SKIP LOCKED/NOWAIT modifier is requested.
3053
- if (skipLocked || noWait) {
3054
- sql += ' FOR SHARE';
3055
- }
3056
- else {
3057
- sql += ' LOCK IN SHARE MODE';
3058
- }
3059
- }
3060
- // MariaDB 10.6+ locking modifiers
3061
- if (lockType === 'UPDATE' || lockType === 'SHARE') {
3062
- if (skipLocked) {
3063
- sql += ' SKIP LOCKED';
3064
- }
3065
- else if (noWait) {
3066
- sql += ' NOWAIT';
3067
- }
3068
- }
3069
- }
3039
+ // Handle row-level locking (MariaDB 10.6+). The shared helper parses
3040
+ // every documented `lock` shape - including `{ level, nowait }`, which this dialect only understood
3041
+ // under its own `{ mode, noWait }` spelling and otherwise compiled to no
3042
+ // lock at all. Both spellings are still accepted. A shared lock uses the
3043
+ // legacy `LOCK IN SHARE MODE` unless a modifier is requested, since only
3044
+ // `FOR SHARE` accepts SKIP LOCKED / NOWAIT.
3045
+ sql += (0, lock_clause_helper_1.appendLockClause)(options.lock, lock_clause_helper_1.MARIADB_LOCK_SPEC);
3070
3046
  // Build UNION/EXCEPT/INTERSECT clause if provided
3071
3047
  // MariaDB 10.3+ supports EXCEPT/EXCEPT ALL/INTERSECT/INTERSECT ALL in
3072
3048
  // addition to UNION/UNION ALL.
@@ -8,9 +8,12 @@ exports.MSSQLDialect = void 0;
8
8
  exports.createMSSQLDialect = createMSSQLDialect;
9
9
  const mssql_1 = require("mssql");
10
10
  const operators_1 = require("../../operators");
11
+ const partial_index_1 = require("../partial-index");
11
12
  const types_1 = require("../../types");
12
13
  const query_stream_helper_1 = require("../query-stream-helper");
14
+ const lock_clause_helper_1 = require("../lock-clause-helper");
13
15
  const prorm_1 = require("../../prorm");
16
+ const order_expression_helper_1 = require("../order-expression-helper");
14
17
  /**
15
18
  * Default retry options for connection
16
19
  */
@@ -975,10 +978,15 @@ class MSSQLDialect {
975
978
  if (options?.using)
976
979
  sql += ` USING ${options.using}`;
977
980
  // Filtered index predicate (a real, commonly-used MSSQL feature) - was
978
- // previously accepted by createIndex() but silently dropped here.
981
+ // previously accepted by createIndex() but silently dropped here. The
982
+ // ordinary WHERE builder emits `@p1`-style placeholders and this DDL is
983
+ // executed without a parameter list, so the predicate is compiled with its
984
+ // values inlined as escaped literals.
979
985
  if (options?.where) {
980
- const whereClause = this.buildWhereClause(options.where);
981
- sql += ` WHERE ${whereClause.sql}`;
986
+ const predicate = (0, partial_index_1.buildPartialIndexPredicate)(options.where, this.name, this);
987
+ if (predicate) {
988
+ sql += ` WHERE ${predicate}`;
989
+ }
982
990
  }
983
991
  await this.query(sql);
984
992
  }
@@ -1333,6 +1341,12 @@ class MSSQLDialect {
1333
1341
  return '';
1334
1342
  const orderParts = [];
1335
1343
  const processOrder = (orderItem) => {
1344
+ // Order helpers: asc('name') / desc('name') / random()
1345
+ const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, orderItem);
1346
+ if (orderExpr !== null) {
1347
+ orderParts.push(orderExpr);
1348
+ return;
1349
+ }
1336
1350
  if (typeof orderItem === 'string') {
1337
1351
  const parts = orderItem.split(' ');
1338
1352
  if (parts.length === 1) {
@@ -1342,6 +1356,16 @@ class MSSQLDialect {
1342
1356
  orderParts.push(`${this.escapeId(parts[0])} ${parts[1].toUpperCase()}`);
1343
1357
  }
1344
1358
  }
1359
+ else if (Array.isArray(orderItem)) {
1360
+ // Tuple form — `['name', 'DESC']` / `['name']`, the elements of the
1361
+ // primary documented `[['name', 'DESC'], ['id', 'ASC']]` shape. An
1362
+ // array is also an object, so without this branch each tuple fell
1363
+ // through to the map branch below and its *indices* were emitted as
1364
+ // column names (`ORDER BY [0] NAME, [1] DESC`).
1365
+ const [field, direction] = orderItem;
1366
+ const column = typeof field === 'string' ? this.escapeId(field) : String(field);
1367
+ orderParts.push(direction ? `${column} ${String(direction).toUpperCase()}` : column);
1368
+ }
1345
1369
  else if (typeof orderItem === 'object' && orderItem !== null) {
1346
1370
  for (const [key, value] of Object.entries(orderItem)) {
1347
1371
  if (key === '$raw') {
@@ -1569,6 +1593,13 @@ class MSSQLDialect {
1569
1593
  else if (options.temporalAll) {
1570
1594
  sql += ' FOR SYSTEM_TIME ALL';
1571
1595
  }
1596
+ // Row-level locking. T-SQL has no `FOR UPDATE`; a lock is requested with a
1597
+ // table hint on the table reference, which has to sit immediately after it
1598
+ // (and after any FOR SYSTEM_TIME clause). See lock-clause-helper.ts.
1599
+ const lockHint = (0, lock_clause_helper_1.buildSqlServerLockHint)(options.lock, options.tableName);
1600
+ if (lockHint) {
1601
+ sql += ` ${lockHint}`;
1602
+ }
1572
1603
  if (options.include && options.include.length > 0) {
1573
1604
  for (const include of options.include) {
1574
1605
  // CROSS APPLY / OUTER APPLY — a lateral join, needed for anything an
@@ -711,7 +711,7 @@ export declare class MySQLDialect implements Dialect {
711
711
  type?: string;
712
712
  using?: string;
713
713
  fields: string[];
714
- where?: WhereOptions;
714
+ where?: WhereOptions | string;
715
715
  expression?: string;
716
716
  include?: string[];
717
717
  invisible?: boolean;
@@ -12,6 +12,8 @@ const promise_1 = __importDefault(require("mysql2/promise"));
12
12
  const prorm_1 = require("../../prorm");
13
13
  const operators_1 = require("../../operators");
14
14
  const operators_2 = require("../../operators");
15
+ const lock_clause_helper_1 = require("../lock-clause-helper");
16
+ const order_expression_helper_1 = require("../order-expression-helper");
15
17
  /**
16
18
  * Default retry options for connection
17
19
  */
@@ -2881,12 +2883,18 @@ class MySQLDialect {
2881
2883
  * Build an ORDER BY clause
2882
2884
  */
2883
2885
  buildOrderClause(order, options) {
2884
- const orderArray = order;
2885
- if (!orderArray || !Array.isArray(orderArray) || orderArray.length === 0) {
2886
+ const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
2887
+ if (orderArray.length === 0) {
2886
2888
  return '';
2887
2889
  }
2888
2890
  const orderParts = [];
2889
2891
  for (const item of orderArray) {
2892
+ // Order helpers: asc('name') / desc('name') / random()
2893
+ const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
2894
+ if (orderExpr !== null) {
2895
+ orderParts.push(orderExpr);
2896
+ continue;
2897
+ }
2890
2898
  if (Array.isArray(item)) {
2891
2899
  const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
2892
2900
  const direction = item[1] ? ` ${item[1]}` : '';
@@ -3483,49 +3491,13 @@ class MySQLDialect {
3483
3491
  // Build LIMIT/OFFSET clause
3484
3492
  sql += this.buildLimitOffset(options.limit, options.offset);
3485
3493
  // Handle row-level locking
3486
- if (options.lock) {
3487
- // lock: true is equivalent to 'UPDATE'
3488
- let lockType;
3489
- let nowait = false;
3490
- let skipLocked = false;
3491
- if (options.lock === true) {
3492
- lockType = 'UPDATE';
3493
- }
3494
- else if (typeof options.lock === 'string') {
3495
- lockType = options.lock;
3496
- }
3497
- else if (typeof options.lock === 'object') {
3498
- // Handle { level: 'UPDATE', nowait: true } or { level: 'UPDATE', skipLocked: true }
3499
- lockType = options.lock.level || (options.lock.of ? 'UPDATE' : undefined);
3500
- nowait = options.lock.nowait === true;
3501
- skipLocked = options.lock.skipLocked === true;
3502
- }
3503
- if (lockType === 'UPDATE') {
3504
- sql += ' FOR UPDATE';
3505
- // MySQL 8.0+ supports table-scoped locking via `FOR UPDATE OF <table>`
3506
- if (typeof options.lock === 'object' && options.lock.of) {
3507
- const model = options.lock.of;
3508
- if (model.tableName) {
3509
- sql += ` OF ${this.escapeId(model.tableName)}`;
3510
- }
3511
- }
3512
- // MySQL 8.0+ supports NOWAIT and SKIP LOCKED
3513
- if (nowait) {
3514
- sql += ' NOWAIT';
3515
- }
3516
- else if (skipLocked) {
3517
- sql += ' SKIP LOCKED';
3518
- }
3519
- }
3520
- else if (lockType === 'SHARE') {
3521
- sql += ' LOCK IN SHARE MODE';
3522
- // MySQL 8.0+ supports NOWAIT for SHARE mode
3523
- if (nowait) {
3524
- sql += ' NOWAIT';
3525
- }
3526
- // MySQL doesn't support SKIP LOCKED for LOCK IN SHARE MODE
3527
- }
3528
- }
3494
+ // `FOR UPDATE [OF tbl] [NOWAIT | SKIP LOCKED]` / `LOCK IN SHARE MODE`, with
3495
+ // every documented `lock` shape parsed by the shared helper. A shared lock
3496
+ // switches to the MySQL 8.0 `FOR SHARE` spelling when a NOWAIT/SKIP LOCKED
3497
+ // modifier is requested, since `LOCK IN SHARE MODE` accepts neither (it
3498
+ // used to emit an invalid `LOCK IN SHARE MODE NOWAIT`, and drop SKIP LOCKED
3499
+ // on the floor). See src/dialects/lock-clause-helper.ts.
3500
+ sql += (0, lock_clause_helper_1.appendLockClause)(options.lock, lock_clause_helper_1.MYSQL_LOCK_SPEC, (id) => this.escapeId(id));
3529
3501
  // Build UNION clause if provided
3530
3502
  if (options.union && options.union.length > 0) {
3531
3503
  const unionType = options.unionType || 'UNION';
@@ -772,7 +772,14 @@ export declare class OracleDialect implements Dialect {
772
772
  */
773
773
  private buildOperatorWhere;
774
774
  /**
775
- * Build ORDER BY clause
775
+ * Build ORDER BY clause.
776
+ *
777
+ * Every `Order` shape is handled explicitly. Previously this only ever
778
+ * called `Object.keys()`/`Object.entries()` on each item, which is correct
779
+ * for the `{ field: 'DESC' }` map form but silently mis-reads every other
780
+ * one: a `['name', 'DESC']` tuple yielded its indices (`ORDER BY "0" ASC`)
781
+ * and a bare `'name DESC'` string yielded its character indices — ordering
782
+ * by the wrong thing rather than failing.
776
783
  */
777
784
  buildOrderClause(order: Order, _options?: BuildOptions): string;
778
785
  /**
@@ -1020,9 +1027,13 @@ export declare class OracleDialect implements Dialect {
1020
1027
  */
1021
1028
  commentColumn(tableName: string, columnName: string, comment: string): Promise<void>;
1022
1029
  /**
1023
- * Create a partial index (index with WHERE clause)
1030
+ * Partial indexes - not available on Oracle.
1031
+ *
1032
+ * Oracle's `CREATE INDEX` has no `WHERE` clause; the equivalent is a
1033
+ * function-based index on a CASE expression that yields NULL for the rows to
1034
+ * exclude, since a B-tree index does not store all-NULL keys.
1024
1035
  */
1025
- createPartialIndex(tableName: string, indexName: string, fields: string[], where: string, options?: IndexOptions): Promise<void>;
1036
+ createPartialIndex(_tableName: string, _indexName: string, _fields: string[], _where: string, _options?: IndexOptions): Promise<void>;
1026
1037
  /**
1027
1038
  * Create an expression index (functional index)
1028
1039
  */
@@ -6,6 +6,7 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.OracleDialect = void 0;
8
8
  exports.createOracleDialect = createOracleDialect;
9
+ const partial_index_1 = require("../partial-index");
9
10
  // Lazily-loaded `oracledb` driver. The native Oracle client is only loaded the
10
11
  // first time a connection is opened, so importing this ORM never dlopens the
11
12
  // oracledb native addon.
@@ -24,8 +25,10 @@ function loadOracleDb() {
24
25
  return oracledbModule;
25
26
  }
26
27
  const query_stream_helper_1 = require("../query-stream-helper");
28
+ const lock_clause_helper_1 = require("../lock-clause-helper");
27
29
  const errors_1 = require("../../errors");
28
30
  const crypto_1 = require("crypto");
31
+ const order_expression_helper_1 = require("../order-expression-helper");
29
32
  /**
30
33
  * Oracle-specific transaction class
31
34
  */
@@ -1295,9 +1298,12 @@ class OracleDialect {
1295
1298
  sql += ` ${options.type.toUpperCase()}`;
1296
1299
  }
1297
1300
  sql += ` INDEX ${this.quoteIdentifier(indexName)} ON ${this.quoteTable(tableName)} (${fieldsSql})`;
1301
+ // Oracle's CREATE INDEX has no WHERE clause - there are no partial indexes.
1302
+ // This used to append one built by the ordinary WHERE builder, so the
1303
+ // statement reached the server both syntactically invalid and carrying
1304
+ // unbound placeholders. Fail with a reason instead.
1298
1305
  if (options?.where) {
1299
- const whereClause = this.buildWhereClause(options.where);
1300
- sql += ` WHERE ${whereClause.sql}`;
1306
+ throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
1301
1307
  }
1302
1308
  return sql;
1303
1309
  }
@@ -1616,22 +1622,52 @@ class OracleDialect {
1616
1622
  }
1617
1623
  }
1618
1624
  /**
1619
- * Build ORDER BY clause
1625
+ * Build ORDER BY clause.
1626
+ *
1627
+ * Every `Order` shape is handled explicitly. Previously this only ever
1628
+ * called `Object.keys()`/`Object.entries()` on each item, which is correct
1629
+ * for the `{ field: 'DESC' }` map form but silently mis-reads every other
1630
+ * one: a `['name', 'DESC']` tuple yielded its indices (`ORDER BY "0" ASC`)
1631
+ * and a bare `'name DESC'` string yielded its character indices — ordering
1632
+ * by the wrong thing rather than failing.
1620
1633
  */
1621
1634
  buildOrderClause(order, _options) {
1622
1635
  if (!order || (Array.isArray(order) && order.length === 0)) {
1623
1636
  return '';
1624
1637
  }
1625
- const orderParts = Array.isArray(order)
1626
- ? order.map((o) => {
1627
- const field = Object.keys(o)[0];
1628
- const direction = Object.values(o)[0];
1629
- return `${this.quoteIdentifier(field)} ${direction === 'DESC' ? 'DESC' : 'ASC'}`;
1630
- })
1631
- : Object.entries(order).map(([field, direction]) => {
1632
- return `${this.quoteIdentifier(field)} ${direction === 'DESC' ? 'DESC' : 'ASC'}`;
1633
- });
1634
- return `ORDER BY ${orderParts.join(', ')}`;
1638
+ const orderParts = [];
1639
+ const term = (field, direction) => `${this.quoteIdentifier(field)} ${String(direction).toUpperCase() === 'DESC' ? 'DESC' : 'ASC'}`;
1640
+ const processOrder = (orderItem) => {
1641
+ // Order helpers: asc('name') / desc('name') / random(). Handled first so
1642
+ // a bare `order: random()` works as well as one inside the order array.
1643
+ const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, orderItem);
1644
+ if (orderExpr !== null) {
1645
+ orderParts.push(orderExpr);
1646
+ }
1647
+ else if (typeof orderItem === 'string') {
1648
+ // 'name' or 'name DESC'
1649
+ const [field, direction] = orderItem.trim().split(/\s+/);
1650
+ orderParts.push(term(field, direction));
1651
+ }
1652
+ else if (Array.isArray(orderItem)) {
1653
+ // ['name', 'DESC'] or ['name']
1654
+ const [field, direction] = orderItem;
1655
+ orderParts.push(term(String(field), direction));
1656
+ }
1657
+ else if (orderItem && typeof orderItem === 'object') {
1658
+ // { field: 'DESC' } map form
1659
+ for (const [field, direction] of Object.entries(orderItem)) {
1660
+ orderParts.push(term(field, direction));
1661
+ }
1662
+ }
1663
+ };
1664
+ if (Array.isArray(order)) {
1665
+ order.forEach(processOrder);
1666
+ }
1667
+ else {
1668
+ processOrder(order);
1669
+ }
1670
+ return orderParts.length > 0 ? `ORDER BY ${orderParts.join(', ')}` : '';
1635
1671
  }
1636
1672
  /**
1637
1673
  * Build a list of ORDER BY items (without the leading `ORDER BY` keyword),
@@ -1899,13 +1935,11 @@ class OracleDialect {
1899
1935
  sql += ` AND ROWNUM <= ${limit + offset}`;
1900
1936
  }
1901
1937
  }
1902
- // FOR UPDATE (locking)
1903
- if (options.lock) {
1904
- sql += ' FOR UPDATE';
1905
- if (options.lock === 'SHARE') {
1906
- sql += ' NOWAIT';
1907
- }
1908
- }
1938
+ // Row-level locking: `FOR UPDATE [NOWAIT | SKIP LOCKED]`. Oracle has no
1939
+ // shared row lock, so `lock: 'SHARE'` now throws instead of quietly turning
1940
+ // into an exclusive `FOR UPDATE NOWAIT`. `lock.of` names *columns* in
1941
+ // Oracle, not a table, so a `{ of: Model }` lock stays a plain FOR UPDATE.
1942
+ sql += (0, lock_clause_helper_1.appendLockClause)(options.lock, lock_clause_helper_1.ORACLE_LOCK_SPEC);
1909
1943
  return { sql, values };
1910
1944
  }
1911
1945
  /**
@@ -2705,22 +2739,18 @@ END;
2705
2739
  }
2706
2740
  // ==================== Advanced Indexes ====================
2707
2741
  /**
2708
- * Create a partial index (index with WHERE clause)
2709
- */
2710
- async createPartialIndex(tableName, indexName, fields, where, options) {
2711
- const fieldsSql = fields.map((f) => this.quoteIdentifier(f)).join(', ');
2712
- let sql = `CREATE`;
2713
- if (options?.unique) {
2714
- sql += ' UNIQUE';
2715
- }
2716
- sql += ` INDEX ${this.quoteIdentifier(indexName)} ON ${this.quoteTable(tableName)} (${fieldsSql}) WHERE ${where}`;
2717
- if (options?.tablespace) {
2718
- sql += ` TABLESPACE ${options.tablespace}`;
2719
- }
2720
- if (options?.compress) {
2721
- sql += ` COMPRESS`;
2722
- }
2723
- await this.query(sql);
2742
+ * Partial indexes - not available on Oracle.
2743
+ *
2744
+ * Oracle's `CREATE INDEX` has no `WHERE` clause; the equivalent is a
2745
+ * function-based index on a CASE expression that yields NULL for the rows to
2746
+ * exclude, since a B-tree index does not store all-NULL keys.
2747
+ */
2748
+ async createPartialIndex(_tableName, _indexName, _fields, _where, _options) {
2749
+ // This used to emit `CREATE INDEX ... WHERE ...`, which Oracle has never
2750
+ // accepted. The Oracle-shaped equivalent is a function-based index on a
2751
+ // CASE expression that returns NULL for the excluded rows (NULLs are not
2752
+ // stored in a B-tree index), i.e. createExpressionIndex.
2753
+ throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
2724
2754
  }
2725
2755
  /**
2726
2756
  * Create an expression index (functional index)
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Shared rendering for the `OrderExpression` objects produced by the public
3
+ * `asc()` / `desc()` / `random()` helpers exported from `src/operators.ts`.
4
+ *
5
+ * Every dialect implements its own `buildOrderClause()` with slightly
6
+ * different handling of the legacy `order` shapes it accepts (bare strings,
7
+ * `['field', 'DESC']` tuples, `{ field: 'DESC' }` maps, `[Model, 'field']`
8
+ * pairs, ...). Rather than teach ~27 dialect files how to recognise and
9
+ * render an `OrderExpression` — the same code copied 27 times — each dialect
10
+ * calls into this module for exactly that one item shape and keeps its own
11
+ * handling for everything else. This mirrors `./query-stream-helper.ts`,
12
+ * which factors one cross-cutting behaviour out of the dialects the same way.
13
+ *
14
+ * The one genuinely dialect-specific piece is `random()`: the function used
15
+ * to produce a random sort key differs per engine, so it is looked up from
16
+ * `RANDOM_FUNCTIONS` by the dialect's own `name`. Dialects with no known
17
+ * random-ordering function throw a descriptive error rather than silently
18
+ * emitting SQL that would not run (or, worse, no `ORDER BY` at all, which is
19
+ * what `order: random()` used to do everywhere).
20
+ */
21
+ import { type OrderExpression } from '../operators';
22
+ import type { Order } from '../types';
23
+ /**
24
+ * The slice of a dialect this helper needs. Deliberately narrower than the
25
+ * full `Dialect` interface so it can be unit tested with a stub, and so
26
+ * `buildOrderClause()` implementations can pass `this` without a cast.
27
+ */
28
+ export interface OrderExpressionDialect {
29
+ /** The dialect's registered name, e.g. `'sqlite'`, `'mysql'`, `'mssql'`. */
30
+ readonly name: string;
31
+ /** The dialect's own identifier quoting. */
32
+ escapeId(identifier: unknown): string;
33
+ }
34
+ /**
35
+ * The SQL expression that orders rows randomly on `dialectName`.
36
+ *
37
+ * @throws if the dialect has no known random-ordering function — callers get
38
+ * an actionable error instead of SQL that silently does the wrong thing.
39
+ */
40
+ export declare function randomOrderFunction(dialectName: string | undefined): string;
41
+ /**
42
+ * Render one `OrderExpression` as an `ORDER BY` list item (no leading
43
+ * `ORDER BY`), using the dialect's own identifier quoting.
44
+ */
45
+ export declare function renderOrderExpression(dialect: OrderExpressionDialect, expression: OrderExpression): string;
46
+ /**
47
+ * Render `item` if — and only if — it is an `OrderExpression`; otherwise
48
+ * return `null` so the caller falls through to its own handling of the
49
+ * legacy order shapes.
50
+ *
51
+ * Usage inside a dialect's `buildOrderClause()`:
52
+ *
53
+ * ```ts
54
+ * const orderExpr = renderOrderExpressionItem(this, item);
55
+ * if (orderExpr !== null) {
56
+ * orderParts.push(orderExpr);
57
+ * continue;
58
+ * }
59
+ * ```
60
+ */
61
+ export declare function renderOrderExpressionItem(dialect: OrderExpressionDialect, item: unknown): string | null;
62
+ /**
63
+ * Normalise a `FindOptions.order` value into the list of items a dialect
64
+ * should iterate over.
65
+ *
66
+ * Behaviour is intentionally conservative so no currently-working shape
67
+ * changes: arrays pass straight through, a **bare** `OrderExpression`
68
+ * (`order: random()`) is wrapped into a single-item list, and anything else
69
+ * non-array yields `[]` — exactly what the `!Array.isArray(order)` guard the
70
+ * dialects used to open with produced.
71
+ */
72
+ export declare function normalizeOrderItems(order: Order | undefined | null): unknown[];