uql-orm 0.66.0 → 0.67.1

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 (193) hide show
  1. package/dist/browser/querier/httpQuerier.js +1 -8
  2. package/dist/browser/uql-browser.min.js.map +5 -5
  3. package/dist/bunSql/bunSql.util.d.ts +2 -6
  4. package/dist/bunSql/bunSql.util.js +2 -6
  5. package/dist/bunSql/bunSqlQuerier.d.ts +2 -5
  6. package/dist/bunSql/bunSqlQuerier.js +2 -5
  7. package/dist/cockroachdb/cockroachDialect.d.ts +4 -13
  8. package/dist/cockroachdb/cockroachDialect.js +4 -13
  9. package/dist/context/context.browser.js +2 -10
  10. package/dist/context/context.d.ts +4 -17
  11. package/dist/context/context.js +4 -17
  12. package/dist/dialect/abstractDialect.d.ts +4 -19
  13. package/dist/dialect/abstractDialect.js +2 -20
  14. package/dist/dialect/abstractSqlDialect.d.ts +47 -212
  15. package/dist/dialect/abstractSqlDialect.js +68 -222
  16. package/dist/dialect/aliases.d.ts +2 -12
  17. package/dist/dialect/aliases.js +4 -12
  18. package/dist/dialect/hydrateColumn.d.ts +2 -6
  19. package/dist/dialect/hydrateColumn.js +3 -13
  20. package/dist/dialect/jsonArrayElemMatchUtils.d.ts +1 -7
  21. package/dist/dialect/jsonArrayElemMatchUtils.js +1 -7
  22. package/dist/dialect/jsonSql.d.ts +6 -27
  23. package/dist/dialect/jsonSql.js +6 -27
  24. package/dist/dialect/mergeSqlDialect.d.ts +4 -22
  25. package/dist/dialect/mergeSqlDialect.js +4 -22
  26. package/dist/dialect/mysqlLikeSqlDialect.d.ts +11 -37
  27. package/dist/dialect/mysqlLikeSqlDialect.js +35 -51
  28. package/dist/dialect/pgLikeSqlDialect.d.ts +8 -22
  29. package/dist/dialect/pgLikeSqlDialect.js +36 -39
  30. package/dist/dialect/queryContext.d.ts +4 -22
  31. package/dist/dialect/queryContext.js +4 -22
  32. package/dist/dialect/queryJoins.d.ts +3 -12
  33. package/dist/dialect/queryJoins.js +3 -12
  34. package/dist/dialect/vectorCast.d.ts +2 -12
  35. package/dist/dialect/vectorCast.js +3 -19
  36. package/dist/dialect/vectorSqlDialect.d.ts +8 -38
  37. package/dist/dialect/vectorSqlDialect.js +7 -38
  38. package/dist/entity/decorator/bag.d.ts +6 -19
  39. package/dist/entity/decorator/bag.js +6 -22
  40. package/dist/entity/decorator/entity.d.ts +2 -7
  41. package/dist/entity/decorator/entity.js +2 -7
  42. package/dist/entity/decorator/members.d.ts +7 -30
  43. package/dist/entity/decorator/members.js +3 -12
  44. package/dist/entity/metadata/definition.d.ts +2 -18
  45. package/dist/entity/metadata/definition.js +6 -28
  46. package/dist/http/handler.d.ts +2 -14
  47. package/dist/index.d.ts +4 -1
  48. package/dist/index.js +3 -1
  49. package/dist/libsql/libsqlDialect.d.ts +1 -8
  50. package/dist/libsql/libsqlDialect.js +1 -8
  51. package/dist/maria/mariaDialect.d.ts +3 -5
  52. package/dist/maria/mariaDialect.js +5 -5
  53. package/dist/maria/mariadbQuerier.js +2 -2
  54. package/dist/maria/mariadbQuerierPool.js +1 -6
  55. package/dist/migrate/builder/migrationBuilder.js +3 -19
  56. package/dist/migrate/builder/splitSqlStatements.d.ts +1 -14
  57. package/dist/migrate/builder/splitSqlStatements.js +2 -22
  58. package/dist/migrate/builder/types.d.ts +2 -15
  59. package/dist/migrate/cli-config.js +2 -11
  60. package/dist/migrate/cli.js +2 -7
  61. package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -15
  62. package/dist/migrate/codegen/entityCodeGenerator.js +15 -44
  63. package/dist/migrate/codegen/fieldOptionsSource.d.ts +1 -8
  64. package/dist/migrate/codegen/fieldOptionsSource.js +3 -22
  65. package/dist/migrate/ddl/indexDdl.d.ts +2 -5
  66. package/dist/migrate/ddl/indexDdl.js +2 -5
  67. package/dist/migrate/ddl/pgIndexDdl.d.ts +3 -13
  68. package/dist/migrate/ddl/pgIndexDdl.js +3 -13
  69. package/dist/migrate/generator/definitionToNode.d.ts +2 -9
  70. package/dist/migrate/generator/definitionToNode.js +3 -17
  71. package/dist/migrate/generator/indexNodeToSchema.d.ts +2 -3
  72. package/dist/migrate/generator/indexNodeToSchema.js +2 -3
  73. package/dist/migrate/generator/mongoCommand.d.ts +1 -8
  74. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +1 -8
  75. package/dist/migrate/generator/mongoSchemaGenerator.js +1 -8
  76. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +6 -26
  77. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +9 -41
  78. package/dist/migrate/introspection/baseSqlIntrospector.js +0 -1
  79. package/dist/migrate/introspection/mongoIntrospector.d.ts +3 -1
  80. package/dist/migrate/introspection/mongoIntrospector.js +48 -46
  81. package/dist/migrate/introspection/mssqlIntrospector.d.ts +4 -4
  82. package/dist/migrate/introspection/mssqlIntrospector.js +18 -27
  83. package/dist/migrate/introspection/mysqlIntrospector.d.ts +7 -2
  84. package/dist/migrate/introspection/mysqlIntrospector.js +16 -14
  85. package/dist/migrate/introspection/postgresIntrospector.d.ts +24 -9
  86. package/dist/migrate/introspection/postgresIntrospector.js +68 -59
  87. package/dist/migrate/introspection/sqliteIntrospector.d.ts +1 -1
  88. package/dist/migrate/introspection/sqliteIntrospector.js +8 -10
  89. package/dist/migrate/migrator.d.ts +9 -53
  90. package/dist/migrate/migrator.js +32 -65
  91. package/dist/migrate/schemaGenerator.d.ts +16 -66
  92. package/dist/migrate/schemaGenerator.js +21 -74
  93. package/dist/mongo/mongoDialect.d.ts +21 -53
  94. package/dist/mongo/mongoDialect.js +25 -70
  95. package/dist/mongo/mongodbQuerier.d.ts +5 -8
  96. package/dist/mongo/mongodbQuerier.js +31 -65
  97. package/dist/mssql/mssqlDialect.d.ts +8 -34
  98. package/dist/mssql/mssqlDialect.js +37 -51
  99. package/dist/mssql/mssqlQuerier.d.ts +37 -4
  100. package/dist/mssql/mssqlQuerier.js +2 -2
  101. package/dist/mssql/mssqlWireTypes.d.ts +2 -14
  102. package/dist/mssql/mssqlWireTypes.js +2 -14
  103. package/dist/nestjs/uqlModule.js +2 -7
  104. package/dist/pglite/pgliteQuerier.d.ts +1 -9
  105. package/dist/pglite/pgliteQuerierPool.d.ts +4 -26
  106. package/dist/pglite/pgliteQuerierPool.js +3 -18
  107. package/dist/postgres/abstractPgQuerierPool.d.ts +1 -8
  108. package/dist/postgres/abstractPgQuerierPool.js +1 -8
  109. package/dist/postgres/pgNumericTypes.d.ts +3 -26
  110. package/dist/postgres/pgNumericTypes.js +3 -26
  111. package/dist/postgres/postgresDialect.d.ts +4 -10
  112. package/dist/postgres/postgresDialect.js +4 -10
  113. package/dist/querier/abstractQuerier.d.ts +35 -103
  114. package/dist/querier/abstractQuerier.js +105 -201
  115. package/dist/querier/abstractSharedHandleQuerierPool.d.ts +3 -17
  116. package/dist/querier/abstractSharedHandleQuerierPool.js +3 -17
  117. package/dist/querier/abstractSqlQuerier.d.ts +15 -36
  118. package/dist/querier/abstractSqlQuerier.js +49 -131
  119. package/dist/schema/canonicalType.d.ts +3 -21
  120. package/dist/schema/canonicalType.js +22 -67
  121. package/dist/schema/dependencyGraph.d.ts +2 -8
  122. package/dist/schema/dependencyGraph.js +2 -32
  123. package/dist/schema/index.d.ts +1 -25
  124. package/dist/schema/index.js +0 -26
  125. package/dist/schema/indexColumns.d.ts +1 -8
  126. package/dist/schema/indexColumns.js +1 -8
  127. package/dist/schema/indexDifferences.d.ts +7 -40
  128. package/dist/schema/indexDifferences.js +6 -31
  129. package/dist/schema/schemaAST.d.ts +8 -175
  130. package/dist/schema/schemaAST.js +13 -365
  131. package/dist/schema/schemaASTBuilder.d.ts +2 -24
  132. package/dist/schema/schemaASTBuilder.js +2 -30
  133. package/dist/schema/schemaASTDiffer.d.ts +6 -46
  134. package/dist/schema/schemaASTDiffer.js +8 -56
  135. package/dist/schema/types.d.ts +5 -61
  136. package/dist/schema/types.js +3 -6
  137. package/dist/sqlite/abstractSqliteQuerier.d.ts +1 -8
  138. package/dist/sqlite/localSqliteQuerierPool.d.ts +1 -7
  139. package/dist/sqlite/localSqliteQuerierPool.js +1 -7
  140. package/dist/sqlite/nodeSqliteQuerierPool.d.ts +2 -7
  141. package/dist/sqlite/nodeSqliteQuerierPool.js +2 -7
  142. package/dist/sqlite/sqliteDialect.d.ts +5 -20
  143. package/dist/sqlite/sqliteDialect.js +29 -35
  144. package/dist/turso/tursoDialect.d.ts +4 -6
  145. package/dist/turso/tursoDialect.js +4 -6
  146. package/dist/turso/tursoLocalQuerierPool.d.ts +1 -7
  147. package/dist/turso/tursoLocalQuerierPool.js +1 -7
  148. package/dist/turso/tursoQuerierPool.d.ts +2 -6
  149. package/dist/turso/tursoQuerierPool.js +2 -6
  150. package/dist/turso/tursoSessionQuerier.d.ts +1 -7
  151. package/dist/turso/tursoSessionQuerier.js +1 -7
  152. package/dist/type/dialect.d.ts +42 -94
  153. package/dist/type/dialect.js +3 -13
  154. package/dist/type/entity.d.ts +163 -534
  155. package/dist/type/entity.js +26 -9
  156. package/dist/type/logger.d.ts +2 -14
  157. package/dist/type/migration.d.ts +9 -38
  158. package/dist/type/querier.d.ts +9 -28
  159. package/dist/type/querierPool.d.ts +4 -26
  160. package/dist/type/query.d.ts +19 -73
  161. package/dist/type/query.js +2 -7
  162. package/dist/type/queryAggregate.d.ts +18 -98
  163. package/dist/type/queryRaw.d.ts +1 -8
  164. package/dist/type/queryRaw.js +1 -8
  165. package/dist/type/queryWhere.d.ts +13 -61
  166. package/dist/type/universalQuerier.d.ts +18 -105
  167. package/dist/type/utility.d.ts +12 -24
  168. package/dist/type/vector.d.ts +8 -38
  169. package/dist/type/vector.js +1 -1
  170. package/dist/type/wire.d.ts +2 -5
  171. package/dist/util/dialect.util.d.ts +9 -27
  172. package/dist/util/dialect.util.js +10 -27
  173. package/dist/util/field.util.d.ts +2 -31
  174. package/dist/util/field.util.js +3 -43
  175. package/dist/util/fieldOption.util.d.ts +7 -15
  176. package/dist/util/fieldOption.util.js +1 -1
  177. package/dist/util/filters.util.d.ts +2 -5
  178. package/dist/util/filters.util.js +2 -5
  179. package/dist/util/logger.d.ts +2 -6
  180. package/dist/util/logger.js +2 -6
  181. package/dist/util/object.util.d.ts +2 -6
  182. package/dist/util/object.util.js +1 -5
  183. package/dist/util/raw.d.ts +3 -23
  184. package/dist/util/relationQuery.util.d.ts +3 -14
  185. package/dist/util/relationQuery.util.js +3 -14
  186. package/dist/util/rowKey.util.d.ts +2 -10
  187. package/dist/util/rowKey.util.js +2 -10
  188. package/dist/util/sql.util.d.ts +6 -37
  189. package/dist/util/sql.util.js +13 -73
  190. package/dist/util/sqlLiteral.d.ts +2 -13
  191. package/dist/util/sqlLiteral.js +8 -13
  192. package/dist/util/string.util.js +0 -2
  193. package/package.json +4 -4
