uql-orm 0.54.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 (131) hide show
  1. package/README.md +2 -2
  2. package/dist/browser/uql-browser.min.js.map +1 -1
  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/crdbQuerierPool.d.ts +1 -3
  10. package/dist/cockroachdb/crdbQuerierPool.js +0 -4
  11. package/dist/cockroachdb/index.d.ts +0 -1
  12. package/dist/cockroachdb/index.js +0 -1
  13. package/dist/d1/d1SqliteDialect.d.ts +5 -0
  14. package/dist/d1/d1SqliteDialect.js +7 -0
  15. package/dist/dialect/abstractSqlDialect.d.ts +9 -9
  16. package/dist/dialect/abstractSqlDialect.js +38 -51
  17. package/dist/dialect/hydrateColumn.js +2 -2
  18. package/dist/dialect/mergeSqlDialect.d.ts +2 -2
  19. package/dist/dialect/mergeSqlDialect.js +0 -4
  20. package/dist/dialect/pgLikeSqlDialect.d.ts +5 -0
  21. package/dist/dialect/pgLikeSqlDialect.js +12 -2
  22. package/dist/entity/index.d.ts +1 -1
  23. package/dist/entity/index.js +1 -1
  24. package/dist/entity/metadata/definition.d.ts +3 -1
  25. package/dist/entity/metadata/definition.js +8 -0
  26. package/dist/libsql/index.d.ts +0 -1
  27. package/dist/libsql/index.js +0 -1
  28. package/dist/libsql/libsqlQuerierPool.d.ts +3 -5
  29. package/dist/libsql/libsqlQuerierPool.js +2 -5
  30. package/dist/maria/mariadbQuerier.d.ts +0 -3
  31. package/dist/maria/mariadbQuerier.js +6 -7
  32. package/dist/maria/mariadbQuerierPool.js +4 -7
  33. package/dist/migrate/ddl/index.d.ts +3 -3
  34. package/dist/migrate/ddl/index.js +3 -3
  35. package/dist/mongo/index.d.ts +0 -1
  36. package/dist/mongo/index.js +0 -1
  37. package/dist/mongo/mongoDialect.d.ts +0 -1
  38. package/dist/mongo/mongoDialect.js +3 -14
  39. package/dist/mongo/mongodbQuerier.js +6 -5
  40. package/dist/mongo/mongodbQuerierPool.d.ts +2 -2
  41. package/dist/mongo/mongodbQuerierPool.js +2 -2
  42. package/dist/mssql/mssqlDialect.d.ts +3 -5
  43. package/dist/mssql/mssqlDialect.js +9 -8
  44. package/dist/mssql/mssqlQuerier.d.ts +13 -6
  45. package/dist/mssql/mssqlQuerier.js +30 -75
  46. package/dist/mssql/mssqlQuerierPool.js +2 -0
  47. package/dist/mssql/mssqlWireTypes.d.ts +3 -4
  48. package/dist/mssql/mssqlWireTypes.js +5 -8
  49. package/dist/mysql/index.d.ts +0 -1
  50. package/dist/mysql/index.js +0 -1
  51. package/dist/mysql/mysql2Querier.d.ts +1 -4
  52. package/dist/mysql/mysql2Querier.js +0 -3
  53. package/dist/mysql/mysql2QuerierPool.d.ts +2 -2
  54. package/dist/mysql/mysql2QuerierPool.js +5 -3
  55. package/dist/neon/index.d.ts +0 -2
  56. package/dist/neon/index.js +0 -2
  57. package/dist/neon/neonQuerierPool.d.ts +2 -4
  58. package/dist/neon/neonQuerierPool.js +2 -6
  59. package/dist/pglite/index.d.ts +0 -1
  60. package/dist/pglite/index.js +0 -1
  61. package/dist/pglite/pgliteQuerier.d.ts +3 -3
  62. package/dist/pglite/pgliteQuerier.js +1 -1
  63. package/dist/pglite/pgliteQuerierPool.d.ts +7 -2
  64. package/dist/pglite/pgliteQuerierPool.js +16 -6
  65. package/dist/postgres/abstractPgQuerierPool.d.ts +8 -12
  66. package/dist/postgres/abstractPgQuerierPool.js +8 -6
  67. package/dist/postgres/index.d.ts +0 -1
  68. package/dist/postgres/index.js +0 -1
  69. package/dist/postgres/pgNumericTypes.d.ts +5 -5
  70. package/dist/postgres/pgNumericTypes.js +11 -7
  71. package/dist/postgres/pgQuerier.d.ts +22 -4
  72. package/dist/postgres/pgQuerier.js +29 -2
  73. package/dist/postgres/pgQuerierPool.d.ts +2 -4
  74. package/dist/postgres/pgQuerierPool.js +2 -6
  75. package/dist/postgres/postgresDialect.d.ts +5 -5
  76. package/dist/postgres/postgresDialect.js +5 -5
  77. package/dist/postgres/postgresWireDriverCapabilities.d.ts +2 -2
  78. package/dist/postgres/postgresWireDriverCapabilities.js +2 -2
  79. package/dist/querier/abstractPoolQuerier.d.ts +1 -1
  80. package/dist/querier/abstractPoolQuerier.js +1 -1
  81. package/dist/querier/abstractSqlQuerier.d.ts +11 -4
  82. package/dist/querier/abstractSqlQuerier.js +28 -16
  83. package/dist/sqlite/hranaQuerier.d.ts +6 -8
  84. package/dist/sqlite/hranaQuerier.js +13 -30
  85. package/dist/sqlite/hranaQuerierPool.d.ts +3 -4
  86. package/dist/sqlite/hranaQuerierPool.js +2 -1
  87. package/dist/sqlite/localSqliteQuerierPool.d.ts +3 -3
  88. package/dist/sqlite/localSqliteQuerierPool.js +4 -2
  89. package/dist/sqlite/nodeSqliteAdapter.d.ts +0 -1
  90. package/dist/sqlite/nodeSqliteQuerierPool.js +0 -2
  91. package/dist/sqlite/sqlitePragmas.d.ts +12 -0
  92. package/dist/sqlite/sqlitePragmas.js +15 -0
  93. package/dist/sqlite/sqliteQuerierPool.d.ts +0 -5
  94. package/dist/sqlite/sqliteQuerierPool.js +2 -13
  95. package/dist/turso/index.d.ts +0 -1
  96. package/dist/turso/index.js +0 -1
  97. package/dist/turso/tursoLocalQuerier.d.ts +0 -1
  98. package/dist/turso/tursoLocalQuerierPool.js +2 -2
  99. package/dist/turso/tursoQuerierPool.d.ts +1 -3
  100. package/dist/turso/tursoQuerierPool.js +0 -4
  101. package/dist/type/dialect.d.ts +1 -1
  102. package/dist/util/dialect.util.d.ts +1 -1
  103. package/dist/util/dialect.util.js +1 -1
  104. package/dist/util/logger.d.ts +7 -8
  105. package/dist/util/logger.js +18 -11
  106. package/dist/util/raw.js +3 -3
  107. package/dist/util/sqlLiteral.js +3 -8
  108. package/dist/util/string.util.js +2 -6
  109. package/dist/util/wideNumber.d.ts +14 -0
  110. package/dist/util/wideNumber.js +24 -0
  111. package/package.json +1 -1
  112. package/dist/cockroachdb/crdbQuerier.d.ts +0 -8
  113. package/dist/cockroachdb/crdbQuerier.js +0 -6
  114. package/dist/libsql/libsqlQuerier.d.ts +0 -10
  115. package/dist/libsql/libsqlQuerier.js +0 -10
  116. package/dist/mongo/mongodbNativeDialect.d.ts +0 -9
  117. package/dist/mongo/mongodbNativeDialect.js +0 -9
  118. package/dist/mysql/mysql2Dialect.d.ts +0 -9
  119. package/dist/mysql/mysql2Dialect.js +0 -9
  120. package/dist/neon/neonDialect.d.ts +0 -10
  121. package/dist/neon/neonDialect.js +0 -10
  122. package/dist/neon/neonQuerier.d.ts +0 -5
  123. package/dist/neon/neonQuerier.js +0 -3
  124. package/dist/pglite/pgliteDialect.d.ts +0 -14
  125. package/dist/pglite/pgliteDialect.js +0 -14
  126. package/dist/postgres/abstractPgQuerier.d.ts +0 -24
  127. package/dist/postgres/abstractPgQuerier.js +0 -32
  128. package/dist/postgres/pgDialect.d.ts +0 -10
  129. package/dist/postgres/pgDialect.js +0 -10
  130. package/dist/turso/tursoQuerier.d.ts +0 -10
  131. package/dist/turso/tursoQuerier.js +0 -10
