uql-orm 0.53.0 → 0.55.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 (158) hide show
  1. package/README.md +2 -2
  2. package/dist/browser/uql-browser.min.js.map +2 -2
  3. package/dist/bunSql/bunSql.util.d.ts +3 -14
  4. package/dist/bunSql/bunSql.util.js +33 -56
  5. package/dist/bunSql/bunSqlQuerier.d.ts +3 -6
  6. package/dist/bunSql/bunSqlQuerier.js +7 -13
  7. package/dist/bunSql/bunSqlQuerierPool.d.ts +10 -5
  8. package/dist/bunSql/bunSqlQuerierPool.js +25 -10
  9. package/dist/cockroachdb/cockroachDialect.d.ts +2 -5
  10. package/dist/cockroachdb/cockroachDialect.js +2 -5
  11. package/dist/cockroachdb/crdbQuerierPool.d.ts +1 -3
  12. package/dist/cockroachdb/crdbQuerierPool.js +0 -4
  13. package/dist/cockroachdb/index.d.ts +0 -1
  14. package/dist/cockroachdb/index.js +0 -1
  15. package/dist/d1/d1SqliteDialect.d.ts +5 -0
  16. package/dist/d1/d1SqliteDialect.js +7 -0
  17. package/dist/dialect/abstractSqlDialect.d.ts +15 -9
  18. package/dist/dialect/abstractSqlDialect.js +47 -54
  19. package/dist/dialect/hydrateColumn.js +2 -2
  20. package/dist/dialect/mergeSqlDialect.d.ts +2 -2
  21. package/dist/dialect/mergeSqlDialect.js +0 -4
  22. package/dist/dialect/mysqlLikeSqlDialect.d.ts +0 -3
  23. package/dist/dialect/mysqlLikeSqlDialect.js +0 -3
  24. package/dist/dialect/pgLikeSqlDialect.d.ts +5 -0
  25. package/dist/dialect/pgLikeSqlDialect.js +12 -2
  26. package/dist/entity/decorator/members.d.ts +3 -10
  27. package/dist/entity/index.d.ts +1 -1
  28. package/dist/entity/index.js +1 -1
  29. package/dist/entity/metadata/definition.d.ts +3 -1
  30. package/dist/entity/metadata/definition.js +8 -4
  31. package/dist/libsql/index.d.ts +0 -1
  32. package/dist/libsql/index.js +0 -1
  33. package/dist/libsql/libsqlQuerierPool.d.ts +3 -5
  34. package/dist/libsql/libsqlQuerierPool.js +2 -5
  35. package/dist/maria/mariadbQuerier.d.ts +0 -3
  36. package/dist/maria/mariadbQuerier.js +6 -7
  37. package/dist/maria/mariadbQuerierPool.js +4 -7
  38. package/dist/migrate/builder/migrationBuilder.js +0 -4
  39. package/dist/migrate/ddl/index.d.ts +3 -3
  40. package/dist/migrate/ddl/index.js +3 -3
  41. package/dist/migrate/migrator.d.ts +3 -7
  42. package/dist/migrate/migrator.js +3 -7
  43. package/dist/migrate/schemaGenerator.d.ts +0 -13
  44. package/dist/migrate/schemaGenerator.js +0 -13
  45. package/dist/migrate/storage/databaseStorage.d.ts +2 -2
  46. package/dist/migrate/storage/databaseStorage.js +2 -2
  47. package/dist/mongo/index.d.ts +0 -1
  48. package/dist/mongo/index.js +0 -1
  49. package/dist/mongo/mongoDialect.d.ts +0 -1
  50. package/dist/mongo/mongoDialect.js +3 -14
  51. package/dist/mongo/mongodbQuerier.d.ts +3 -5
  52. package/dist/mongo/mongodbQuerier.js +23 -23
  53. package/dist/mongo/mongodbQuerierPool.d.ts +2 -2
  54. package/dist/mongo/mongodbQuerierPool.js +2 -2
  55. package/dist/mssql/mssqlDialect.d.ts +5 -5
  56. package/dist/mssql/mssqlDialect.js +11 -8
  57. package/dist/mssql/mssqlQuerier.d.ts +13 -6
  58. package/dist/mssql/mssqlQuerier.js +30 -75
  59. package/dist/mssql/mssqlQuerierPool.js +2 -0
  60. package/dist/mssql/mssqlWireTypes.d.ts +3 -4
  61. package/dist/mssql/mssqlWireTypes.js +5 -8
  62. package/dist/mysql/index.d.ts +0 -1
  63. package/dist/mysql/index.js +0 -1
  64. package/dist/mysql/mysql2Querier.d.ts +1 -4
  65. package/dist/mysql/mysql2Querier.js +0 -3
  66. package/dist/mysql/mysql2QuerierPool.d.ts +2 -2
  67. package/dist/mysql/mysql2QuerierPool.js +5 -3
  68. package/dist/neon/index.d.ts +0 -2
  69. package/dist/neon/index.js +0 -2
  70. package/dist/neon/neonQuerierPool.d.ts +2 -4
  71. package/dist/neon/neonQuerierPool.js +2 -6
  72. package/dist/pglite/index.d.ts +0 -1
  73. package/dist/pglite/index.js +0 -1
  74. package/dist/pglite/pgliteQuerier.d.ts +3 -3
  75. package/dist/pglite/pgliteQuerier.js +1 -1
  76. package/dist/pglite/pgliteQuerierPool.d.ts +7 -2
  77. package/dist/pglite/pgliteQuerierPool.js +16 -6
  78. package/dist/postgres/abstractPgQuerierPool.d.ts +8 -12
  79. package/dist/postgres/abstractPgQuerierPool.js +8 -6
  80. package/dist/postgres/index.d.ts +0 -1
  81. package/dist/postgres/index.js +0 -1
  82. package/dist/postgres/pgNumericTypes.d.ts +5 -5
  83. package/dist/postgres/pgNumericTypes.js +11 -7
  84. package/dist/postgres/pgQuerier.d.ts +22 -4
  85. package/dist/postgres/pgQuerier.js +29 -2
  86. package/dist/postgres/pgQuerierPool.d.ts +2 -4
  87. package/dist/postgres/pgQuerierPool.js +2 -6
  88. package/dist/postgres/postgresDialect.d.ts +5 -5
  89. package/dist/postgres/postgresDialect.js +5 -5
  90. package/dist/postgres/postgresWireDriverCapabilities.d.ts +2 -2
  91. package/dist/postgres/postgresWireDriverCapabilities.js +2 -2
  92. package/dist/querier/abstractPoolQuerier.d.ts +1 -1
  93. package/dist/querier/abstractPoolQuerier.js +1 -1
  94. package/dist/querier/abstractQuerier.d.ts +17 -22
  95. package/dist/querier/abstractQuerier.js +80 -57
  96. package/dist/querier/abstractSqlQuerier.d.ts +20 -13
  97. package/dist/querier/abstractSqlQuerier.js +96 -100
  98. package/dist/schema/schemaASTBuilder.js +7 -7
  99. package/dist/sqlite/hranaQuerier.d.ts +6 -8
  100. package/dist/sqlite/hranaQuerier.js +13 -30
  101. package/dist/sqlite/hranaQuerierPool.d.ts +3 -4
  102. package/dist/sqlite/hranaQuerierPool.js +2 -1
  103. package/dist/sqlite/localSqliteQuerierPool.d.ts +3 -3
  104. package/dist/sqlite/localSqliteQuerierPool.js +4 -2
  105. package/dist/sqlite/nodeSqliteAdapter.d.ts +0 -1
  106. package/dist/sqlite/nodeSqliteQuerierPool.js +0 -2
  107. package/dist/sqlite/sqlitePragmas.d.ts +12 -0
  108. package/dist/sqlite/sqlitePragmas.js +15 -0
  109. package/dist/sqlite/sqliteQuerierPool.d.ts +0 -5
  110. package/dist/sqlite/sqliteQuerierPool.js +2 -13
  111. package/dist/turso/index.d.ts +0 -1
  112. package/dist/turso/index.js +0 -1
  113. package/dist/turso/tursoLocalQuerier.d.ts +0 -1
  114. package/dist/turso/tursoLocalQuerierPool.js +2 -2
  115. package/dist/turso/tursoQuerierPool.d.ts +1 -3
  116. package/dist/turso/tursoQuerierPool.js +0 -4
  117. package/dist/type/dialect.d.ts +1 -1
  118. package/dist/type/entity.d.ts +6 -11
  119. package/dist/type/migration.d.ts +0 -3
  120. package/dist/type/query.d.ts +12 -12
  121. package/dist/type/query.js +0 -6
  122. package/dist/type/universalQuerier.d.ts +3 -3
  123. package/dist/util/dialect.util.d.ts +4 -3
  124. package/dist/util/dialect.util.js +2 -1
  125. package/dist/util/field.util.d.ts +4 -16
  126. package/dist/util/field.util.js +6 -19
  127. package/dist/util/fieldOption.util.d.ts +1 -4
  128. package/dist/util/fieldOption.util.js +0 -2
  129. package/dist/util/logger.d.ts +10 -11
  130. package/dist/util/logger.js +21 -11
  131. package/dist/util/raw.d.ts +3 -10
  132. package/dist/util/raw.js +3 -3
  133. package/dist/util/sql.util.js +2 -2
  134. package/dist/util/sqlLiteral.js +3 -8
  135. package/dist/util/string.util.js +2 -6
  136. package/dist/util/wideNumber.d.ts +14 -0
  137. package/dist/util/wideNumber.js +24 -0
  138. package/package.json +1 -1
  139. package/dist/cockroachdb/crdbQuerier.d.ts +0 -8
  140. package/dist/cockroachdb/crdbQuerier.js +0 -6
  141. package/dist/libsql/libsqlQuerier.d.ts +0 -10
  142. package/dist/libsql/libsqlQuerier.js +0 -10
  143. package/dist/mongo/mongodbNativeDialect.d.ts +0 -9
  144. package/dist/mongo/mongodbNativeDialect.js +0 -9
  145. package/dist/mysql/mysql2Dialect.d.ts +0 -9
  146. package/dist/mysql/mysql2Dialect.js +0 -9
  147. package/dist/neon/neonDialect.d.ts +0 -10
  148. package/dist/neon/neonDialect.js +0 -10
  149. package/dist/neon/neonQuerier.d.ts +0 -5
  150. package/dist/neon/neonQuerier.js +0 -3
  151. package/dist/pglite/pgliteDialect.d.ts +0 -14
  152. package/dist/pglite/pgliteDialect.js +0 -14
  153. package/dist/postgres/abstractPgQuerier.d.ts +0 -24
  154. package/dist/postgres/abstractPgQuerier.js +0 -32
  155. package/dist/postgres/pgDialect.d.ts +0 -10
  156. package/dist/postgres/pgDialect.js +0 -10
  157. package/dist/turso/tursoQuerier.d.ts +0 -10
  158. package/dist/turso/tursoQuerier.js +0 -10