@@ -3,20 +3,8 @@ type ColumnTypes = Record<string, {
3
3
  readonly type: unknown;
4
4
  }>;
5
5
  /**
6
- * Decode `BIGINT` as a JS number, leaving every other type to the driver.
7
- *
8
- * uql owes this to the caller because uql picks the column: `type: Number` maps to BIGINT (see
9
- * `schema/canonicalType.ts`), and `tedious` hands that back as a string to protect the digits past
10
- * 2^53 - so without this a field declared `number` reads back as `'9'`, including every generated
11
- * primary key on every entity.
12
- *
13
- * At the wire, for the reason `pgNumericTypes` gives: everything crosses it exactly once - entity
14
- * reads, the ids an `OUTPUT` reports, raw SQL, counts, aggregates - where the ORM's own hydration
15
- * only ever sees entity reads.
16
- *
17
- * By the rule every driver here shares, `decodeWideNumber`: a number where one is exact, the driver's
18
- * exact text past 2^53. The lighter escape hatch for a column that big is the declaration -
19
- * `@Field({ type: String, columnType: 'bigint' })`.
6
+ * Decodes `BIGINT`, which `tedious` returns as text, by `decodeWideNumber`: `type: Number` maps to
7
+ * BIGINT, so otherwise every generated key reads back as a string. At the wire, which every result crosses.
20
8
  */