@@ -1,5 +1,7 @@
1
+ import { decodeWideNumber } from '../util/wideNumber.js';
1
2
  /**
2
- * Decode `INT8` and `FLOAT8` as JS numbers, leaving every other type to the driver.
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.
3
5
  *
4
6
  * uql owes this to the caller because uql picks the column: `type: Number` maps to BIGINT (see
5
7
  * `schema/canonicalType.ts`), so without it a field declared `number` read back as `'9'` - including
@@ -21,15 +23,17 @@
21
23
  * `types.setTypeParser` calls in `neon/neonQuerier.test.ts` - and both made the suite pass on
22
24
  * behaviour the library never shipped. Do not reintroduce one.
23
25
  *
24
- * Exact to 2^53, which covers any auto-increment id. A caller who needs more passes their own
25
- * `types` in the pool options: it is spread after this one and therefore wins. For a decimal, the
26
- * lighter escape hatch is the declaration itself: `@Field({ type: String, columnType: 'decimal' })`
27
- * keeps the column DECIMAL while leaving the value as the exact text the driver returned.
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.
28
29
  */
29
30
  export function numericTypes(types) {
30
31
  // Text only: in binary mode an INT8 arrives as an 8-byte Buffer, and `Number(buffer)` is `NaN`.
31
- const textNumeric = new Set([types.builtins['INT8'], types.builtins['FLOAT8']]);
32
+ const decoders = new Map([
33
+ [types.builtins['INT8'], decodeWideNumber],
34
+ [types.builtins['FLOAT8'], Number],
35
+ ]);
32
36
  return {
33
- getTypeParser: (oid, format) => format === 'text' && textNumeric.has(oid) ? Number : types.getTypeParser(oid, format),
37
+ getTypeParser: (oid, format) => (format === 'text' && decoders.get(oid)) || types.getTypeParser(oid, format),
34
38
  };
35
39
  }