@@ -10,10 +10,6 @@ import { derivedIndexName } from '../../util/sql.util.js';
10
10
  import { createSchemaGenerator } from '../schemaGenerator.js';
11
11
  import { splitSqlStatements } from './splitSqlStatements.js';
12
12
  import { TableBuilder } from './tableBuilder.js';
13
- /**
14
- * Builder for altering a table.
15
- * Delegates to parent builder for operation recording.
16
- */
17
13
  /**
18
14
  * One `createIndex` operation. Shared because the alter-table builder, the recorder and the
19
15
  * executing builder all record the same thing, and an entry left unnormalized reaches the generator
@@ -7,9 +7,9 @@ export { MariaIndexDdl, MySqlIndexDdl, MysqlLikeIndexDdl } from './mysqlIndexDdl
7
7
  export { CockroachIndexDdl, PgIndexDdl } from './pgIndexDdl.js';
8
8
  export { TableDdl } from './tableDdl.js';
9
9
  /**
10
- * The index DDL a dialect gets, most specific first. `instanceof` rather than `dialectName` so a
11
- * dialect subclassed by a user keeps its family's DDL, which is what overriding gave it while this
12
- * lived on the dialect itself. Anything else gets the portable form, which is SQLite's.
10
+ * The index DDL a dialect gets, most specific first. `instanceof` rather than the `dialectName`
11
+ * {@link tableDdlFor} reads, because each family's index DDL is typed to its dialect and the narrowing
12
+ * is what hands it one. Anything else gets the portable form, which is SQLite's.
13
13
  */