21
9
  export declare function decodeWireTypes<T>(rows: T[] | undefined, columns: ColumnTypes | undefined): T[];
22
10
  export {};
@@ -1,19 +1,7 @@
1
1
  import { decodeWideNumber } from '../util/wideNumber.js';
2
2
  /**
3
- * Decode `BIGINT` as a JS number, leaving every other type to the driver.
4
- *
5
- * uql owes this to the caller because uql picks the column: `type: Number` maps to BIGINT (see
6
- * `schema/canonicalType.ts`), and `tedious` hands that back as a string to protect the digits past
7
- * 2^53 - so without this a field declared `number` reads back as `'9'`, including every generated
8
- * primary key on every entity.
9
- *
10
- * At the wire, for the reason `pgNumericTypes` gives: everything crosses it exactly once - entity
11
- * reads, the ids an `OUTPUT` reports, raw SQL, counts, aggregates - where the ORM's own hydration
12
- * only ever sees entity reads.
13
- *
14
- * By the rule every driver here shares, `decodeWideNumber`: a number where one is exact, the driver's
15
- * exact text past 2^53. The lighter escape hatch for a column that big is the declaration -
16
- * `@Field({ type: String, columnType: 'bigint' })`.
3
+ * Decodes `BIGINT`, which `tedious` returns as text, by `decodeWideNumber`: `type: Number` maps to
4
+ * BIGINT, so otherwise every generated key reads back as a string. At the wire, which every result crosses.
17
5
  */