@@ -1,5 +1,23 @@
1
- import type { PoolClient } from 'pg';
2
- import { AbstractPgQuerier } from './abstractPgQuerier.js';
3
- import type { PostgresDialect } from './postgresDialect.js';
4
- export declare class PgQuerier extends AbstractPgQuerier<PoolClient, PostgresDialect> {
1
+ import { AbstractPoolQuerier } from '../querier/abstractPoolQuerier.js';
2
+ import type { RawRow } from '../type/index.js';
3
+ export interface PgAnyClient {
4
+ query(text: string, values?: unknown[]): Promise<{
5
+ rows: RawRow[];
6
+ rowCount: number | null;
7
+ }>;
8
+ query(stream: object): AsyncIterable<RawRow> & {
9
+ destroy(): void;
10
+ };
11
+ /** Any truthy argument makes `pg-pool` evict the client instead of returning it to the idle list. */
12
+ release(discard?: boolean): void | Promise<void>;
13
+ }
14
+ /**
15
+ * Querier for every client with node-postgres' API: `pg` itself, for Postgres and CockroachDB, and
16
+ * Neon's serverless driver. Generic over the client alone - the dialect is whichever the pool built.
17
+ */
18
+ export declare class PgQuerier<C extends PgAnyClient = PgAnyClient> extends AbstractPoolQuerier<C> {
19
+ internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
20
+ internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
21
+ internalStream<T>(query: string, values?: unknown[]): AsyncGenerator<Awaited<T>, void, unknown>;
22
+ protected releaseConn(conn: C, discard: boolean): Promise<void>;
5
23
  }
@@ -1,3 +1,30 @@
1
- import { AbstractPgQuerier } from './abstractPgQuerier.js';
2
- export class PgQuerier extends AbstractPgQuerier {
1
+ import { AbstractPoolQuerier } from '../querier/abstractPoolQuerier.js';
2
+ /**
3
+ * Querier for every client with node-postgres' API: `pg` itself, for Postgres and CockroachDB, and
4
+ * Neon's serverless driver. Generic over the client alone - the dialect is whichever the pool built.
5
+ */
6
+ export class PgQuerier extends AbstractPoolQuerier {
7
+ async internalAll(query, values) {
8
+ const res = await this.getConn().query(query, values);
9
+ return res.rows;
10
+ }
11
+ async internalRun(query, values) {
12
+ const res = await this.getConn().query(query, values);
13
+ return this.buildUpdateResult({ rows: res.rows, changes: res.rowCount ?? 0 });
14
+ }
15
+ async *internalStream(query, values) {
16
+ const { default: QueryStream } = await import('pg-query-stream');
17
+ const stream = this.getConn().query(new QueryStream(query, values));
18
+ try {
19
+ for await (const row of stream) {
20
+ yield row;
21
+ }
22
+ }
23
+ finally {
24
+ stream.destroy();
25
+ }
26
+ }
27
+ async releaseConn(conn, discard) {
28
+ await conn.release(discard);
29
+ }
3
30
  }
@@ -1,10 +1,8 @@
1
1
  import { Pool, type PoolClient, type PoolConfig } from 'pg';
2
2
  import type { ExtraOptions } from '../type/index.js';
3
3
  import { AbstractPgQuerierPool } from './abstractPgQuerierPool.js';
4
- import { PgDialect } from './pgDialect.js';
5
- import { PgQuerier } from './pgQuerier.js';
6
- export declare class PgQuerierPool extends AbstractPgQuerierPool<PoolClient, PgQuerier, PgDialect> {
4
+ import { PostgresDialect } from './postgresDialect.js';
5
+ export declare class PgQuerierPool extends AbstractPgQuerierPool<PoolClient, PostgresDialect> {
7
6
  readonly pool: Pool;
8
7
  constructor(opts: PoolConfig, extra?: ExtraOptions);
9
- protected buildQuerier(connect: () => Promise<PoolClient>): PgQuerier;
10
8
  }
@@ -1,16 +1,12 @@
1
1
  import { Pool, types } from 'pg';
2
2
  import { dialectOptionsFrom } from '../dialect/abstractDialect.js';
3
3
  import { AbstractPgQuerierPool } from './abstractPgQuerierPool.js';
4
- import { PgDialect } from './pgDialect.js';
5
4
  import { numericTypes } from './pgNumericTypes.js';
6
- import { PgQuerier } from './pgQuerier.js';
5
+ import { PostgresDialect } from './postgresDialect.js';
7
6
  export class PgQuerierPool extends AbstractPgQuerierPool {
8
7
  constructor(opts, extra) {
9
8
  // keepAlive reduces (but can't eliminate) idle connections being silently
10
9
  // dropped by NATs/firewalls on long-lived remote connections.
11
- super(new PgDialect(dialectOptionsFrom(extra)), new Pool({ keepAlive: true, types: numericTypes(types), ...opts }), extra);
12
- }
13
- buildQuerier(connect) {
14
- return new PgQuerier(connect, this.dialect, this.extra);
10
+ super(new PostgresDialect(dialectOptionsFrom(extra)), new Pool({ keepAlive: true, types: numericTypes(types), ...opts }), extra);
15
11
  }
16
12
  }
@@ -1,11 +1,11 @@
1
1
  import { PgLikeSqlDialect } from '../dialect/pgLikeSqlDialect.js';
2
2
  import type { QueryConflictPaths, QueryContext, SqlDialectName, Type } from '../type/index.js';
3
3
  /**
4
- * PostgreSQL dialect. For node-pg use PgDialect. Neon, Bun SQL, and Cockroach use driver-specific
5
- * subclasses. Shared Postgres-wire AST/quoting/JSONB/full-text-search/vector-search logic
6
- * (including BIGINT IDENTITY PKs) lives in {@link PgLikeSqlDialect}; this class adds what's
7
- * Postgres-only: the `vector` extension requirement, pgvector's index syntax, and `xmax`-based
8
- * upsert `created` detection.
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
9
  */
10
10
  export declare class PostgresDialect extends PgLikeSqlDialect {
11
11
  readonly dialectName: SqlDialectName;
@@ -2,11 +2,11 @@ import { COUNT_ALIAS } from '../dialect/aliases.js';
2
2
  import { PgLikeSqlDialect } from '../dialect/pgLikeSqlDialect.js';
3
3
  import { getMeta } from '../entity/index.js';
4
4
  /**
5
- * PostgreSQL dialect. For node-pg use PgDialect. Neon, Bun SQL, and Cockroach use driver-specific
6
- * subclasses. Shared Postgres-wire AST/quoting/JSONB/full-text-search/vector-search logic
7
- * (including BIGINT IDENTITY PKs) lives in {@link PgLikeSqlDialect}; this class adds what's
8
- * Postgres-only: the `vector` extension requirement, pgvector's index syntax, and `xmax`-based
9
- * upsert `created` detection.
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
10
  */
11
11
  export class PostgresDialect extends PgLikeSqlDialect {
12
12
  dialectName = 'postgres';
@@ -9,11 +9,11 @@
9
9
  *
10
10
  * The pair is one constant because it is one driver's shape, and `BunSqlQuerierPool` hands it to
11
11
  * `PostgresDialect`/`CockroachDialect` as their `driverCapabilities` rather than subclassing either:
12
- * Bun changes how a parameter binds, never the SQL. `PgDialect` uses neither, keeping the base
12
+ * Bun changes how a parameter binds, never the SQL. `PgQuerierPool` uses neither, keeping the base
13
13
  * {@link PgLikeSqlDialect} defaults, since node-`pg` needs no fix.
14
14
  *
15
15
  * @remarks Optional import for custom pools. Neon uses its own serverless driver (not `bun:sql`),
16
- * so `NeonDialect` is a separate, unverified case - do not assume it needs this without testing.
16
+ * so `NeonQuerierPool` is a separate, unverified case - do not assume it needs this without testing.
17
17
  */
18
18
  export declare const POSTGRES_WIRE_DRIVER_CAPABILITIES: {
19
19
  readonly nativeArrays: false;
@@ -9,11 +9,11 @@
9
9
  *
10
10
  * The pair is one constant because it is one driver's shape, and `BunSqlQuerierPool` hands it to
11
11
  * `PostgresDialect`/`CockroachDialect` as their `driverCapabilities` rather than subclassing either:
12
- * Bun changes how a parameter binds, never the SQL. `PgDialect` uses neither, keeping the base
12
+ * Bun changes how a parameter binds, never the SQL. `PgQuerierPool` uses neither, keeping the base
13
13
  * {@link PgLikeSqlDialect} defaults, since node-`pg` needs no fix.
14
14
  *
15
15
  * @remarks Optional import for custom pools. Neon uses its own serverless driver (not `bun:sql`),
16
- * so `NeonDialect` is a separate, unverified case - do not assume it needs this without testing.
16
+ * so `NeonQuerierPool` is a separate, unverified case - do not assume it needs this without testing.
17
17
  */
18
18
  export const POSTGRES_WIRE_DRIVER_CAPABILITIES = {
19
19
  nativeArrays: false,
@@ -6,7 +6,7 @@ export declare abstract class AbstractPoolQuerier<C> extends AbstractSqlQuerier
6
6
  readonly extra?: ExtraOptions | undefined;
7
7
  protected conn: C | undefined;
8
8
  protected getConn(): C;
9
- constructor(dialect: AbstractSqlDialect, connect: () => Promise<C>, extra?: ExtraOptions | undefined);
9
+ constructor(connect: () => Promise<C>, dialect: AbstractSqlDialect, extra?: ExtraOptions | undefined);
10
10
  protected lazyConnect(): Promise<void>;
11
11
  internalRelease(discard: boolean): Promise<void>;
12
12
  protected abstract releaseConn(conn: C, discard: boolean): Promise<void>;
@@ -8,7 +8,7 @@ export class AbstractPoolQuerier extends AbstractSqlQuerier {
8
8
  throw new TypeError('pool querier not connected');
9
9
  return this.conn;
10
10
  }
11
- constructor(dialect, connect, extra) {
11
+ constructor(connect, dialect, extra) {
12
12
  super(dialect, extra);
13
13
  this.connect = connect;
14
14
  this.extra = extra;
@@ -129,14 +129,21 @@ export declare abstract class AbstractSqlQuerier extends AbstractQuerier impleme
129
129
  protected internalDeleteMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, opts?: QueryOptions): Promise<number>;
130
130
  get hasOpenTransaction(): boolean;
131
131
  beginTransaction(opts?: TransactionOptions): Promise<void>;
132
+ /**
133
+ * Only an end that succeeded ends the transaction. A `COMMIT` that fails can leave it open (SQLite
134
+ * answers `SQLITE_BUSY` and keeps it), so the flag has to stay set for the `catch` in
135
+ * {@link AbstractQuerier.transaction} or {@link AbstractQuerier.release} to roll it back.
136
+ */
132
137
  commitTransaction(): Promise<void>;
133
138
  rollbackTransaction(): Promise<void>;
134
139
  /**
135
- * Only a statement that succeeded ends the transaction. A `COMMIT` that fails can leave it open
136
- * (SQLite answers `SQLITE_BUSY` and keeps it), so the flag has to stay set for the `catch` in
137
- * {@link AbstractQuerier.transaction} or {@link AbstractQuerier.release} to roll it back.
140
+ * How this driver opens, commits and rolls back: the dialect's statements, unless its transactions
141
+ * are objects rather than statements - Hrana's session handle, `mssql`'s `Transaction` - in which
142
+ * case it overrides all three. The bookkeeping around them stays above, written once.
138
143
  */
139
- private endTransactionWith;
144
+ protected internalBegin(opts?: TransactionOptions): Promise<void>;
145
+ protected internalCommit(): Promise<void>;
146
+ protected internalRollback(): Promise<void>;
140
147
  /** Transaction statements skip `timed()`, so they attach their own query context to a failure. */
141
148
  private runTransactionCommand;
142
149
  }
@@ -241,18 +241,19 @@ export class AbstractSqlQuerier extends AbstractQuerier {
241
241
  // The one path that does not go through `all`/`run`, so it connects on its own: streaming first on
242
242
  // a freshly acquired querier used to reach `getConn()` with nothing acquired.
243
243
  await this.lazyConnect();
244
+ // No `normalizeValues` here, unlike `all`/`run`: those also take raw SQL, while every value a
245
+ // context holds was normalized as it was bound.
244
246
  const ctx = this.dialect.createContext();
245
247
  this.dialect.find(ctx, entity, q, opts);
246
- const normalizedParams = this.dialect.normalizeValues(ctx.values);
247
248
  let attrsPaths;
248
249
  try {
249
- for await (const row of this.internalStream(ctx.sql, normalizedParams)) {
250
+ for await (const row of this.internalStream(ctx.sql, ctx.values)) {
250
251
  attrsPaths ??= obtainAttrsPaths(row);
251
252
  yield this.hydrateFields(entity, unflatObject(row, attrsPaths));
252
253
  }
253
254
  }
254
255
  catch (err) {
255
- throw enrichError(err, this.logger, ctx.sql, normalizedParams);
256
+ throw enrichError(err, this.logger, ctx.sql, ctx.values);
256
257
  }
257
258
  }
258
259
  /**
@@ -261,8 +262,7 @@ export class AbstractSqlQuerier extends AbstractQuerier {
261
262
  * Drivers with native cursor/streaming APIs (SQLite, Pg) should override this.
262
263
  */
263
264
  async *internalStream(query, values) {
264
- const rows = await this.internalAll(query, this.dialect.normalizeValues(values));
265
- yield* rows;
265
+ yield* await this.internalAll(query, values);
266
266
  }
267
267
  /**
268
268
  * Turn what a driver returned back into the types the entity declares, for the row and everything
@@ -504,35 +504,47 @@ export class AbstractSqlQuerier extends AbstractQuerier {
504
504
  throwPendingTransaction();
505
505
  }
506
506
  await this.lazyConnect();
507
- for (const sql of this.dialect.getBeginTransactionStatements(opts?.isolationLevel)) {
508
- await this.runTransactionCommand(sql);
509
- }
507
+ await this.internalBegin(opts);
510
508
  this.hasPendingTransaction = true;
511
509
  });
512
510
  }
511
+ /**
512
+ * Only an end that succeeded ends the transaction. A `COMMIT` that fails can leave it open (SQLite
513
+ * answers `SQLITE_BUSY` and keeps it), so the flag has to stay set for the `catch` in
514
+ * {@link AbstractQuerier.transaction} or {@link AbstractQuerier.release} to roll it back.
515
+ */
513
516
  async commitTransaction() {
514
517
  return this.serialize(async () => {
515
518
  if (!this.hasPendingTransaction) {
516
519
  throwNoPendingTransaction();
517
520
  }
518
- await this.endTransactionWith(this.dialect.commitTransactionCommand);
521
+ await this.internalCommit();
522
+ this.hasPendingTransaction = false;
519
523
  });
520
524
  }
521
525
  async rollbackTransaction() {
522
526
  return this.serialize(async () => {
523
527
  if (this.hasPendingTransaction) {
524
- await this.endTransactionWith(this.dialect.rollbackTransactionCommand);
528
+ await this.internalRollback();
529
+ this.hasPendingTransaction = false;
525
530
  }
526
531
  });
527
532
  }
528
533
  /**
529
- * Only a statement that succeeded ends the transaction. A `COMMIT` that fails can leave it open
530
- * (SQLite answers `SQLITE_BUSY` and keeps it), so the flag has to stay set for the `catch` in
531
- * {@link AbstractQuerier.transaction} or {@link AbstractQuerier.release} to roll it back.
534
+ * How this driver opens, commits and rolls back: the dialect's statements, unless its transactions
535
+ * are objects rather than statements - Hrana's session handle, `mssql`'s `Transaction` - in which
536
+ * case it overrides all three. The bookkeeping around them stays above, written once.
532
537
  */
533
- async endTransactionWith(command) {
534
- await this.runTransactionCommand(command);
535
- this.hasPendingTransaction = false;
538
+ async internalBegin(opts) {
539
+ for (const sql of this.dialect.getBeginTransactionStatements(opts?.isolationLevel)) {
540
+ await this.runTransactionCommand(sql);
541
+ }
542
+ }
543
+ internalCommit() {
544
+ return this.runTransactionCommand(this.dialect.commitTransactionCommand);
545
+ }
546
+ internalRollback() {
547
+ return this.runTransactionCommand(this.dialect.rollbackTransactionCommand);
536
548
  }
537
549
  /** Transaction statements skip `timed()`, so they attach their own query context to a failure. */
538
550
  async runTransactionCommand(sql) {
@@ -1,4 +1,4 @@
1
- import type { ExtraOptions, TransactionOptions } from '../type/index.js';
1
+ import type { ExtraOptions } from '../type/index.js';
2
2
  import { AbstractSqliteQuerier, type SqliteBindValue } from './abstractSqliteQuerier.js';
3
3
  import type { SqliteDialect } from './sqliteDialect.js';
4
4
  /**
@@ -46,14 +46,12 @@ export declare class HranaQuerier extends AbstractSqliteQuerier {
46
46
  constructor(client: HranaClient, dialect: SqliteDialect, extra?: ExtraOptions | undefined, connection?: HranaQuerierConnectionOptions);
47
47
  internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
48
48
  internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
49
- get hasOpenTransaction(): boolean;
50
- beginTransaction(_opts?: TransactionOptions): Promise<void>;
49
+ protected internalBegin(): Promise<void>;
51
50
  /**
52
- * Both drop the handle before the call, not after: one that outlived a failed commit or rollback left
53
- * the querier unreleasable. The optional call in the rollback is also what makes it a no-op when
54
- * there is nothing open.
51
+ * Both drop the handle before the call, not after: one that outlived a failed commit would carry
52
+ * every later statement into a transaction the server may already have ended.
55
53
  */
56
- commitTransaction(): Promise<void>;
57
- rollbackTransaction(): Promise<void>;
54
+ protected internalCommit(): Promise<void>;
55
+ protected internalRollback(): Promise<void>;
58
56
  internalRelease(): Promise<void>;
59
57
  }
@@ -1,4 +1,3 @@
1
- import { throwNoPendingTransaction, throwPendingTransaction } from '../util/index.js';
2
1
  import { AbstractSqliteQuerier } from './abstractSqliteQuerier.js';
3
2
  /**
4
3
  * Querier for SQLite databases reached through a Hrana client.
@@ -30,38 +29,22 @@ export class HranaQuerier extends AbstractSqliteQuerier {
30
29
  // the actual row count when rows were returned.
31
30
  return this.buildUpdateResult({ rows, changes: rows.length || res.rowsAffected, id: res.lastInsertRowid });
32
31
  }
33
- get hasOpenTransaction() {
34
- return !!this.tx;
35
- }
36
- async beginTransaction(_opts) {
37
- return this.serialize(async () => {
38
- if (this.tx) {
39
- throwPendingTransaction();
40
- }
41
- this.tx = await this.client.transaction('write');
42
- });
32
+ async internalBegin() {
33
+ this.tx = await this.client.transaction('write');
43
34
  }
44
35
  /**
45
- * Both drop the handle before the call, not after: one that outlived a failed commit or rollback left
46
- * the querier unreleasable. The optional call in the rollback is also what makes it a no-op when
47
- * there is nothing open.
36
+ * Both drop the handle before the call, not after: one that outlived a failed commit would carry
37
+ * every later statement into a transaction the server may already have ended.
48
38
  */
49
- async commitTransaction() {
50
- return this.serialize(async () => {
51
- const tx = this.tx;
52
- if (!tx) {
53
- throwNoPendingTransaction();
54
- }
55
- this.tx = undefined;
56
- await tx.commit();
57
- });
58
- }
59
- async rollbackTransaction() {
60
- return this.serialize(async () => {
61
- const tx = this.tx;
62
- this.tx = undefined;
63
- await tx?.rollback();
64
- });
39
+ async internalCommit() {
40
+ const tx = this.tx;
41
+ this.tx = undefined;
42
+ await tx?.commit();
43
+ }
44
+ async internalRollback() {
45
+ const tx = this.tx;
46
+ this.tx = undefined;
47
+ await tx?.rollback();
65
48
  }
66
49
  async internalRelease() {
67
50
  await super.internalRelease();
@@ -1,5 +1,5 @@
1
1
  import { AbstractSqlQuerierPool } from '../querier/index.js';
2
- import type { HranaClient, HranaQuerier, HranaQuerierConnectionOptions } from './hranaQuerier.js';
2
+ import { type HranaClient, HranaQuerier } from './hranaQuerier.js';
3
3
  import type { SqliteDialect } from './sqliteDialect.js';
4
4
  /**
5
5
  * Pool for SQLite databases reached over the Hrana wire protocol (`@libsql/client`,
@@ -10,12 +10,11 @@ import type { SqliteDialect } from './sqliteDialect.js';
10
10
  * constructor, so building a pool never throws when the optional driver peer is absent, which is what
11
11
  * lets a Workers bundle construct one at module scope.
12
12
  */
13
- export declare abstract class AbstractHranaQuerierPool<Q extends HranaQuerier, D extends SqliteDialect> extends AbstractSqlQuerierPool<Q, D> {
13
+ export declare abstract class AbstractHranaQuerierPool<D extends SqliteDialect> extends AbstractSqlQuerierPool<HranaQuerier, D> {
14
14
  private client?;
15
15
  /** False when the caller injected their own client, in which case they own its lifecycle. */
16
16
  protected readonly ownsClient: boolean;
17
17
  protected abstract openClient(): Promise<HranaClient>;
18
- protected abstract buildQuerier(client: HranaClient, connection?: HranaQuerierConnectionOptions): Q;
19
- getQuerier(): Promise<Q>;
18
+ getQuerier(): Promise<HranaQuerier>;
20
19
  end(): Promise<void>;
21
20
  }
@@ -1,4 +1,5 @@
1
1
  import { AbstractSqlQuerierPool } from '../querier/index.js';
2
+ import { HranaQuerier } from './hranaQuerier.js';
2
3
  /**
3
4
  * Pool for SQLite databases reached over the Hrana wire protocol (`@libsql/client`,
4
5
  * `@tursodatabase/serverless/compat`).
@@ -14,7 +15,7 @@ export class AbstractHranaQuerierPool extends AbstractSqlQuerierPool {
14
15
  ownsClient = true;
15
16
  async getQuerier() {
16
17
  this.client ??= await this.openClient();
17
- return this.buildQuerier(this.client);
18
+ return new HranaQuerier(this.client, this.dialect, this.extra);
18
19
  }
19
20
  async end() {
20
21
  if (this.ownsClient) {
@@ -15,13 +15,13 @@ export type LocalSqlitePoolOptions = {
15
15
  * Pool for a SQLite database opened in this process, whichever driver provides it. SQLite gives one
16
16
  * connection per file, so the shared-handle lifecycle is {@link AbstractSharedHandleQuerierPool}'s.
17
17
  *
18
- * Subclasses supply only {@link createDb}: loading the extensions on the way up is the same for
19
- * `better-sqlite3`, `bun:sqlite` and `node:sqlite`, and was written out once per pool before.
18
+ * Subclasses supply only {@link createDb}: configuring the connection on the way up - the pragmas,
19
+ * then the extensions - is the same for `better-sqlite3`, `bun:sqlite` and `node:sqlite`.
20
20
  */
21
21
  export declare abstract class AbstractLocalSqliteQuerierPool<O extends LocalSqlitePoolOptions> extends AbstractSharedHandleQuerierPool<SqliteDatabase, SqliteQuerier, SqliteDialect> {
22
22
  readonly opts?: O | undefined;
23
23
  constructor(opts?: O | undefined, extra?: ExtraOptions);
24
- /** Opens the driver's database. Extensions are loaded by the caller, not here. */
24
+ /** Opens the driver's database, and nothing more: the caller configures it. */
25
25
  protected abstract createDb(): Promise<SqliteDatabase>;
26
26
  protected openDb(): Promise<SqliteDatabase>;
27
27
  protected buildQuerier(db: SqliteDatabase): SqliteQuerier;
@@ -1,13 +1,14 @@
1
1
  import { dialectOptionsFrom } from '../dialect/abstractDialect.js';
2
2
  import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandleQuerierPool.js';
3
3
  import { SqliteDialect } from './sqliteDialect.js';
4
+ import { applySqlitePragmas } from './sqlitePragmas.js';
4
5
  import { SqliteQuerier } from './sqliteQuerier.js';
5
6
  /**
6
7
  * Pool for a SQLite database opened in this process, whichever driver provides it. SQLite gives one
7
8
  * connection per file, so the shared-handle lifecycle is {@link AbstractSharedHandleQuerierPool}'s.
8
9
  *
9
- * Subclasses supply only {@link createDb}: loading the extensions on the way up is the same for
10
- * `better-sqlite3`, `bun:sqlite` and `node:sqlite`, and was written out once per pool before.
10
+ * Subclasses supply only {@link createDb}: configuring the connection on the way up - the pragmas,
11
+ * then the extensions - is the same for `better-sqlite3`, `bun:sqlite` and `node:sqlite`.
11
12
  */
12
13
  export class AbstractLocalSqliteQuerierPool extends AbstractSharedHandleQuerierPool {
13
14
  opts;
@@ -17,6 +18,7 @@ export class AbstractLocalSqliteQuerierPool extends AbstractSharedHandleQuerierP
17
18
  }
18
19
  async openDb() {
19
20
  const db = await this.createDb();
21
+ await applySqlitePragmas(db);
20
22
  for (const extension of this.opts?.extensions ?? []) {
21
23
  db.loadExtension(extension);
22
24
  }
@@ -19,7 +19,6 @@ type NodeSqliteStatement = {
19
19
  */
20
20
  export type NodeSqliteDatabase = {
21
21
  prepare(sql: string): NodeSqliteStatement;
22
- exec(sql: string): void;
23
22
  loadExtension(path: string): void;
24
23
  close(): void;
25
24
  };
@@ -23,8 +23,6 @@ export class NodeSqliteQuerierPool extends AbstractLocalSqliteQuerierPool {
23
23
  // `node:sqlite` refuses `loadExtension` unless the database was opened with this on.
24
24
  ...(extensions?.length ? { allowExtension: true } : undefined),
25
25
  });
26
- nodeDb.exec('PRAGMA journal_mode = WAL');
27
- nodeDb.exec('PRAGMA foreign_keys = ON');
28
26
  return adaptNodeSqlite(nodeDb);
29
27
  }
30
28
  }
@@ -0,0 +1,12 @@
1
+ import type { SqlitePreparedStatement } from './abstractSqliteQuerier.js';
2
+ /**
3
+ * How every local SQLite connection opens. WAL, so a reader does not wait on the writer, and
4
+ * `foreign_keys`, which SQLite ships off per connection for backward compatibility: without it the
5
+ * constraints uql's own DDL declares are decorative - a declared `onDelete: 'CASCADE'` silently does
6
+ * nothing and a dangling reference is accepted.
7
+ */
8
+ export declare const SQLITE_PRAGMAS: readonly ['journal_mode = WAL', 'foreign_keys = ON'];
9
+ /** Runs {@link SQLITE_PRAGMAS} through a driver's own statements, whether it answers now or later. */
10
+ export declare function applySqlitePragmas(db: {
11
+ prepare(sql: string): SqlitePreparedStatement | Promise<SqlitePreparedStatement>;
12
+ }): Promise<void>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * How every local SQLite connection opens. WAL, so a reader does not wait on the writer, and
3
+ * `foreign_keys`, which SQLite ships off per connection for backward compatibility: without it the
4
+ * constraints uql's own DDL declares are decorative - a declared `onDelete: 'CASCADE'` silently does
5
+ * nothing and a dangling reference is accepted.
6
+ */
7
+ export const SQLITE_PRAGMAS = ['journal_mode = WAL', 'foreign_keys = ON'];
8
+ /** Runs {@link SQLITE_PRAGMAS} through a driver's own statements, whether it answers now or later. */
9
+ export async function applySqlitePragmas(db) {
10
+ for (const pragma of SQLITE_PRAGMAS) {
11
+ const stmt = await db.prepare(`PRAGMA ${pragma}`);
12
+ // `journal_mode` answers with a row and `foreign_keys` with none; `reader` picks the call for each.
13
+ await (stmt.reader ? stmt.all() : stmt.run());
14
+ }
15
+ }
@@ -11,10 +11,5 @@ export type Sqlite3PoolOptions = Options & LocalSqlitePoolOptions;
11
11
  export declare class Sqlite3QuerierPool extends AbstractLocalSqliteQuerierPool<Sqlite3PoolOptions> {
12
12
  readonly filename: string | Buffer;
13
13
  constructor(filename?: string | Buffer, opts?: Sqlite3PoolOptions, extra?: ExtraOptions);
14
- /**
15
- * SQLite ships with foreign keys unenforced, per connection, for backward compatibility. UQL emits the
16
- * constraints in its DDL, so leaving them off means a declared `onDelete: 'CASCADE'` silently does
17
- * nothing and a dangling reference is accepted. Enabled here on every driver, as TypeORM also does.
18
- */
19
14
  protected createDb(): Promise<SqliteDatabase>;
20
15
  }
@@ -10,11 +10,6 @@ export class Sqlite3QuerierPool extends AbstractLocalSqliteQuerierPool {
10
10
  super(opts, extra);
11
11
  this.filename = filename;
12
12
  }
13
- /**
14
- * SQLite ships with foreign keys unenforced, per connection, for backward compatibility. UQL emits the
15
- * constraints in its DDL, so leaving them off means a declared `onDelete: 'CASCADE'` silently does
16
- * nothing and a dangling reference is accepted. Enabled here on every driver, as TypeORM also does.
17
- */
18
13
  async createDb() {
19
14
  // `bun:sqlite` rejects option keys it does not know, and rejects an options object carrying no
20
15
  // open flags, so `extensions` is stripped out and what remains of it collapses back to nothing.
@@ -23,15 +18,9 @@ export class Sqlite3QuerierPool extends AbstractLocalSqliteQuerierPool {
23
18
  if (typeof Bun !== 'undefined') {
24
19
  const { Database: BunDatabase } = await import('bun:sqlite');
25
20
  const { adaptBunSqlite } = await import('./bunSqliteAdapter.bun.js');
26
- const bunDb = new BunDatabase(this.filename, opts);
27
- bunDb.run('PRAGMA journal_mode = WAL');
28
- bunDb.run('PRAGMA foreign_keys = ON');
29
- return adaptBunSqlite(bunDb);
21
+ return adaptBunSqlite(new BunDatabase(this.filename, opts));
30
22
  }
31
23
  const { default: BetterSqlite3 } = await import('better-sqlite3');
32
- const db = new BetterSqlite3(this.filename, opts);
33
- db.pragma('journal_mode = WAL');
34
- db.pragma('foreign_keys = ON');
35
- return db;
24
+ return new BetterSqlite3(this.filename, opts);
36
25
  }
37
26
  }
@@ -1,3 +1,2 @@
1
1
  export * from './tursoDialect.js';
2
- export * from './tursoQuerier.js';
3
2
  export * from './tursoQuerierPool.js';
@@ -1,3 +1,2 @@
1
1
  export * from './tursoDialect.js';
2
- export * from './tursoQuerier.js';
3
2
  export * from './tursoQuerierPool.js';
@@ -7,7 +7,6 @@ import type { ExtraOptions } from '../type/index.js';
7
7
  */
8
8
  export type TursoDatabase = {
9
9
  prepare(sql: string): Promise<SqlitePreparedStatement>;
10
- pragma(source: string, options?: unknown): Promise<unknown[]>;
11
10
  close(): Promise<void>;
12
11
  };
13
12
  /**