14
14
  export declare function indexDdlFor(dialect: AbstractSqlDialect): IndexDdl;
15
15
  /**
@@ -14,9 +14,9 @@ export { MariaIndexDdl, MySqlIndexDdl, MysqlLikeIndexDdl } from './mysqlIndexDdl
14
14
  export { CockroachIndexDdl, PgIndexDdl } from './pgIndexDdl.js';
15
15
  export { TableDdl } from './tableDdl.js';
16
16
  /**
17
- * The index DDL a dialect gets, most specific first. `instanceof` rather than `dialectName` so a
18
- * dialect subclassed by a user keeps its family's DDL, which is what overriding gave it while this
19
- * lived on the dialect itself. Anything else gets the portable form, which is SQLite's.
17
+ * The index DDL a dialect gets, most specific first. `instanceof` rather than the `dialectName`
18
+ * {@link tableDdlFor} reads, because each family's index DDL is typed to its dialect and the narrowing
19
+ * is what hands it one. Anything else gets the portable form, which is SQLite's.
20
20
  */
21
21
  export function indexDdlFor(dialect) {
22
22
  if (dialect instanceof CockroachDialect) {
@@ -105,14 +105,10 @@ export declare class Migrator {
105
105
  */
106
106
  private forceStatements;
107
107
  /**
108
- * Sync one entity, for a schema that grows while the process runs: a content type an admin just
109
- * created is one table to add, where the whole set would read the catalogue to work that out.
110
- *
111
- * The new-table case costs one existence check and creates with `IF NOT EXISTS`, so instances racing
112
- * the same admin save settle instead of colliding. An existing table still pays for introspection,
113
- * since a column diff needs the columns.
108
+ * The DDL for one entity: {@link planSync} narrowed to the table it names. A new table costs one
109
+ * existence check and is created with `IF NOT EXISTS`, so instances racing the same admin save
110
+ * settle instead of colliding; an existing one still pays for introspection, as a diff needs columns.
114
111
  */
115
- /** The DDL for one entity: {@link planSync} narrowed to the table it names. */
116
112
  private planEntity;
117
113
  /** An alter diff as statements, narrowed to what the caller allows. */
118
114
  private alterFromDiff;
@@ -345,14 +345,10 @@ export class Migrator {
345
345
  ];
346
346
  }
347
347
  /**
348
- * Sync one entity, for a schema that grows while the process runs: a content type an admin just
349
- * created is one table to add, where the whole set would read the catalogue to work that out.
350
- *
351
- * The new-table case costs one existence check and creates with `IF NOT EXISTS`, so instances racing
352
- * the same admin save settle instead of colliding. An existing table still pays for introspection,
353
- * since a column diff needs the columns.
348
+ * The DDL for one entity: {@link planSync} narrowed to the table it names. A new table costs one
349
+ * existence check and is created with `IF NOT EXISTS`, so instances racing the same admin save
350
+ * settle instead of colliding; an existing one still pays for introspection, as a diff needs columns.
354
351
  */
355
- /** The DDL for one entity: {@link planSync} narrowed to the table it names. */
356
352
  async planEntity(entity, options) {
357
353
  const meta = getMeta(entity);
358
354
  // Before anything reads `this.generator`, whose own failure names neither the entity nor the
@@ -25,9 +25,6 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
25
25
  resolveColumnName(key: string, field: FieldOptions): string;
26
26
  /** Escape an identifier (table name, column name, etc.) */
27
27
  protected escapeId(identifier: string): string;
28
- /**
29
- * Primary key type for auto-increment integer IDs
30
- */
31
28
  /**
32
29
  * How an auto-increment key of `type` is spelled: the type as any other column renders it, plus what
33
30
  * the engine appends to make it generated.
@@ -128,9 +125,6 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
128
125
  name: string;
129
126
  comment?: string;
130
127
  }, schema?: string): string[];
131
- /**
132
- * Compare an entity with a database table node and return the differences.
133
- */
134
128
  /**
135
129
  * How this entity differs from the table the database reported, as the migrator's `SchemaDiff`.
136
130
  *
@@ -139,13 +133,6 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
139
133
  * table node first, and types are compared as the *engine* would store them - see `normalizeType`.
140
134
  */
141
135
  diffSchema<E>(entity: Type<E>, currentTable: TableNode | undefined, desiredAst?: SchemaAST): SchemaDiff | undefined;
142
- /**
143
- * What the shared differ needs from a dialect: a type as this engine would actually store it.
144
- *
145
- * `boolean` is `TINYINT(1)` on MySQL and `INTEGER` on SQLite, so two canonical types that differ on
146
- * paper can be one column in the database. Round-tripping through the engine's own spelling is what
147
- * stops every such column reporting an alteration on every sync.
148
- */
149
136
  /**
150
137
  * Indexes the entity declares that the table does not already have, in any shape.
151
138
  *
@@ -50,9 +50,6 @@ export class SqlSchemaGenerator {
50
50
  escapeId(identifier) {
51
51
  return this.dialect.escapeId(identifier);
52
52
  }
53
- /**
54
- * Primary key type for auto-increment integer IDs
55
- */
56
53
  /**
57
54
  * How an auto-increment key of `type` is spelled: the type as any other column renders it, plus what
58
55
  * the engine appends to make it generated.
@@ -361,9 +358,6 @@ export class SqlSchemaGenerator {
361
358
  const tableRef = this.dialect.escapeQualifiedId(tableName, schema);
362
359
  return [`COMMENT ON COLUMN ${tableRef}.${this.escapeId(column.name)} IS ${this.dialect.escape(column.comment)};`];
363
360
  }
364
- /**
365
- * Compare an entity with a database table node and return the differences.
366
- */
367
361
  /**
368
362
  * How this entity differs from the table the database reported, as the migrator's `SchemaDiff`.
369
363
  *
@@ -447,13 +441,6 @@ export class SqlSchemaGenerator {
447
441
  foreignKeysToAlter: foreignKeysToAlter.length ? foreignKeysToAlter : undefined,
448
442
  };
449
443
  }
450
- /**
451
- * What the shared differ needs from a dialect: a type as this engine would actually store it.
452
- *
453
- * `boolean` is `TINYINT(1)` on MySQL and `INTEGER` on SQLite, so two canonical types that differ on
454
- * paper can be one column in the database. Round-tripping through the engine's own spelling is what
455
- * stops every such column reporting an alteration on every sync.
456
- */
457
444
  /**
458
445
  * Indexes the entity declares that the table does not already have, in any shape.
459
446
  *
@@ -1,10 +1,10 @@
1
1
  import type { MigrationStorage, QuerierPool, SqlQuerier } from '../../type/index.js';
2
+ /** Where executed migrations are recorded when the config does not name a table. */
3
+ export declare const DEFAULT_MIGRATIONS_TABLE = "uql_migrations";
2
4
  /**
3
5
  * Stores migration state in a database table.
4
6
  * Uses the querier's dialect for escaping and placeholders.
5
7
  */
6
- /** Where executed migrations are recorded when the config does not name a table. */
7
- export declare const DEFAULT_MIGRATIONS_TABLE = "uql_migrations";
8
8
  export declare class DatabaseMigrationStorage implements MigrationStorage {
9
9
  private readonly pool;
10
10
  private readonly tableName;
@@ -1,10 +1,10 @@
1
1
  import { withSqlQuerierForMigrations } from '../acquireQuerierForMigrations.js';
2
+ /** Where executed migrations are recorded when the config does not name a table. */
3
+ export const DEFAULT_MIGRATIONS_TABLE = 'uql_migrations';
2
4
  /**
3
5
  * Stores migration state in a database table.
4
6
  * Uses the querier's dialect for escaping and placeholders.
5
7
  */
6
- /** Where executed migrations are recorded when the config does not name a table. */
7
- export const DEFAULT_MIGRATIONS_TABLE = 'uql_migrations';
8
8
  export class DatabaseMigrationStorage {
9
9
  pool;
10
10
  tableName;
@@ -1,5 +1,4 @@
1
1
  export { MongoSchemaGenerator } from '../migrate/generator/mongoSchemaGenerator.js';
2
2
  export * from './mongoDialect.js';
3
- export * from './mongodbNativeDialect.js';
4
3
  export * from './mongodbQuerier.js';
5
4
  export * from './mongodbQuerierPool.js';
@@ -1,5 +1,4 @@
1
1
  export { MongoSchemaGenerator } from '../migrate/generator/mongoSchemaGenerator.js';
2
2
  export * from './mongoDialect.js';
3
- export * from './mongodbNativeDialect.js';
4
3
  export * from './mongodbQuerier.js';
5
4
  export * from './mongodbQuerierPool.js';
@@ -20,7 +20,6 @@ export declare class MongoDialect extends AbstractDialect {
20
20
  private static readonly ID_KEY;
21
21
  /** Atlas rejects a `$vectorSearch` asking for more candidates than this. */
22
22
  private static readonly MAX_NUM_CANDIDATES;
23
- private static readonly AGGREGATE_OP_MAP;
24
23
  /**
25
24
  * MongoDB stores the primary key as `_id`; everything else resolves as usual. Projections, sorts,
26
25
  * `$group` refs and `$where` keys all map through here, so no read path can address a property name
@@ -39,14 +39,6 @@ export class MongoDialect extends AbstractDialect {
39
39
  static ID_KEY = '_id';
40
40
  /** Atlas rejects a `$vectorSearch` asking for more candidates than this. */
41
41
  static MAX_NUM_CANDIDATES = 10_000;
42
- // Direct field aggregates → MongoDB accumulator. `$count` is handled separately (COUNT(*) vs
43
- // COUNT(field) differ), so it is not listed here.
44
- static AGGREGATE_OP_MAP = new Map([
45
- ['$sum', '$sum'],
46
- ['$avg', '$avg'],
47
- ['$min', '$min'],
48
- ['$max', '$max'],
49
- ]);
50
42
  /**
51
43
  * MongoDB stores the primary key as `_id`; everything else resolves as usual. Projections, sorts,
52
44
  * `$group` refs and `$where` keys all map through here, so no read path can address a property name
@@ -511,7 +503,7 @@ export class MongoDialect extends AbstractDialect {
511
503
  */
512
504
  aliasSort(sort) {
513
505
  const normalized = {};
514
- for (const [alias, dir] of Object.entries(sort ?? {})) {
506
+ for (const [alias, dir] of Object.entries(sort)) {
515
507
  normalized[alias] = sortDirection(dir);
516
508
  }
517
509
  return normalized;
@@ -999,11 +991,8 @@ export class MongoDialect extends AbstractDialect {
999
991
  entry.fieldRef === '*' ? { $sum: 1 } : { $sum: { $cond: [{ $ne: [ref, null] }, 1, 0] } };
1000
992
  }
1001
993
  else {
1002
- const mongoOp = MongoDialect.AGGREGATE_OP_MAP.get(entry.op);
1003
- if (!mongoOp) {
1004
- throw TypeError(`unsupported aggregate operator: ${entry.op}`);
1005
- }
1006
- groupAccumulators[entry.alias] = { [mongoOp]: ref };
994
+ // `$sum`, `$avg`, `$min` and `$max` are MongoDB accumulators of the same name.
995
+ groupAccumulators[entry.alias] = { [entry.op]: ref };
1007
996
  }
1008
997
  }
1009
998
  return { groupId, groupAccumulators, distinctReducers };
@@ -1,6 +1,6 @@
1
1
  import type { Document, MongoClient } from 'mongodb';
2
2
  import { AbstractQuerier } from '../querier/index.js';
3
- import type { EntityData, ExtraOptions, IdValue, PrimaryKey, Query, QueryAggMap, QueryAggregate, QueryAggregateResult, QueryConflictPaths, QueryFilter, QueryGroupMap, QueryOptions, QuerySearch, TransactionOptions, Type, UpdatePayload } from '../type/index.js';
3
+ import type { EntityData, ExtraOptions, PrimaryKey, Query, QueryAggMap, QueryAggregate, QueryAggregateResult, QueryConflictPaths, QueryFilter, QueryGroupMap, QueryOptions, QuerySearch, TransactionOptions, Type, UpdatePayload } from '../type/index.js';
4
4
  import { type ParentPartition } from '../util/index.js';
5
5
  import type { MongoDialect } from './mongoDialect.js';
6
6
  export declare class MongodbQuerier extends AbstractQuerier {
@@ -56,7 +56,7 @@ export declare class MongodbQuerier extends AbstractQuerier {
56
56
  * the floor, and skipped the relation-sort rejection {@link MongoDialect.sort} raises.
57
57
  */
58
58
  private settleIds;
59
- internalInsertMany<E extends Document>(entity: Type<E>, payloads: EntityData<E>[]): Promise<IdValue<E>[]>;
59
+ internalInsertMany<E extends Document>(entity: Type<E>, rows: EntityData<E>[]): Promise<void>;
60
60
  internalUpdateMany<E extends Document>(entity: Type<E>, qm: QuerySearch<E>, payload: UpdatePayload<E>, opts?: QueryOptions): Promise<number>;
61
61
  /**
62
62
  * `_id` is immutable, so a key the payload names can only be written on the insert branch of an
@@ -65,18 +65,16 @@ export declare class MongodbQuerier extends AbstractQuerier {
65
65
  private upsertUpdate;
66
66
  private buildConflictFilter;
67
67
  protected internalUpsertOne<E extends Document>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityData<E>): Promise<{
68
- firstId: PrimaryKey | undefined;
68
+ ids: (PrimaryKey | undefined)[];
69
69
  changes: number;
70
70
  created: boolean;
71
71
  }>;
72
72
  protected internalUpsertMany<E extends Document>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityData<E>[]): Promise<{
73
73
  changes: number;
74
74
  ids?: undefined;
75
- firstId?: undefined;
76
75
  } | {
77
76
  changes: number;
78
77
  ids: (PrimaryKey | undefined)[];
79
- firstId: PrimaryKey | undefined;
80
78
  }>;
81
79
  protected internalDeleteMany<E extends Document>(entity: Type<E>, qm: QuerySearch<E>, opts?: QueryOptions): Promise<number>;
82
80
  get hasOpenTransaction(): boolean;
@@ -1,6 +1,6 @@
1
1
  import { COUNT_ALIAS } from '../dialect/aliases.js';
2
2
  import { hasRequiredJoin } from '../dialect/queryJoins.js';
3
- import { getMeta, idOf, soleIdOf } from '../entity/index.js';
3
+ import { fieldOf, getMeta, idOf, namesKey, soleIdOf } from '../entity/index.js';
4
4
  import { AbstractQuerier, enrichError } from '../querier/index.js';
5
5
  import { clone, getKeys, getRelationRequestSummary, getSoftDeleteValue, hasKeys, idOnlyQuery, isPagedQuery, populatesRelations, queryChildrenOf, throwNoPendingTransaction, throwPendingTransaction, withoutSoftDeleteFilter, } from '../util/index.js';
6
6
  /**
@@ -259,22 +259,17 @@ export class MongodbQuerier extends AbstractQuerier {
259
259
  // are named the same way every other driver names them.
260
260
  return (this.dialect.normalizeIds(meta, founds) || []).map((found) => idOf(meta, found));
261
261
  }
262
- async internalInsertMany(entity, payloads) {
262
+ async internalInsertMany(entity, rows) {
263
263
  return this.timed('internalInsertMany', undefined, async () => {
264
- if (!payloads?.length) {
265
- return [];
266
- }
267
- payloads = clone(payloads);
268
264
  const meta = getMeta(entity);
269
- const persistables = this.dialect.getPersistables(meta, payloads, 'onInsert');
265
+ const persistables = this.dialect.getPersistables(meta, rows, 'onInsert');
270
266
  const { insertedIds } = await this.execute((session) => this.collection(entity).insertMany(persistables, { session }));
271
267
  const ids = Object.values(insertedIds).map((id) => this.dialect.fromWireId(id));
272
268
  const idKey = soleIdOf(meta, 'insert');
273
- for (const [index, it] of payloads.entries()) {
274
- it[idKey] = ids[index];
269
+ for (let index = 0; index < rows.length; index++) {
270
+ rows[index][idKey] = ids[index];
275
271
  }
276
- await this.insertRelations(entity, payloads);
277
- return ids;
272
+ await this.insertRelations(entity, rows);
278
273
  });
279
274
  }
280
275
  async internalUpdateMany(entity, qm, payload, opts) {
@@ -332,10 +327,11 @@ export class MongodbQuerier extends AbstractQuerier {
332
327
  includeResultMetadata: true,
333
328
  session,
334
329
  }));
335
- const firstId = this.dialect.fromWireId(res?.value?._id);
330
+ // Read off the document as written, which carries its `_id` on either branch.
331
+ const id = this.dialect.fromWireId(res?.value?._id);
336
332
  // `updatedExisting` is false when a new document was inserted (upserted).
337
333
  const created = res?.lastErrorObject?.['updatedExisting'] === false;
338
- return { firstId, changes: firstId ? 1 : 0, created };
334
+ return { ids: [id], changes: id === undefined ? 0 : 1, created };
339
335
  });
340
336
  }
341
337
  async internalUpsertMany(entity, conflictPaths, payload) {
@@ -343,8 +339,10 @@ export class MongodbQuerier extends AbstractQuerier {
343
339
  if (!payload?.length) {
344
340
  return { changes: 0 };
345
341
  }
346
- payload = clone(payload);
347
342
  const meta = getMeta(entity);
343
+ // Asked before `getPersistable` fills an `onInsert` key into rows it may only update.
344
+ const unnamed = payload.map((row) => !namesKey(meta, row));
345
+ payload = clone(payload);
348
346
  const operations = payload.map((item) => {
349
347
  const persistable = this.dialect.getPersistable(meta, item, 'onInsert');
350
348
  const filter = this.buildConflictFilter(entity, conflictPaths, item);
@@ -359,23 +357,24 @@ export class MongodbQuerier extends AbstractQuerier {
359
357
  });
360
358
  const res = await this.execute((session) => this.collection(entity).bulkWrite(operations, { session }));
361
359
  const changes = (res.upsertedCount ?? 0) + (res.modifiedCount ?? 0);
362
- // `upsertedIds` only covers newly-inserted documents, keyed by operation index: a matched and
363
- // updated document's `_id` is not in the response. Read by that index rather than flattened,
364
- // so each id lands on the row it belongs to and the gaps stay gaps.
365
- const ids = payload.map((_, index) => {
360
+ // `upsertedIds` names only the documents inserted, keyed by operation index, so each lands on
361
+ // its own row; an updated document's `_id` is read back by the conflict fields instead.
362
+ const reported = payload.map((_, index) => {
366
363
  const id = res.upsertedIds[index];
367
364
  return id === undefined ? undefined : this.dialect.fromWireId(id);
368
365
  });
369
- return { changes, ids, firstId: ids.find((id) => id !== undefined) };
366
+ const unplaced = reported.some((id, index) => id === undefined && unnamed[index]);
367
+ const found = unplaced ? await this.idsByConflict(entity, conflictPaths, payload) : [];
368
+ return { changes, ids: reported.map((id, index) => id ?? found[index]) };
370
369
  });
371
370
  }
372
371
  async internalDeleteMany(entity, qm, opts = {}) {
373
372
  return this.timed('internalDeleteMany', undefined, async () => {
374
373
  const meta = getMeta(entity);
375
374
  // Soft-delete (stamp) unless `hardDelete` is requested or the entity has no soft-delete field.
376
- const field = !opts.hardDelete && meta.softDelete ? meta.fields[meta.softDelete] : undefined;
375
+ const softDelete = opts.hardDelete ? undefined : meta.softDelete;
377
376
  // Hard delete targets matching rows regardless of soft-delete state (keeps other filters).
378
- const findOpts = field ? opts : { ...opts, filters: withoutSoftDeleteFilter(opts.filters) };
377
+ const findOpts = softDelete ? opts : { ...opts, filters: withoutSoftDeleteFilter(opts.filters) };
379
378
  // Delete has always resolved its ids first (it stamps or removes them by `_id`), so a relation
380
379
  // condition needs nothing extra here - and passing the whole query is what makes its page apply.
381
380
  const ids = await this.settleIds(entity, qm, findOpts);
@@ -383,10 +382,11 @@ export class MongodbQuerier extends AbstractQuerier {
383
382
  return 0;
384
383
  }
385
384
  let changes;
386
- if (field) {
385
+ if (softDelete) {
386
+ const field = fieldOf(meta, softDelete);
387
387
  // Stamp the mapped column: reads filter on it, so a `@Field({ name })` mismatch here would
388
388
  // report a successful delete and leave the row visible.
389
- const softDeleteColumn = this.dialect.resolveColumnName(meta.softDelete, field);
389
+ const softDeleteColumn = this.dialect.resolveColumnName(softDelete, field);
390
390
  const updateResult = await this.execute((session) => this.collection(entity).updateMany({ _id: { $in: this.dialect.toWireId(ids) } }, { $set: { [softDeleteColumn]: getSoftDeleteValue(field) } }, {
391
391
  session,
392
392
  }));
@@ -1,9 +1,9 @@
1
1
  import { type MongoClientOptions } from 'mongodb';
2
2
  import { AbstractQuerierPool } from '../querier/index.js';
3
3
  import type { ExtraOptions } from '../type/index.js';
4
- import { MongodbNativeDialect } from './mongodbNativeDialect.js';
4
+ import { MongoDialect } from './mongoDialect.js';
5
5
  import { MongodbQuerier } from './mongodbQuerier.js';
6
- export declare class MongodbQuerierPool extends AbstractQuerierPool<MongodbQuerier, MongodbNativeDialect> {
6
+ export declare class MongodbQuerierPool extends AbstractQuerierPool<MongodbQuerier, MongoDialect> {
7
7
  private readonly client;
8
8
  constructor(uri: string, opts?: MongoClientOptions, extra?: ExtraOptions);
9
9
  getQuerier(): Promise<MongodbQuerier>;
@@ -1,12 +1,12 @@
1
1
  import { MongoClient } from 'mongodb';
2
2
  import { dialectOptionsFrom } from '../dialect/abstractDialect.js';
3
3
  import { AbstractQuerierPool } from '../querier/index.js';
4
- import { MongodbNativeDialect } from './mongodbNativeDialect.js';
4
+ import { MongoDialect } from './mongoDialect.js';
5
5
  import { MongodbQuerier } from './mongodbQuerier.js';
6
6
  export class MongodbQuerierPool extends AbstractQuerierPool {
7
7
  client;
8
8
  constructor(uri, opts, extra) {
9
- super(new MongodbNativeDialect(dialectOptionsFrom(extra)), extra);
9
+ super(new MongoDialect(dialectOptionsFrom(extra)), extra);
10
10
  this.client = new MongoClient(uri, opts);
11
11
  }
12
12
  async getQuerier() {
@@ -18,18 +18,18 @@ export declare class MsSqlDialect extends MergeSqlDialect {
18
18
  readonly commitTransactionCommand = "COMMIT TRANSACTION";
19
19
  readonly rollbackTransactionCommand = "ROLLBACK TRANSACTION";
20
20
  /**
21
- * The level rides the `BEGIN` rather than preceding it as its own statement: a `SET TRANSACTION
22
- * ISOLATION LEVEL` sent on its own would go to whichever pooled connection served it, not the one
23
- * the transaction opens on, and would then stick to that connection for unrelated later queries.
24
- * `MsSqlQuerier` reads the level back off this command and hands it to the driver.
21
+ * T-SQL has no inline form, so the level is set before the `BEGIN`. What a driver that sends these
22
+ * as statements would run; `MsSqlQuerier` opens its transactions through the driver instead.
25
23
  */
26
- readonly isolationLevelStrategy = "inline";
24
+ readonly isolationLevelStrategy = "set-before";
27
25
  readonly dropIndexSyntax = "on-table";
28
26
  readonly booleanLiteral = "integer";
29
27
  /** [The hard server limit](https://github.com/yiisoft/yii2/issues/10371), not a driver preference. */
30
28
  readonly maxBindValues = 2100;
31
29
  /** `OUTPUT` has no trailing form: it sits between the column list and `VALUES`. */
32
30
  readonly returningPosition = "after-target";
31
+ /** Microsoft documents no row order for a `MERGE ... OUTPUT`. */
32
+ readonly upsertReturningOrdered = false;
33
33
  readonly insertIdSource: InsertIdSource;
34
34
  /** Holds the update key lock across the insert; without it two concurrent upserts of one key race. */
35
35
  protected readonly mergeTargetHint = " WITH (HOLDLOCK)";
@@ -52,18 +52,18 @@ export class MsSqlDialect extends MergeSqlDialect {
52
52
  commitTransactionCommand = 'COMMIT TRANSACTION';
53
53
  rollbackTransactionCommand = 'ROLLBACK TRANSACTION';
54
54
  /**
55
- * The level rides the `BEGIN` rather than preceding it as its own statement: a `SET TRANSACTION
56
- * ISOLATION LEVEL` sent on its own would go to whichever pooled connection served it, not the one
57
- * the transaction opens on, and would then stick to that connection for unrelated later queries.
58
- * `MsSqlQuerier` reads the level back off this command and hands it to the driver.
55
+ * T-SQL has no inline form, so the level is set before the `BEGIN`. What a driver that sends these
56
+ * as statements would run; `MsSqlQuerier` opens its transactions through the driver instead.
59
57
  */
60
- isolationLevelStrategy = 'inline';
58
+ isolationLevelStrategy = 'set-before';
61
59
  dropIndexSyntax = 'on-table';
62
60
  booleanLiteral = 'integer';
63
61
  /** [The hard server limit](https://github.com/yiisoft/yii2/issues/10371), not a driver preference. */
64
62
  maxBindValues = 2100;
65
63
  /** `OUTPUT` has no trailing form: it sits between the column list and `VALUES`. */
66
64
  returningPosition = 'after-target';
65
+ /** Microsoft documents no row order for a `MERGE ... OUTPUT`. */
66
+ upsertReturningOrdered = false;
67
67
  insertIdSource = 'returning';
68
68
  /** Holds the update key lock across the insert; without it two concurrent upserts of one key race. */
69
69
  mergeTargetHint = ' WITH (HOLDLOCK)';
@@ -268,11 +268,14 @@ export class MsSqlDialect extends MergeSqlDialect {
268
268
  return typeof value === 'boolean' ? `CAST(${placeholder} AS BIT)` : placeholder;
269
269
  }
270
270
  /**
271
- * An object or array bound as JSON, which is the half both binders spell the same way, or
272
- * `undefined` for a scalar - where they diverge.
271
+ * An object or array bound as JSON, or a `raw()` rendered in place, which is the half both binders
272
+ * spell the same way; `undefined` for a scalar - where they diverge.
273
273
  */
274
274
  #jsonCompound(ctx, value) {
275
- if (value === null || typeof value !== 'object' || value instanceof QueryRaw) {
275
+ if (value instanceof QueryRaw) {
276
+ return this.rawFragment(ctx, value);
277
+ }
278
+ if (value === null || typeof value !== 'object') {
276
279
  return undefined;
277
280
  }
278
281
  ctx.pushValue(JSON.stringify(value));
@@ -1,7 +1,6 @@
1
1
  import type { ConnectionPool } from 'mssql';
2
2
  import { AbstractPoolQuerier } from '../querier/abstractPoolQuerier.js';
3
- import type { ExtraOptions, QueryUpdateResult } from '../type/index.js';
4
- import type { MsSqlDialect } from './mssqlDialect.js';
3
+ import type { QueryUpdateResult, TransactionOptions } from '../type/index.js';
5
4
  /**
6
5
  * A connection is a `ConnectionPool` handle here rather than a checked-out socket: `mssql` owns its
7
6
  * own pool and hands out `Request`s, so what UQL holds is the pool plus, once a transaction opens,
@@ -9,15 +8,23 @@ import type { MsSqlDialect } from './mssqlDialect.js';
9
8
  */
10
9
  export declare class MsSqlQuerier extends AbstractPoolQuerier<ConnectionPool> {
11
10
  #private;
12
- constructor(connect: () => Promise<ConnectionPool>, dialect: MsSqlDialect, extra?: ExtraOptions);
13
11
  internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
14
12
  internalRun(query: string, values?: unknown[]): Promise<QueryUpdateResult>;
15
13
  /**
16
- * `tedious` streams by event, not by async iterator, so rows are handed over as they arrive rather
17
- * than collected first - buffering the whole result set would make this `all()` with extra steps.
18
- * The `query` promise is awaited at the end so its rejection surfaces rather than going unhandled.
14
+ * The driver's own stream over the request, which pauses the request while the loop is behind, so
15
+ * rows arrive only as fast as they are read. A failure the driver reports through the promise alone
16
+ * would leave the loop waiting for rows, so it ends the stream instead.
19
17
  */
20
18
  internalStream<T>(query: string, values?: unknown[]): AsyncGenerator<Awaited<T>, void, unknown>;
19
+ /**
20
+ * `mssql` owns its pool and hands out a `Request` per call, so a `BEGIN TRANSACTION` sent as text
21
+ * would open one on a connection the next call may not be given. Its `Transaction` object is the
22
+ * only thing that pins them together, so the transaction is that object rather than statements, and
23
+ * the level goes to its `begin`: sent as a statement it would land on whichever connection served it.
24
+ */
25
+ protected internalBegin(opts?: TransactionOptions): Promise<void>;
26
+ protected internalCommit(): Promise<void>;
27
+ protected internalRollback(): Promise<void>;
21
28
  /** The pool owns the socket; releasing a querier only drops this one's claim on it. */
22
29
  protected releaseConn(_conn: ConnectionPool, _discard: boolean): Promise<void>;
23
30
  }