18
6
  export function decodeWireTypes(rows, columns) {
19
7
  if (!rows?.length || !columns) {
@@ -43,13 +43,8 @@ import { UqlContextInterceptor } from './uqlContextInterceptor.js';
43
43
  */
44
44
  export const UQL_QUERIER_POOL = Symbol('UQL_QUERIER_POOL');
45
45
  /**
46
- * Ends the pool when Nest shuts down.
47
- *
48
- * @remarks A provider rather than a hook on the module class, and built through `useFactory` with an
49
- * `inject` list rather than constructor injection: Nest injects constructor parameters with a parameter
50
- * decorator, and the TC39 decorator spec has none, so `@Inject()` cannot appear in a file compiled
51
- * against it. Nest runs lifecycle hooks on providers too, so this keeps the pool that *this* module was
52
- * configured with instead of reaching for the global default.
46
+ * Ends the pool this module was configured with when Nest shuts down: a provider built by `useFactory`,
47
+ * since TC39 decorators have no parameter decorator for `@Inject()`.
53
48
  */
54
49
  class UqlPoolLifecycle {
55
50
  pool;
@@ -1,15 +1,7 @@
1
1
  import type { PostgresDialect } from '../postgres/postgresDialect.js';
2
2
  import { AbstractSqlQuerier } from '../querier/index.js';
3
3
  import type { ExtraOptions } from '../type/index.js';
4
- /**
5
- * Structural subset of the `@electric-sql/pglite` API actually used here, declared locally so this
6
- * package does not couple its published types to a pre-1.0 dependency.
7
- *
8
- * @remarks This is what uql *consumes* from the driver, so it is two methods and stating them costs
9
- * nothing. `PglitePoolOptions` is the opposite case and imports PGlite's own type: those options are
10
- * the caller's input to the driver, so restating them would mean re-deriving its whole option surface
11
- * and then casting at the `PGlite.create` call.
12
- */
4
+ /** The two methods uql uses of `@electric-sql/pglite`, stated so its published types do not depend on a pre-1.0 package. */
13
5
  export type PgliteDatabase = {
14
6
  query<T>(query: string, params?: unknown[]): Promise<{
15
7
  rows: T[];
@@ -3,34 +3,12 @@ import { PostgresDialect } from '../postgres/postgresDialect.js';
3
3
  import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandleQuerierPool.js';
4
4
  import type { ExtraOptions } from '../type/index.js';
5
5
  import { type PgliteDatabase, PgliteQuerier } from './pgliteQuerier.js';
6
- /**
7
- * The driver's own options, minus the `dataDir` this pool takes as its first argument.
8
- *
9
- * @remarks Imported rather than restated so extensions and the filesystem hooks keep their real types:
10
- * `extensions: { vector }` from `@electric-sql/pglite-pgvector` is how a vector column becomes usable,
11
- * mirroring `LocalSqlitePoolOptions.extensions` for `sqlite-vec`. Type-only, like the `pg` imports in
12
- * `abstractPgQuerierPool.ts`, so nothing here reaches a runtime without the peer installed.
13
- */
6
+ /** PGlite's own options but `dataDir`, the pool's first argument; `extensions: { vector }` enables pgvector. Type-only. */
14
7
  export type PglitePoolOptions = Omit<PGliteOptions, 'dataDir'>;
15
8
  /**
16
- * Pool for PGlite, Postgres compiled to WASM and run in this process. No server, no container.
17
- *
18
- * PGlite is single connection, so the shared-handle lifecycle is {@link AbstractSharedHandleQuerierPool}'s.
19
- * Where PGlite differs from the two SQLite-family pools there is that it does not refuse a second
20
- * `BEGIN`: a querier that opens a transaction while another already has one silently joins it, and that
21
- * one's `ROLLBACK` then discards both queriers' writes. Nothing reports it, so a unit of work that needs
22
- * a transaction of its own needs its own pool, and therefore its own database.
23
- *
24
- * @remarks Transactions are plain `BEGIN`/`COMMIT` statements rather than `db.transaction()`, whose
25
- * callback holds PGlite's transaction mutex and would block every other querier's reads until commit.
26
- * The cost is that PGlite cannot see the transaction, so it flushes to the filesystem after each
27
- * statement within one: pass `relaxedDurability: true` on a persistent `dataDir` to skip waiting on
28
- * those flushes.
29
- *
30
- * The dialect is plain `PostgresDialect`, `dialectName` included: PGlite *is* Postgres, so the
31
- * introspector, schema generator and CLI resolve to Postgres's. Both driver capabilities hold as
32
- * they are - PGlite serializes every built-in array type itself, and the `$n::jsonb` cast is what
33
- * makes its `Describe` report JSONB and pick its JSON serializer; a bare `$n` binds `[object Object]`.
9
+ * A pool for PGlite, Postgres in WASM in this process, on one connection. A second `BEGIN` silently joins
10
+ * the open transaction, so a unit of work needing its own needs its own pool. Transactions are plain
11
+ * statements, so pass `relaxedDurability` on a persistent `dataDir` to skip a flush per statement.
34
12
  */
35
13
  export declare class PgliteQuerierPool extends AbstractSharedHandleQuerierPool<PgliteDatabase, PgliteQuerier, PostgresDialect> {
36
14
  readonly dataDir: string;
@@ -4,24 +4,9 @@ import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandle
4
4
  import { decodeWideNumber } from '../util/wideNumber.js';
5
5
  import { PgliteQuerier } from './pgliteQuerier.js';
6
6
  /**
7
- * Pool for PGlite, Postgres compiled to WASM and run in this process. No server, no container.
8
- *
9
- * PGlite is single connection, so the shared-handle lifecycle is {@link AbstractSharedHandleQuerierPool}'s.
10
- * Where PGlite differs from the two SQLite-family pools there is that it does not refuse a second
11
- * `BEGIN`: a querier that opens a transaction while another already has one silently joins it, and that
12
- * one's `ROLLBACK` then discards both queriers' writes. Nothing reports it, so a unit of work that needs
13
- * a transaction of its own needs its own pool, and therefore its own database.
14
- *
15
- * @remarks Transactions are plain `BEGIN`/`COMMIT` statements rather than `db.transaction()`, whose
16
- * callback holds PGlite's transaction mutex and would block every other querier's reads until commit.
17
- * The cost is that PGlite cannot see the transaction, so it flushes to the filesystem after each
18
- * statement within one: pass `relaxedDurability: true` on a persistent `dataDir` to skip waiting on
19
- * those flushes.
20
- *
21
- * The dialect is plain `PostgresDialect`, `dialectName` included: PGlite *is* Postgres, so the
22
- * introspector, schema generator and CLI resolve to Postgres's. Both driver capabilities hold as
23
- * they are - PGlite serializes every built-in array type itself, and the `$n::jsonb` cast is what
24
- * makes its `Describe` report JSONB and pick its JSON serializer; a bare `$n` binds `[object Object]`.
7
+ * A pool for PGlite, Postgres in WASM in this process, on one connection. A second `BEGIN` silently joins
8
+ * the open transaction, so a unit of work needing its own needs its own pool. Transactions are plain
9
+ * statements, so pass `relaxedDurability` on a persistent `dataDir` to skip a flush per statement.
25
10
  */
26
11
  export class PgliteQuerierPool extends AbstractSharedHandleQuerierPool {
27
12
  dataDir;
@@ -7,14 +7,7 @@ export interface PgAnyPool<C extends PgAnyClient> extends ErrorEmittingPool {
7
7
  connect: () => Promise<C>;
8
8
  end: () => Promise<void>;
9
9
  }
10
- /**
11
- * Shared base class for Postgres-compatible querier pools. Each hands out a {@link PgQuerier} over its
12
- * driver's client, so a subclass supplies only the dialect and the driver's pool.
13
- *
14
- * Wires the crash-preventing error handler here, once, so a new pg-compatible pool subclass can't be
15
- * added without it - the constructor takes the already constructed pool and attaches the handler
16
- * unconditionally.
17
- */
10
+ /** A Postgres-wire pool of {@link PgQuerier}s, attaching the error handler that keeps a dropped connection from crashing the process. */
18
11
  export declare abstract class AbstractPgQuerierPool<C extends PgAnyClient, D extends AbstractSqlDialect> extends AbstractSqlQuerierPool<PgQuerier<C>, D> {
19
12
  readonly pool: PgAnyPool<C>;
20
13
  constructor(dialect: D, pool: PgAnyPool<C>, extra?: ExtraOptions);
@@ -1,14 +1,7 @@
1
1
  import { AbstractSqlQuerierPool } from '../querier/index.js';
2
2
  import { attachPoolErrorHandler } from '../util/index.js';
3
3
  import { PgQuerier } from './pgQuerier.js';
4
- /**
5
- * Shared base class for Postgres-compatible querier pools. Each hands out a {@link PgQuerier} over its
6
- * driver's client, so a subclass supplies only the dialect and the driver's pool.
7
- *
8
- * Wires the crash-preventing error handler here, once, so a new pg-compatible pool subclass can't be
9
- * added without it - the constructor takes the already constructed pool and attaches the handler
10
- * unconditionally.
11
- */
4
+ /** A Postgres-wire pool of {@link PgQuerier}s, attaching the error handler that keeps a dropped connection from crashing the process. */
12
5
  export class AbstractPgQuerierPool extends AbstractSqlQuerierPool {
13
6
  pool;
14
7
  constructor(dialect, pool, extra) {
@@ -10,32 +10,9 @@ type PgTypes = {
10
10
  getTypeParser(oid: number, format?: 'text' | 'binary'): (value: string) => unknown;
11
11
  };
12
12
  /**
13
- * Decode `INT8` and `FLOAT8`, leaving every other type to the driver: an INT8 by `decodeWideNumber` - a
14
- * number where one is exact, its exact text past 2^53 - and a FLOAT8 as the float64 it already is.
15
- *
16
- * uql owes this to the caller because uql picks the column: `type: Number` maps to BIGINT (see
17
- * `schema/canonicalType.ts`), so without it a field declared `number` read back as `'9'` - including
18
- * every auto-increment primary key, on every entity. `FLOAT8` is a float64, which is exactly what a
19
- * JS number is, so decoding it loses nothing at all.
20
- *
21
- * At the driver because everything crosses the wire decoder exactly once - entity reads, `RETURNING
22
- * id`, raw SQL, counts, aggregates - while the ORM's hydration only ever sees entity reads. Which
23
- * types belong here and which need the entity's declaration is settled in `hydratableFields`.
24
- *
25
- * `NUMERIC` is deliberately absent, and decoded in hydration instead: `type: BigInt` also maps to
26
- * BIGINT, so a blanket decode here is already as far as a driver can go without the declaration. That
27
- * split also covers mysql2, which returns DECIMAL as text and has no equivalent hook.
28
- *
29
- * Per pool, never global, which is the whole reason this takes `types` as an argument. TypeORM does
30
- * the same job by assigning `postgres.defaults.parseInt8`, a module-wide flag every pool in the
31
- * process then shares; MikroORM passes a per-pool `TypeOverrides`, as here. Two globals of exactly
32
- * that shape have already been deleted from this repo - `test/pgTypeParsers.util.ts` and the
33
- * `types.setTypeParser` calls in `neon/neonQuerier.test.ts` - and both made the suite pass on
34
- * behaviour the library never shipped. Do not reintroduce one.
35
- *
36
- * A caller's own `types` in the pool options are spread after this one and therefore win. For a
37
- * decimal, the lighter escape hatch is the declaration itself: `@Field({ type: String, columnType:
38
- * 'decimal' })` keeps the column DECIMAL while leaving the value as the exact text the driver returned.
13
+ * Decodes `INT8` by `decodeWideNumber` and `FLOAT8` as the float64 it is, since `type: Number` maps to
14
+ * BIGINT. At the wire, which every result crosses; `NUMERIC` is left to hydration, which knows the field.
15
+ * Per pool, never a global parser, and a caller's own `types` win.
39
16
  */
40
17
  export declare function numericTypes(types: PgTypes): CustomTypesConfig;
41
18
  export {};
@@ -1,31 +1,8 @@
1
1
  import { decodeWideNumber } from '../util/wideNumber.js';
2
2
  /**
3
- * Decode `INT8` and `FLOAT8`, leaving every other type to the driver: an INT8 by `decodeWideNumber` - a
4
- * number where one is exact, its exact text past 2^53 - and a FLOAT8 as the float64 it already is.
5
- *
6
- * uql owes this to the caller because uql picks the column: `type: Number` maps to BIGINT (see
7
- * `schema/canonicalType.ts`), so without it a field declared `number` read back as `'9'` - including
8
- * every auto-increment primary key, on every entity. `FLOAT8` is a float64, which is exactly what a
9
- * JS number is, so decoding it loses nothing at all.
10
- *
11
- * At the driver because everything crosses the wire decoder exactly once - entity reads, `RETURNING
12
- * id`, raw SQL, counts, aggregates - while the ORM's hydration only ever sees entity reads. Which
13
- * types belong here and which need the entity's declaration is settled in `hydratableFields`.
14
- *
15
- * `NUMERIC` is deliberately absent, and decoded in hydration instead: `type: BigInt` also maps to
16
- * BIGINT, so a blanket decode here is already as far as a driver can go without the declaration. That
17
- * split also covers mysql2, which returns DECIMAL as text and has no equivalent hook.
18
- *
19
- * Per pool, never global, which is the whole reason this takes `types` as an argument. TypeORM does
20
- * the same job by assigning `postgres.defaults.parseInt8`, a module-wide flag every pool in the
21
- * process then shares; MikroORM passes a per-pool `TypeOverrides`, as here. Two globals of exactly
22
- * that shape have already been deleted from this repo - `test/pgTypeParsers.util.ts` and the
23
- * `types.setTypeParser` calls in `neon/neonQuerier.test.ts` - and both made the suite pass on
24
- * behaviour the library never shipped. Do not reintroduce one.
25
- *
26
- * A caller's own `types` in the pool options are spread after this one and therefore win. For a
27
- * decimal, the lighter escape hatch is the declaration itself: `@Field({ type: String, columnType:
28
- * 'decimal' })` keeps the column DECIMAL while leaving the value as the exact text the driver returned.
3
+ * Decodes `INT8` by `decodeWideNumber` and `FLOAT8` as the float64 it is, since `type: Number` maps to
4
+ * BIGINT. At the wire, which every result crosses; `NUMERIC` is left to hydration, which knows the field.
5
+ * Per pool, never a global parser, and a caller's own `types` win.
29
6
  */
30
7
  export function numericTypes(types) {
31
8
  // Text only: in binary mode an INT8 arrives as an 8-byte Buffer, and `Number(buffer)` is `NaN`.
@@ -1,17 +1,11 @@
1
1
  import { PgLikeSqlDialect } from '../dialect/pgLikeSqlDialect.js';
2
- import type { QueryConflictPaths, QueryContext, SqlDialectName, Type } from '../type/index.js';
3
- /**
4
- * PostgreSQL dialect, the same class under every Postgres driver - `pg`, Neon, PGlite, `bun:sql` -
5
- * where a driver that binds differently passes `driverCapabilities` rather than subclassing it.
6
- * Shared Postgres-wire AST/quoting/JSONB/full-text-search/vector-search logic (including BIGINT
7
- * IDENTITY PKs) lives in {@link PgLikeSqlDialect}; this class adds what's Postgres-only: the `vector`
8
- * extension requirement, pgvector's index syntax, and `xmax`-based upsert `created` detection.
9
- */
2
+ import type { QueryConflictPaths, QueryContext, SqlDialectFeatures, SqlDialectName, Type } from '../type/index.js';
3
+ /** PostgreSQL, under every driver: `pg`, Neon, PGlite, `bun:sql`. Adds pgvector and the `xmax` upsert `created`. */
10
4
  export declare class PostgresDialect extends PgLikeSqlDialect {
11
5
  readonly dialectName: SqlDialectName;
12
6
  readonly vectorExtension: string | undefined;
13
- /** pgvector is the only engine with `halfvec` and `sparsevec`; every other maps them onto `vector`. */
14
- protected readonly hasNarrowVectorTypes = true;
7
+ /** pgvector is the only engine with `halfvec` and `sparsevec`. */
8
+ readonly features: SqlDialectFeatures;
15
9
  upsert<E>(ctx: QueryContext, entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E | E[]): void;
16
10
  /**
17
11
  * `to_regclass` rather than a `::regclass` cast: it answers `NULL` for a table that does not exist
@@ -1,18 +1,12 @@
1
1
  import { COUNT_ALIAS } from '../dialect/aliases.js';
2
- import { PgLikeSqlDialect } from '../dialect/pgLikeSqlDialect.js';
2
+ import { PG_FEATURES, PgLikeSqlDialect } from '../dialect/pgLikeSqlDialect.js';
3
3
  import { getMeta } from '../entity/index.js';
4
- /**
5
- * PostgreSQL dialect, the same class under every Postgres driver - `pg`, Neon, PGlite, `bun:sql` -
6
- * where a driver that binds differently passes `driverCapabilities` rather than subclassing it.
7
- * Shared Postgres-wire AST/quoting/JSONB/full-text-search/vector-search logic (including BIGINT
8
- * IDENTITY PKs) lives in {@link PgLikeSqlDialect}; this class adds what's Postgres-only: the `vector`
9
- * extension requirement, pgvector's index syntax, and `xmax`-based upsert `created` detection.
10
- */
4
+ /** PostgreSQL, under every driver: `pg`, Neon, PGlite, `bun:sql`. Adds pgvector and the `xmax` upsert `created`. */
11
5
  export class PostgresDialect extends PgLikeSqlDialect {
12
6
  dialectName = 'postgres';
13
7
  vectorExtension = 'vector';
14
- /** pgvector is the only engine with `halfvec` and `sparsevec`; every other maps them onto `vector`. */
15
- hasNarrowVectorTypes = true;
8
+ /** pgvector is the only engine with `halfvec` and `sparsevec`. */
9
+ features = { ...PG_FEATURES, narrowVectorTypes: true };
16
10
  upsert(ctx, entity, conflictPaths, payload) {
17
11
  // The xmax system column is 0 for a newly inserted row and non-zero for an updated one (MVCC).
18
12
  super.upsert(ctx, entity, conflictPaths, payload, `(xmax = 0) AS ${this.escapeId('_created')}`);
@@ -1,4 +1,4 @@
1
- import type { EntityData, EntityId, ExtraOptions, FieldKey, IdValue, Querier, Query, QueryAggMap, QueryAggregate, QueryAggregateResult, QueryConflictPaths, QueryFilter, QueryFindResult, QueryGroupMap, QueryOneProjected, QueryOptions, QueryPage, QueryProjected, QuerySearch, PrimaryKey, QueryUpdateResult, QueryUpsertOneResult, QueryUpsertManyResult, RelationKey, TransactionOptions, Type, UpdatePayload, WrittenId } from '../type/index.js';
1
+ import type { EntityData, EntityId, ExtraOptions, FieldKey, Querier, Query, QueryAggMap, QueryAggregate, QueryAggregateResult, QueryConflictPaths, QueryFilter, QueryFindResult, QueryGroupMap, QueryOneProjected, QueryOptions, QueryPage, QueryProjected, QuerySearch, PrimaryKey, QueryUpdateResult, QueryUpsertOneResult, QueryUpsertManyResult, RelationKey, TransactionOptions, Type, UpdatePayload, WrittenId } from '../type/index.js';
2
2
  import { LoggerWrapper } from '../util/index.js';
3
3
  /** Base class for all database queriers. */
4
4
  export declare abstract class AbstractQuerier implements Querier {
@@ -18,28 +18,17 @@ export declare abstract class AbstractQuerier implements Querier {
18
18
  constructor(extra?: ExtraOptions | undefined);
19
19
  protected validateProjectionQuery<E extends object>(entity: Type<E>, q: Query<E>): void;
20
20
  private validateProjectionQueryRecursive;
21
- /**
22
- * Resolves `[entity, query, opts]` for the dual call pattern: `(entity, q, opts)` (entity argument)
23
- * vs `(query, opts)` (entity via the query's `$entity` field). Generic in the query `Q` because it
24
- * only ever reads `$entity`: pinning it to one statement's shape made every caller launder its own
25
- * through a cast, which is how a read query's `$sort` used to reach a write's.
26
- */
21
+ /** `[entity, query, opts]` from either call form, `(entity, q, opts)` or `({ $entity, ...q }, opts)`. */
27
22
  protected resolveEntityQuery<E extends object, Q extends object>(entityOrQuery: Type<E> | (Q & {
28
23
  $entity: Type<E>;
29
24
  }), maybeQueryOrOpts?: Q | QueryOptions, maybeOpts?: QueryOptions): [Type<E>, Q, QueryOptions | undefined];
30
25
  findOneById<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(entity: Type<E>, id: EntityId<E>, q?: QueryOneProjected<E, S, V, X, P, C>, opts?: QueryOptions): Promise<QueryFindResult<E, S, V, X, P, C> | undefined>;
31
- /**
32
- * Find a single record matching the query.
33
- * Supports both entity-as-argument and entity-as-field patterns.
34
- */
26
+ /** Find one record, the entity passed first or as the query's `$entity`. */
35
27
  findOne<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(q: QueryOneProjected<E, S, V, X, P, C> & {
36
28
  $entity: Type<E>;
37
29
  }, opts?: QueryOptions): Promise<QueryFindResult<E, S, V, X, P, C> | undefined>;
38
30
  findOne<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(entity: Type<E>, q: QueryOneProjected<E, S, V, X, P, C>, opts?: QueryOptions): Promise<QueryFindResult<E, S, V, X, P, C> | undefined>;
39
- /**
40
- * Find multiple records matching the query.
41
- * Supports both entity-as-argument and entity-as-field patterns.
42
- */
31
+ /** Find many records, the entity passed first or as the query's `$entity`. */
43
32
  findMany<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(q: QueryProjected<E, S, V, X, P, C> & {
44
33
  $entity: Type<E>;
45
34
  }, opts?: QueryOptions): Promise<QueryFindResult<E, S, V, X, P, C>[]>;
@@ -49,20 +38,13 @@ export declare abstract class AbstractQuerier implements Querier {
49
38
  * or pipeline. [The design](../../../../architecture/relations-in-one-statement.md).
50
39
  */
51
40
  protected abstract internalFindMany<E extends object>(entity: Type<E>, q: Query<E>, opts?: QueryOptions): Promise<E[]>;
52
- /**
53
- * Stream records as an async iterable, in both the entity-as-argument and entity-as-field patterns.
54
- * Each row streams with its populated relations and counts, read by the same statement or pipeline
55
- * `findMany` runs. No `afterLoad` hooks on streamed rows.
56
- */
41
+ /** Stream records with the relations and counts `findMany` reads, the entity passed first or as `$entity`. No hooks fire. */
57
42
  findManyStream<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(q: QueryProjected<E, S, V, X, P, C> & {
58
43
  $entity: Type<E>;
59
44
  }, opts?: QueryOptions): AsyncIterable<QueryFindResult<E, S, V, X, P, C>>;
60
45
  findManyStream<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(entity: Type<E>, q: QueryProjected<E, S, V, X, P, C>, opts?: QueryOptions): AsyncIterable<QueryFindResult<E, S, V, X, P, C>>;
61
46
  protected abstract internalFindManyStream<E extends object>(entity: Type<E>, q: Query<E>, opts?: QueryOptions): AsyncIterable<E>;
62
- /**
63
- * Find multiple records and return both the records and total count.
64
- * Supports both entity-as-argument and entity-as-field patterns.
65
- */
47
+ /** Find many records and count every match, the entity passed first or as the query's `$entity`. */
66
48
  findManyAndCount<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(q: QueryProjected<E, S, V, X, P, C> & {
67
49
  $entity: Type<E>;
68
50
  }, opts?: QueryOptions): Promise<[QueryFindResult<E, S, V, X, P, C>[], number]>;
@@ -72,20 +54,14 @@ export declare abstract class AbstractQuerier implements Querier {
72
54
  * backend with no way to answer both at once is left with; SQL overrides it to answer from one.
73
55
  */
74
56
  protected internalFindManyAndCount<E extends object>(entity: Type<E>, q: Query<E>, opts?: QueryOptions): Promise<[E[], number]>;
75
- /**
76
- * Count records matching the query, in both the entity-as-argument and entity-as-field patterns.
77
- * A `$skip`/`$limit` settles the matching ids and counts those, rather than scanning every match;
78
- * `$sort` never reaches that SELECT, since it changes which rows a page holds, never how many.
79
- */
57
+ /** Count records matching the query, the entity passed first or as the query's `$entity`. */
80
58
  count<E extends object>(entity: Type<E>, q?: QueryPage<E>, opts?: QueryOptions): Promise<number>;
81
59
  count<E extends object>(q: QueryPage<E> & {
82
60
  $entity: Type<E>;
83
61
  }, opts?: QueryOptions): Promise<number>;
84
- protected abstract internalCount<E extends object>(entity: Type<E>, q: QueryFilter<E>, opts?: QueryOptions): Promise<number>;
85
- /**
86
- * Whether anything matches, in both the entity-as-argument and entity-as-field patterns. A count
87
- * capped at one row, so the engine stops at the first match instead of scanning every other one.
88
- */
62
+ /** How many rows match, or how many of them a page takes: counted where they are, never read. */
63
+ protected abstract internalCount<E extends object>(entity: Type<E>, q: QueryPage<E>, opts?: QueryOptions): Promise<number>;
64
+ /** Whether anything matches, the entity passed first or as `$entity`: a count capped at one row. */
89
65
  exists<E extends object>(entity: Type<E>, q?: QueryFilter<E>, opts?: QueryOptions): Promise<boolean>;
90
66
  exists<E extends object>(q: QueryFilter<E> & {
91
67
  $entity: Type<E>;
@@ -106,38 +82,30 @@ export declare abstract class AbstractQuerier implements Querier {
106
82
  /** Writes `rows`, and onto each one the key the database generated for it, where it can tell. */
107
83
  protected abstract internalInsertMany<E extends object>(entity: Type<E>, rows: EntityData<E>[]): Promise<void>;
108
84
  updateOneById<E extends object>(entity: Type<E>, id: EntityId<E>, payload: UpdatePayload<E>, opts?: QueryOptions): Promise<number>;
85
+ /** Settles the rows first where the update cascades, so a payload changing what `$where` reads still names them. */
109
86
  updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, payload: UpdatePayload<E>, opts?: QueryOptions): Promise<number>;
87
+ /** The UPDATE, skipped where the payload writes no column, reporting `unwritten` instead. */
88
+ private updateColumns;
89
+ /** Whether a write has to name the rows `q` matches by their ids: no engine pages or orders an update or delete. */
90
+ protected settlesWrite<E extends object>(_entity: Type<E>, q: QuerySearch<E>): boolean;
91
+ /** The ids `q` matches, in its own order and page. */
92
+ protected settleIds<E extends object>(entity: Type<E>, q: QuerySearch<E>, opts?: QueryOptions): Promise<EntityId<E>[]>;
93
+ /** Runs one UPDATE over `q`, which names its rows by id wherever {@link updateMany} settled them. */
110
94
  protected abstract internalUpdateMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, payload: UpdatePayload<E>, opts?: QueryOptions): Promise<number>;
111
95
  restoreOneById<E extends object>(entity: Type<E>, id: EntityId<E>): Promise<number>;
112
96
  restoreMany<E extends object>(entity: Type<E>, q: QuerySearch<E>): Promise<number>;
113
- /**
114
- * `beforeUpsert`/`afterUpsert` rather than the insert's or the update's pair: the database decides
115
- * which branch each row takes as the statement runs, so neither of those could be fired honestly -
116
- * but the upsert itself is a fact known before and after, and a row written with no hook at all
117
- * was how an `@Id({ onInsert })` or an audit trail silently skipped this path.
118
- */
97
+ /** Fires `beforeUpsert`/`afterUpsert`: which branch a row takes is the database's to decide, so neither the insert's nor the update's pair fits. */
119
98
  upsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityData<E>): Promise<QueryUpsertOneResult<E>>;
120
99
  upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityData<E>[]): Promise<QueryUpsertManyResult<E>>;
121
100
  protected abstract internalUpsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityData<E>): Promise<QueryUpdateResult>;
122
101
  protected abstract internalUpsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityData<E>[]): Promise<QueryUpdateResult>;
123
102
  deleteOneById<E extends object>(entity: Type<E>, id: EntityId<E>, opts?: QueryOptions): Promise<number>;
124
- /**
125
- * Delete records matching the query. Soft-deletes when the entity has a soft-delete field (unless
126
- * `opts.hardDelete`), otherwise removes the rows. Supports both entity-as-argument and entity-as-field patterns.
127
- */
103
+ /** Delete records matching the query, the entity passed first or as `$entity`; soft-deletes unless `opts.hardDelete`. */
128
104
  deleteMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, opts?: QueryOptions): Promise<number>;
129
105
  deleteMany<E extends object>(q: QuerySearch<E> & {
130
106
  $entity: Type<E>;
131
107
  }, opts?: QueryOptions): Promise<number>;
132
- /**
133
- * The rows a delete is about to take, loaded only when a hook or listener is there to receive
134
- * them: the round trip is pure overhead for the (common) delete nobody is watching, and
135
- * `internalDeleteMany` has its own fast path that never reads the rows at all.
136
- *
137
- * `undefined` means nobody was watching, which is not the same as the empty array meaning nothing
138
- * matched - the caller deletes by `q` for the first and skips the statement entirely for the second.
139
- */
140
- private findDoomed;
108
+ /** Runs one DELETE (or soft-delete stamp) over `q`, which names its rows by id wherever {@link deleteMany} settled them. */
141
109
  protected abstract internalDeleteMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, opts?: QueryOptions): Promise<number>;
142
110
  saveOne<E extends object>(entity: Type<E>, payload: EntityData<E>): Promise<WrittenId<E> | undefined>;
143
111
  /**
@@ -146,39 +114,21 @@ export declare abstract class AbstractQuerier implements Querier {
146
114
  * inserts. A composite is always named. The hooks follow the statement: a named row fires the upsert pair.
147
115
  */
148
116
  saveMany<E extends object>(entity: Type<E>, payload: EntityData<E>[]): Promise<(WrittenId<E> | undefined)[]>;
149
- protected insertRelations<E extends object>(entity: Type<E>, payload: E[]): Promise<void>;
150
- protected updateRelations<E extends object>(entity: Type<E>, q: QuerySearch<E>, payload: UpdatePayload<E>, opts?: QueryOptions): Promise<void>;
117
+ /** Writes each inserted row's relations, one set of statements per relation whatever the number of rows. */
118
+ protected insertRelations<E extends object>(entity: Type<E>, rows: E[]): Promise<void>;
119
+ /** `EntityId` because a settled composite row is an object, which {@link childrenOf} reads each foreign key column out of. */
120
+ private deleteRelations;
151
121
  /**
152
- * `EntityId` because a settled set names composite rows as objects, which is also what the parent's
153
- * own delete takes - and what {@link childrenOf} reads each child's foreign key columns out of.
122
+ * Writes each parent's value into one relation. The parent owns what it points at: an update replaces
123
+ * it, and a `null` only clears it.
154
124
  */
155
- protected deleteRelations<E extends object>(entity: Type<E>, ids: EntityId<E>[], opts?: QueryOptions): Promise<void>;
156
- /**
157
- * Persists `relValue` against every id in `ids`, which an update hands the whole page of rows it
158
- * settled: the value is the same for all of them, so the statements are per relation rather than per
159
- * row wherever the cardinality allows it.
160
- */
161
- protected saveRelation<E extends object>(entity: Type<E>, ids: IdValue<E>[], relValue: unknown, relKey: RelationKey<E>, isUpdate?: boolean): Promise<void>;
162
- private saveToMany;
163
- private saveOneToOne;
125
+ private saveRelation;
126
+ /** Each parent gets its own referenced row, and its own column pointing at it. */
164
127
  private saveManyToOne;
165
128
  abstract readonly hasOpenTransaction: boolean;
166
129
  /**
167
- * Runs `callback` in a transaction: begin, commit on success, roll back on failure.
168
- *
169
- * The single place that sequence is written; everything else delegates here, because both subtleties
170
- * below were got wrong by code that hand-rolled it:
171
- *
172
- * - `beginTransaction` connects before it begins, so a refused connection lands in the catch with no
173
- * transaction open. `rollbackTransaction` is a no-op there rather than an error, which is why a
174
- * wrong password no longer surfaces as a transaction-state error.
175
- * - A rollback that fails too is a consequence of the original failure, not news, so it must not
176
- * replace it either.
177
- *
178
- * The connection is **not** released here: whoever took it from the pool gives it back, through
179
- * {@link QuerierPool.transaction}, {@link QuerierPool.withQuerier} or `await using`. Releasing a
180
- * connection this method never acquired is what forced every caller to know whether it still owned
181
- * one afterwards.
130
+ * Runs `callback` in a transaction, joining one already open. A rollback that fails is logged, never
131
+ * thrown over the original error, and the connection stays with whoever acquired it.
182
132
  */
183
133
  transaction<T>(callback: () => Promise<T>, opts?: TransactionOptions): Promise<T>;
184
134
  /** Whether anything at all - a global listener or the entity itself - handles `event`. */
@@ -205,23 +155,9 @@ export declare abstract class AbstractQuerier implements Querier {
205
155
  private hooked;
206
156
  /** Fires the global listeners first, then the entity's own hooks. */
207
157
  private emitHook;
208
- /**
209
- * Runs `task` after everything already queued on this querier, one at a time.
210
- *
211
- * @remarks Not re-entrant: only one task runs at a time, so a serialized method awaited from inside
212
- * another one would wait for a task queued behind itself. Callers below keep their `serialize` calls
213
- * sequential rather than nested.
214
- */
158
+ /** Runs `task` after everything already queued, one at a time. Not re-entrant: never nest `serialize` calls. */
215
159
  protected serialize<T>(task: () => Promise<T>): Promise<T>;
216
- /**
217
- * Runs `task`, logs `query` with how long it took, and tags any error it throws with that query.
218
- *
219
- * A method rather than the `@Log()` decorator it replaces. A standard-spec method decorator works by
220
- * returning a replacement function, and a replacement cannot carry the original's type parameters, so
221
- * decorating `internalFindMany<E extends Document>` made its signature unresolvable. Most of the query
222
- * surface is generic like that, and wrapping at the call site costs one line while keeping the
223
- * signature intact.
224
- */
160
+ /** Runs `task`, logs `query` with its duration, and tags a failure with it: a method, since a decorator would lose the generics. */
225
161
  protected timed<T>(query: string, values: unknown[] | undefined, task: () => Promise<T>): Promise<T>;
226
162
  abstract beginTransaction(opts?: TransactionOptions): Promise<void>;
227
163
  /** Strict: this is the check that catches a forgotten `beginTransaction`. */
@@ -232,12 +168,8 @@ export declare abstract class AbstractQuerier implements Querier {
232
168
  */
233
169
  abstract rollbackTransaction(): Promise<void>;
234
170
  /**
235
- * Rolls back an unfinished transaction, then hands the connection back.
236
- *
237
- * @remarks Refusing to release was the opposite of safe: the throw came *before* the connection went
238
- * back, so it destroyed the error that got here and cost the pool a connection with a live `BEGIN` on
239
- * it. It is also the only option `await using` can reach, which calls `Symbol.asyncDispose` with no
240
- * arguments and discards what it returns.
171
+ * Rolls back an unfinished transaction, then hands the connection back, discarding it if the rollback
172
+ * failed. Never throws first, since `await using` has no other way to release.
241
173
  */
242
174
  release(): Promise<void>;
243
175
  [Symbol.asyncDispose](): Promise<void>;