uql-orm 0.64.0 → 0.65.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 (77) hide show
  1. package/dist/bunSql/bunSqlQuerier.d.ts +3 -10
  2. package/dist/bunSql/bunSqlQuerier.js +2 -16
  3. package/dist/bunSql/bunSqlQuerierPool.d.ts +0 -2
  4. package/dist/bunSql/bunSqlQuerierPool.js +14 -16
  5. package/dist/d1/d1Querier.d.ts +13 -34
  6. package/dist/d1/d1Querier.js +1 -1
  7. package/dist/d1/d1QuerierPool.d.ts +3 -3
  8. package/dist/dialect/abstractDialect.d.ts +4 -9
  9. package/dist/dialect/abstractDialect.js +4 -5
  10. package/dist/dialect/abstractSqlDialect.d.ts +2 -2
  11. package/dist/dialect/index.d.ts +0 -1
  12. package/dist/dialect/index.js +2 -3
  13. package/dist/dialect/mysqlLikeSqlDialect.js +0 -3
  14. package/dist/dialect/pgLikeSqlDialect.d.ts +9 -1
  15. package/dist/dialect/pgLikeSqlDialect.js +8 -5
  16. package/dist/dialect/queryContext.d.ts +3 -3
  17. package/dist/entity/metadata/definition.d.ts +6 -0
  18. package/dist/entity/metadata/definition.js +139 -94
  19. package/dist/migrate/cli.d.ts +1 -1
  20. package/dist/migrate/cli.js +20 -34
  21. package/dist/migrate/codegen/entityCodeGenerator.d.ts +5 -0
  22. package/dist/migrate/codegen/entityCodeGenerator.js +29 -7
  23. package/dist/migrate/codegen/indexDecoratorSource.js +1 -5
  24. package/dist/migrate/codegen/sourceLiteral.d.ts +2 -0
  25. package/dist/migrate/codegen/sourceLiteral.js +4 -0
  26. package/dist/migrate/index.d.ts +1 -1
  27. package/dist/migrate/index.js +1 -1
  28. package/dist/migrate/introspection/registry.d.ts +2 -2
  29. package/dist/migrate/introspection/registry.js +6 -11
  30. package/dist/migrate/migrationTarget.d.ts +6 -6
  31. package/dist/migrate/migrationTarget.js +18 -16
  32. package/dist/migrate/migrator.d.ts +3 -7
  33. package/dist/migrate/migrator.js +10 -31
  34. package/dist/migrate/schemaGenerator.d.ts +1 -3
  35. package/dist/migrate/schemaGenerator.js +0 -4
  36. package/dist/mongo/mongoDialect.d.ts +1 -1
  37. package/dist/mongo/mongoDialect.js +1 -4
  38. package/dist/mssql/mssqlDialect.js +0 -3
  39. package/dist/pglite/pgliteQuerier.d.ts +2 -6
  40. package/dist/pglite/pgliteQuerier.js +2 -9
  41. package/dist/postgres/index.d.ts +0 -1
  42. package/dist/postgres/index.js +0 -1
  43. package/dist/querier/abstractSharedHandleQuerierPool.d.ts +2 -5
  44. package/dist/querier/abstractSharedHandleQuerierPool.js +2 -5
  45. package/dist/querier/abstractSqlQuerier.d.ts +2 -3
  46. package/dist/querier/abstractSqlQuerier.js +8 -4
  47. package/dist/querier/cursorStream.d.ts +13 -0
  48. package/dist/{postgres/pgCursorStream.js → querier/cursorStream.js} +4 -13
  49. package/dist/schema/canonicalType.js +1 -1
  50. package/dist/schema/schemaASTBuilder.js +34 -40
  51. package/dist/sqlite/abstractSqliteQuerier.d.ts +7 -3
  52. package/dist/sqlite/abstractSqliteQuerier.js +18 -4
  53. package/dist/sqlite/hranaQuerier.d.ts +1 -1
  54. package/dist/sqlite/localSqliteQuerierPool.d.ts +7 -0
  55. package/dist/sqlite/localSqliteQuerierPool.js +19 -0
  56. package/dist/sqlite/nodeSqliteQuerierPool.js +2 -3
  57. package/dist/sqlite/sqliteDialect.js +0 -3
  58. package/dist/sqlite/sqliteQuerier.d.ts +8 -5
  59. package/dist/sqlite/sqliteQuerier.js +4 -13
  60. package/dist/sqlite/sqliteQuerierPool.js +4 -4
  61. package/dist/turso/tursoSessionQuerier.d.ts +2 -2
  62. package/dist/turso/tursoSessionQuerier.js +16 -18
  63. package/dist/type/dialect.d.ts +25 -35
  64. package/dist/type/entity.d.ts +29 -32
  65. package/dist/type/migratorDialect.d.ts +0 -9
  66. package/dist/type/migratorDialect.js +1 -16
  67. package/dist/type/querierPool.d.ts +0 -4
  68. package/dist/type/queryRaw.d.ts +2 -2
  69. package/dist/type/universalQuerier.d.ts +2 -2
  70. package/package.json +1 -3
  71. package/dist/postgres/pgCursorStream.d.ts +0 -20
  72. package/dist/postgres/postgresWireDriverCapabilities.d.ts +0 -21
  73. package/dist/postgres/postgresWireDriverCapabilities.js +0 -21
  74. package/dist/sqlite/bunSqliteAdapter.bun.d.ts +0 -27
  75. package/dist/sqlite/bunSqliteAdapter.bun.js +0 -25
  76. package/dist/sqlite/nodeSqliteAdapter.d.ts +0 -33
  77. package/dist/sqlite/nodeSqliteAdapter.js +0 -25
@@ -1,9 +1,9 @@
1
- import { isKnownMigratorDialect, isMongoQuerier, isSqlQuerier, } from '../type/index.js';
1
+ import { isMongoQuerier, isSqlQuerier, } from '../type/index.js';
2
2
  import { withMongoQuerierForMigrations, withSqlQuerierForMigrations } from './acquireQuerierForMigrations.js';
3
3
  import { MigrationBuilder } from './builder/migrationBuilder.js';
4
4
  import { migrationSource } from './codegen/migrationFile.js';
5
5
  import { runMongoCommand } from './generator/mongoCommand.js';
6
- import { createSchemaGenerator, SqlSchemaGenerator } from './schemaGenerator.js';
6
+ import { SqlSchemaGenerator } from './schemaGenerator.js';
7
7
  import { DatabaseMigrationStorage } from './storage/databaseStorage.js';
8
8
  import { MongoMigrationStorage } from './storage/mongoStorage.js';
9
9
  const sqlSession = (querier) => ({
@@ -21,20 +21,22 @@ async function mongoSchemaGenerator(namingStrategy, defaultForeignKeyAction) {
21
21
  const { MongoSchemaGenerator } = await import('./generator/mongoSchemaGenerator.js');
22
22
  return new MongoSchemaGenerator(namingStrategy, defaultForeignKeyAction);
23
23
  }
24
- const sqlTarget = {
25
- source: migrationSource.SqlQuerier,
26
- storage: (pool, tableName) => new DatabaseMigrationStorage(pool, { tableName }),
27
- generator: async (dialect, defaultForeignKeyAction) => isKnownMigratorDialect(dialect.dialectName) ? createSchemaGenerator(dialect, defaultForeignKeyAction) : undefined,
28
- withSession: (pool, task) => withSqlQuerierForMigrations(pool, 'Migrator', (querier) => task(sqlSession(querier))),
29
- };
30
- const mongoTarget = {
31
- source: migrationSource.MongoQuerier,
32
- storage: (pool, tableName) => new MongoMigrationStorage(pool, { tableName }),
33
- generator: (dialect, defaultForeignKeyAction) => mongoSchemaGenerator(dialect.namingStrategy, defaultForeignKeyAction),
34
- withSession: (pool, task) => withMongoQuerierForMigrations(pool, 'Migrator', (querier) => task(mongoSession(querier))),
35
- };
36
- export function migrationTargetFor(dialect) {
37
- return dialect.dialectName === 'mongodb' ? mongoTarget : sqlTarget;
24
+ export function migrationTargetFor(pool, defaultForeignKeyAction) {
25
+ const { dialect } = pool;
26
+ if (dialect.dialectName === 'mongodb') {
27
+ return {
28
+ source: migrationSource.MongoQuerier,
29
+ storage: (tableName) => new MongoMigrationStorage(pool, { tableName }),
30
+ generator: () => mongoSchemaGenerator(dialect.namingStrategy, defaultForeignKeyAction),
31
+ withSession: (task) => withMongoQuerierForMigrations(pool, 'Migrator', (querier) => task(mongoSession(querier))),
32
+ };
33
+ }
34
+ return {
35
+ source: migrationSource.SqlQuerier,
36
+ storage: (tableName) => new DatabaseMigrationStorage(pool, { tableName }),
37
+ generator: async () => new SqlSchemaGenerator(dialect, defaultForeignKeyAction),
38
+ withSession: (task) => withSqlQuerierForMigrations(pool, 'Migrator', (querier) => task(sqlSession(querier))),
39
+ };
38
40
  }
39
41
  /** A builder running each operation on `querier`, as SQL or as MongoDB driver commands. */
40
42
  export async function migrationBuilderFor(querier) {
@@ -1,4 +1,4 @@
1
- import type { DialectName, LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorDialect, MigratorOptions, Querier, QuerierPool, SchemaDiff, SchemaGenerator, SchemaIntrospector, SqlQuerier, SyncOptions, Type } from '../type/index.js';
1
+ import type { LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorDialect, MigratorOptions, Querier, QuerierPool, SchemaDiff, SchemaGenerator, SchemaIntrospector, SqlQuerier, SyncOptions, Type } from '../type/index.js';
2
2
  import { LoggerWrapper } from '../util/index.js';
3
3
  import type { IMigrationBuilder } from './builder/types.js';
4
4
  /**
@@ -13,17 +13,13 @@ export declare class Migrator {
13
13
  set logger(value: LoggingOptions);
14
14
  private readonly _entities?;
15
15
  get entities(): Type<object>[];
16
- readonly dialectName: DialectName;
17
16
  /** The generator given, or this dialect's once {@link getSchemaGenerator} has loaded it. */
18
17
  schemaGenerator?: SchemaGenerator;
19
- schemaIntrospector?: SchemaIntrospector;
20
- private readonly defaultForeignKeyAction?;
18
+ schemaIntrospector: SchemaIntrospector;
21
19
  private readonly target;
22
20
  constructor(pool: QuerierPool<Querier, MigratorDialect>, options?: MigratorOptions);
23
21
  /** The schema generator, loaded on first use: MongoDB's needs its optional peer. */
24
22
  getSchemaGenerator(): Promise<SchemaGenerator>;
25
- /** `schema` reads one namespace instead of the connection's own; see {@link BaseSqlIntrospector.schema}. */
26
- protected createIntrospector(schema?: string): SchemaIntrospector | undefined;
27
23
  /**
28
24
  * Get all discovered migrations from the migrations directory
29
25
  */
@@ -112,7 +108,7 @@ export declare class Migrator {
112
108
  /** The configured entities, with `entity` among them however the migrator was built. */
113
109
  private entitiesWith;
114
110
  /** The introspector for a claimed schema, which is the connection's own where none is claimed. */
115
- private introspectorFor;
111
+ private schemaIntrospectorFor;
116
112
  /**
117
113
  * The DDL {@link sync} would run, without running it. Separate so `--dry-run` shows the real
118
114
  * statements rather than a summary of a second, differently-computed diff.
@@ -25,36 +25,25 @@ export class Migrator {
25
25
  get entities() {
26
26
  return this._entities ?? getEntities();
27
27
  }
28
- dialectName;
29
28
  /** The generator given, or this dialect's once {@link getSchemaGenerator} has loaded it. */
30
29
  schemaGenerator;
31
30
  schemaIntrospector;
32
- defaultForeignKeyAction;
33
31
  target;
34
32
  constructor(pool, options = {}) {
35
33
  this.pool = pool;
36
- this.dialectName = pool.dialect.dialectName;
37
- this.target = migrationTargetFor(pool.dialect);
38
- this.defaultForeignKeyAction = options.defaultForeignKeyAction;
39
- this.storage = options.storage ?? this.target.storage(pool, options.tableName);
34
+ this.target = migrationTargetFor(pool, options.defaultForeignKeyAction);
35
+ this.storage = options.storage ?? this.target.storage(options.tableName);
40
36
  this.migrationsPath = options.migrationsPath ?? './migrations';
41
37
  this._logger = new LoggerWrapper(options.logger, { logValues: options.logValues, slowQuery: options.slowQuery });
42
38
  this._entities = options.entities;
43
- this.schemaIntrospector = this.createIntrospector();
39
+ this.schemaIntrospector = introspectorFor(pool);
44
40
  this.schemaGenerator = options.schemaGenerator;
45
41
  }
46
42
  /** The schema generator, loaded on first use: MongoDB's needs its optional peer. */
47
43
  async getSchemaGenerator() {
48
- this.schemaGenerator ??= await this.target.generator(this.pool.dialect, this.defaultForeignKeyAction);
49
- if (!this.schemaGenerator) {
50
- throw new TypeError(`No schema generator for dialect '${this.dialectName}'`);
51
- }
44
+ this.schemaGenerator ??= await this.target.generator();
52
45
  return this.schemaGenerator;
53
46
  }
54
- /** `schema` reads one namespace instead of the connection's own; see {@link BaseSqlIntrospector.schema}. */
55
- createIntrospector(schema) {
56
- return introspectorFor(this.dialectName, this.pool, schema);
57
- }
58
47
  /**
59
48
  * Get all discovered migrations from the migrations directory
60
49
  */
@@ -133,7 +122,7 @@ export class Migrator {
133
122
  */
134
123
  async runMigration(migration, direction) {
135
124
  const startTime = Date.now();
136
- return this.target.withSession(this.pool, async ({ querier, transaction }) => {
125
+ return this.target.withSession(async ({ querier, transaction }) => {
137
126
  try {
138
127
  this.logger.logMigration(`${direction === 'up' ? 'Running' : 'Reverting'} migration: ${migration.name}`);
139
128
  await transaction(async () => {
@@ -222,9 +211,6 @@ export class Migrator {
222
211
  */
223
212
  async getDiffs() {
224
213
  const generator = await this.getSchemaGenerator();
225
- if (!this.schemaIntrospector) {
226
- throw new TypeError(`No introspector for dialect '${this.dialectName}'`);
227
- }
228
214
  const ast = await this.introspectClaimedSchemas();
229
215
  // Both sides built once: the database's above, the entities' here. Left to `diffSchema`, each
230
216
  // entity would rebuild the whole AST, which is quadratic in the number of entities. Absent on a
@@ -247,11 +233,7 @@ export class Migrator {
247
233
  const claimed = new Set(this.entities.map((entity) => this.pool.dialect.resolveSchema(getMeta(entity))));
248
234
  const merged = new SchemaAST();
249
235
  for (const schema of claimed) {
250
- const introspector = this.introspectorFor(schema);
251
- if (!introspector) {
252
- continue;
253
- }
254
- for (const table of (await introspectSchema(introspector)).getTables()) {
236
+ for (const table of (await introspectSchema(this.schemaIntrospectorFor(schema))).getTables()) {
255
237
  merged.addTable(table);
256
238
  }
257
239
  }
@@ -294,10 +276,7 @@ export class Migrator {
294
276
  */
295
277
  async planEntity(generator, entity, options) {
296
278
  const meta = getMeta(entity);
297
- const introspector = this.introspectorFor(this.pool.dialect.resolveSchema(meta));
298
- if (!introspector) {
299
- throw new TypeError(`No introspector for '${meta.entity.name}' on '${this.dialectName}'`);
300
- }
279
+ const introspector = this.schemaIntrospectorFor(this.pool.dialect.resolveSchema(meta));
301
280
  const tableName = generator.resolveTableName(meta);
302
281
  return (await introspector.tableExists(tableName))
303
282
  ? this.alterFromEntity(generator, entity, (await introspectSchema(introspector)).getTable(tableName), options)
@@ -319,8 +298,8 @@ export class Migrator {
319
298
  return entities.includes(entity) ? entities : [...entities, entity];
320
299
  }
321
300
  /** The introspector for a claimed schema, which is the connection's own where none is claimed. */
322
- introspectorFor(schema) {
323
- return schema === undefined ? this.schemaIntrospector : this.createIntrospector(schema);
301
+ schemaIntrospectorFor(schema) {
302
+ return schema === undefined ? this.schemaIntrospector : introspectorFor(this.pool, schema);
324
303
  }
325
304
  /**
326
305
  * The DDL {@link sync} would run, without running it. Separate so `--dry-run` shows the real
@@ -399,7 +378,7 @@ export class Migrator {
399
378
  }
400
379
  /** Runs the statements a generator wrote, in one transaction where the engine takes DDL in one. */
401
380
  async executeSyncStatements(statements, options) {
402
- await this.target.withSession(this.pool, ({ run, transaction }) => transaction(async () => {
381
+ await this.target.withSession(({ run, transaction }) => transaction(async () => {
403
382
  for (const statement of statements) {
404
383
  if (options.logging)
405
384
  this.logger.logSchema(`Executing: ${statement}`);
@@ -2,7 +2,7 @@ import type { AbstractSqlDialect } from '../dialect/index.js';
2
2
  import type { SchemaAST } from '../schema/schemaAST.js';
3
3
  import { type DiffOptions } from '../schema/schemaASTDiffer.js';
4
4
  import type { CanonicalType, ColumnNode, ForeignKeyAction, IndexNode, TableNode } from '../schema/types.js';
5
- import type { ColumnSchema, CreateSchemaOptions, DialectFeatures, DropSchemaOptions, EntityMeta, EntityWhereMeta, FieldMeta, FieldOptions, ForeignKeySchema, IndexSchema, MigratorDialect, NamingStrategy, SchemaDiff, SchemaGenerator, Type } from '../type/index.js';
5
+ import type { ColumnSchema, CreateSchemaOptions, DialectFeatures, DropSchemaOptions, EntityMeta, EntityWhereMeta, FieldMeta, FieldOptions, ForeignKeySchema, IndexSchema, NamingStrategy, SchemaDiff, SchemaGenerator, Type } from '../type/index.js';
6
6
  import type { AnyMigrationOperation, FullColumnDefinition, IndexDefinition, TableDefinition } from './builder/types.js';
7
7
  import { type IndexDdl, type TableDdl } from './ddl/index.js';
8
8
  /**
@@ -222,5 +222,3 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
222
222
  * table of a project using a naming strategy as both missing and unexpected.
223
223
  */
224
224
  export declare function buildEntityAST(generator: Pick<SchemaGenerator, 'resolveTableAlias' | 'resolveSchema' | 'resolveColumnName' | 'compileDdl' | 'compileIndexPredicate'>, entities: readonly Type<object>[], defaultForeignKeyAction?: ForeignKeyAction): SchemaAST;
225
- /** The SQL schema generator for `dialect`, `undefined` on MongoDB, whose generator needs its optional peer. */
226
- export declare function createSchemaGenerator(dialect: MigratorDialect, defaultForeignKeyAction?: ForeignKeyAction): SqlSchemaGenerator | undefined;
@@ -766,7 +766,3 @@ export function buildEntityAST(generator, entities, defaultForeignKeyAction) {
766
766
  defaultForeignKeyAction,
767
767
  });
768
768
  }
769
- /** The SQL schema generator for `dialect`, `undefined` on MongoDB, whose generator needs its optional peer. */
770
- export function createSchemaGenerator(dialect, defaultForeignKeyAction) {
771
- return dialect.dialectName === 'mongodb' ? undefined : new SqlSchemaGenerator(dialect, defaultForeignKeyAction);
772
- }
@@ -15,7 +15,7 @@ type RelationLookups = {
15
15
  readonly stages: MongoAggregationPipelineEntry<Document>[];
16
16
  readonly temps: string[];
17
17
  };
18
- /** Default {@link DialectFeatures} for MongoDB; shared by {@link MongoDialect} and its schema generator. */
18
+ /** Default {@link DialectFeatures} for MongoDB. */
19
19
  export declare const mongoDialectFeatures: DialectFeatures;
20
20
  export declare class MongoDialect extends AbstractDialect {
21
21
  #private;
@@ -6,11 +6,8 @@ import { assertSoleId, fieldOf, getMeta, relationOf, soleIdOf } from '../entity/
6
6
  import { COUNT_RESULT_KEY } from '../type/query.js';
7
7
  import { QueryRaw } from '../type/queryRaw.js';
8
8
  import { asSelectMap, assertAggregateColumns, assertNonNegativeInteger, columnFamily, countedRelations, entityName, fillOnFields, filterFieldKeys, findVectorIndex, findVectorSort, getKeys, getRelationRequestSummary, hasKeys, isJsonUpdateOp, isOperatorMap, isOperatorObject, isVectorSearch, normalizeScalarFieldSelection, parentJoins, parseGroupMap, parseRelationAtKey, parseRelationSize, parseSortByCount, someKey, targetKeyColumns, } from '../util/index.js';
9
- /** Default {@link DialectFeatures} for MongoDB; shared by {@link MongoDialect} and its schema generator. */
9
+ /** Default {@link DialectFeatures} for MongoDB. */
10
10
  export const mongoDialectFeatures = {
11
- explicitJsonCast: false,
12
- nativeArrays: false,
13
- supportsJsonb: false,
14
11
  ifNotExists: false,
15
12
  indexIfNotExists: false,
16
13
  schemas: false, // the connection picks the database, and a collection name takes no dot
@@ -20,9 +20,6 @@ import { escapeSingleQuotes } from '../util/sqlLiteral.js';
20
20
  */
21
21
  export class MsSqlDialect extends MergeSqlDialect {
22
22
  featureDefaults = {
23
- explicitJsonCast: false,
24
- nativeArrays: false,
25
- supportsJsonb: false,
26
23
  // Neither object takes an `IF NOT EXISTS`; both need a `sys` catalogue lookup around them, which
27
24
  // the generator does not emit.
28
25
  ifNotExists: false,
@@ -20,10 +20,8 @@ export type PgliteDatabase = {
20
20
  /**
21
21
  * Querier for PGlite, Postgres compiled to WASM and run in this process.
22
22
  *
23
- * @remarks Extends {@link AbstractSqlQuerier} rather than `PgQuerier`, whose `internalStream`
24
- * hands a `pg-query-stream` object to `query()`, which PGlite's client has no equivalent of - so
25
- * streaming pages the rows in SQL instead. `BEGIN`/`COMMIT` are plain statements on the single
26
- * connection, leaving transactions to the base class.
23
+ * @remarks Extends {@link AbstractSqlQuerier} rather than `PgQuerier`, whose stream needs `pg-query-stream`,
24
+ * which PGlite's client cannot run. `BEGIN`/`COMMIT` are plain statements on the single connection.
27
25
  */
28
26
  export declare class PgliteQuerier extends AbstractSqlQuerier {
29
27
  readonly db: PgliteDatabase;
@@ -31,8 +29,6 @@ export declare class PgliteQuerier extends AbstractSqlQuerier {
31
29
  constructor(db: PgliteDatabase, dialect: PostgresDialect, extra?: ExtraOptions | undefined);
32
30
  internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
33
31
  internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
34
- /** Postgres compiled to WASM is still Postgres: `DECLARE`/`FETCH` streams what the client cannot. */
35
- protected internalStream<T>(query: string, values?: unknown[]): AsyncGenerator<Awaited<T>, void, any>;
36
32
  /** The handle belongs to the pool, which hands out one querier per unit of work over it. */
37
33
  internalRelease(): Promise<void>;
38
34
  }
@@ -1,12 +1,9 @@
1
- import { streamViaCursor } from '../postgres/pgCursorStream.js';
2
1
  import { AbstractSqlQuerier } from '../querier/index.js';
3
2
  /**
4
3
  * Querier for PGlite, Postgres compiled to WASM and run in this process.
5
4
  *
6
- * @remarks Extends {@link AbstractSqlQuerier} rather than `PgQuerier`, whose `internalStream`
7
- * hands a `pg-query-stream` object to `query()`, which PGlite's client has no equivalent of - so
8
- * streaming pages the rows in SQL instead. `BEGIN`/`COMMIT` are plain statements on the single
9
- * connection, leaving transactions to the base class.
5
+ * @remarks Extends {@link AbstractSqlQuerier} rather than `PgQuerier`, whose stream needs `pg-query-stream`,
6
+ * which PGlite's client cannot run. `BEGIN`/`COMMIT` are plain statements on the single connection.
10
7
  */
11
8
  export class PgliteQuerier extends AbstractSqlQuerier {
12
9
  db;
@@ -26,10 +23,6 @@ export class PgliteQuerier extends AbstractSqlQuerier {
26
23
  // where the latter also counts a `SELECT`'s rows and is absent altogether from a DDL tag.
27
24
  return this.buildUpdateResult({ rows: res.rows, changes: res.affectedRows ?? 0 });
28
25
  }
29
- /** Postgres compiled to WASM is still Postgres: `DECLARE`/`FETCH` streams what the client cannot. */
30
- async *internalStream(query, values) {
31
- yield* streamViaCursor((sql, params) => this.internalAll(sql, params), query, values, this.hasOpenTransaction);
32
- }
33
26
  /** The handle belongs to the pool, which hands out one querier per unit of work over it. */
34
27
  async internalRelease() { }
35
28
  }
@@ -1,4 +1,3 @@
1
1
  export * from './pgQuerier.js';
2
2
  export * from './pgQuerierPool.js';
3
3
  export * from './postgresDialect.js';
4
- export * from './postgresWireDriverCapabilities.js';
@@ -1,4 +1,3 @@
1
1
  export * from './pgQuerier.js';
2
2
  export * from './pgQuerierPool.js';
3
3
  export * from './postgresDialect.js';
4
- export * from './postgresWireDriverCapabilities.js';
@@ -15,13 +15,10 @@ import { AbstractSqlQuerierPool } from './abstractSqlQuerierPool.js';
15
15
  * into the transaction already open - see {@link PgliteQuerierPool}, which is why that one is worth
16
16
  * saying out loud.
17
17
  *
18
- * Subclasses supply only how to open the handle and how to wrap it, the way {@link AbstractPgQuerierPool}
19
- * takes `buildQuerier` alone. The lazy open and the close were written out once per pool before, along
20
- * with three partial copies of the paragraph above.
18
+ * Subclasses supply only how to open the handle and how to wrap it.
21
19
  *
22
20
  * @remarks Deliberately not re-exported from `querier/index.ts`, which the root entry point re-exports:
23
- * only the three driver entries need this, and each imports it by path, as `postgres/abstractPgQuerier.ts`
24
- * is imported.
21
+ * only the driver entries that open a single handle need this, and each imports it by path.
25
22
  */
26
23
  export declare abstract class AbstractSharedHandleQuerierPool<DB extends {
27
24
  close(): unknown;
@@ -13,13 +13,10 @@ import { AbstractSqlQuerierPool } from './abstractSqlQuerierPool.js';
13
13
  * into the transaction already open - see {@link PgliteQuerierPool}, which is why that one is worth
14
14
  * saying out loud.
15
15
  *
16
- * Subclasses supply only how to open the handle and how to wrap it, the way {@link AbstractPgQuerierPool}
17
- * takes `buildQuerier` alone. The lazy open and the close were written out once per pool before, along
18
- * with three partial copies of the paragraph above.
16
+ * Subclasses supply only how to open the handle and how to wrap it.
19
17
  *
20
18
  * @remarks Deliberately not re-exported from `querier/index.ts`, which the root entry point re-exports:
21
- * only the three driver entries need this, and each imports it by path, as `postgres/abstractPgQuerier.ts`
22
- * is imported.
19
+ * only the driver entries that open a single handle need this, and each imports it by path.
23
20
  */
24
21
  export class AbstractSharedHandleQuerierPool extends AbstractSqlQuerierPool {
25
22
  /**
@@ -80,9 +80,8 @@ export declare abstract class AbstractSqlQuerier extends AbstractQuerier impleme
80
80
  private hydrateRows;
81
81
  protected internalFindManyStream<E extends object>(entity: Type<E>, q: Query<E>, opts?: QueryOptions): AsyncGenerator<Awaited<E>, void, unknown>;
82
82
  /**
83
- * Internal streaming query - returns an async iterable of raw rows.
84
- * Default implementation falls back to `internalAll()` then yields each row.
85
- * Drivers with native cursor/streaming APIs (SQLite, Pg) should override this.
83
+ * A read's rows one at a time: paged through a server-side cursor where the engine has one, read whole
84
+ * where it has none. A driver that streams on its own overrides this.
86
85
  */
87
86
  protected internalStream<T>(query: string, values?: unknown[]): AsyncIterable<T>;
88
87
  /**
@@ -4,6 +4,7 @@ import { getMeta, idOf, namesKey } from '../entity/index.js';
4
4
  import { COUNT_RESULT_KEY } from '../type/index.js';
5
5
  import { buildUpdateResult, cascadesOnDelete, clone, getInsertFieldKeys, insertShapeOf, idOnlyQuery, isAutoIncrement, isPagedQuery, isRecord, obtainAttrsPaths, throwNoPendingTransaction, throwPendingTransaction, unflatObject, unflatObjects, whereIds, withoutSoftDeleteFilter, } from '../util/index.js';
6
6
  import { AbstractQuerier } from './abstractQuerier.js';
7
+ import { streamViaCursor } from './cursorStream.js';
7
8
  import { enrichError } from './queryError.js';
8
9
  /**
9
10
  * Row indexes grouped by whether the caller supplied the key, payload order kept within each group.
@@ -240,12 +241,15 @@ export class AbstractSqlQuerier extends AbstractQuerier {
240
241
  }
241
242
  }
242
243
  /**
243
- * Internal streaming query - returns an async iterable of raw rows.
244
- * Default implementation falls back to `internalAll()` then yields each row.
245
- * Drivers with native cursor/streaming APIs (SQLite, Pg) should override this.
244
+ * A read's rows one at a time: paged through a server-side cursor where the engine has one, read whole
245
+ * where it has none. A driver that streams on its own overrides this.
246
246
  */
247
247
  async *internalStream(query, values) {
248
- yield* await this.internalAll(query, values);
248
+ if (!this.dialect.features.serverSideCursors) {
249
+ yield* await this.internalAll(query, values);
250
+ return;
251
+ }
252
+ yield* streamViaCursor((sql, params) => this.internalAll(sql, params), query, values, this.hasOpenTransaction);
249
253
  }
250
254
  /**
251
255
  * Turn what a driver returned back into the types the entity declares, for every row and everything
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Runs one statement of the cursor protocol on the querier's own connection, which holds the cursor, and
3
+ * not through `all()`, whose `serialize` is not re-entrant.
4
+ */
5
+ type CursorExecutor<T> = (query: string, values?: unknown[]) => Promise<T[]>;
6
+ /**
7
+ * Streams a Postgres-wire read through a server-side cursor. `DECLARE` needs a transaction, so one is
8
+ * opened on the connection itself when the caller has none, leaving the querier's state untouched.
9
+ * Cleanup sits in `finally`, which a consumer that stops early still reaches, and is best-effort once
10
+ * the read failed, so the read's error stays the one thrown.
11
+ */
12
+ export declare function streamViaCursor<T>(exec: CursorExecutor<T>, query: string, values?: unknown[], inTransaction?: boolean): AsyncIterable<T>;
13
+ export {};
@@ -4,17 +4,10 @@ const CURSOR_ALIAS = '_uql_cursor';
4
4
  const FETCH_SIZE = 100;
5
5
  let cursorSeq = 0;
6
6
  /**
7
- * Stream a Postgres-wire result through a server-side cursor, for a driver whose client exposes none:
8
- * `bun:sql` (no cursor API at all, [oven-sh/bun#17181](https://github.com/oven-sh/bun/issues/17181))
9
- * and PGlite. `pg` has `pg-query-stream` and keeps using it.
10
- *
11
- * `DECLARE` is only legal inside a transaction, so one is opened here when the caller has none - and
12
- * then committed, or rolled back if the stream failed. That `BEGIN` goes straight to the connection
13
- * rather than through `beginTransaction`, so the querier's own transaction state stays untouched:
14
- * this one is the generator's, and ends with it.
15
- *
16
- * The cleanup lives in `finally` because a consumer that stops early (`break`, a `throw` downstream)
17
- * ends the generator there and nowhere else, and an abandoned cursor holds its transaction open.
7
+ * Streams a Postgres-wire read through a server-side cursor. `DECLARE` needs a transaction, so one is
8
+ * opened on the connection itself when the caller has none, leaving the querier's state untouched.
9
+ * Cleanup sits in `finally`, which a consumer that stops early still reaches, and is best-effort once
10
+ * the read failed, so the read's error stays the one thrown.
18
11
  */
19
12
  export async function* streamViaCursor(exec, query, values, inTransaction = false) {
20
13
  const cursor = `${CURSOR_ALIAS}_${++cursorSeq}`;
@@ -38,8 +31,6 @@ export async function* streamViaCursor(exec, query, values, inTransaction = fals
38
31
  throw err;
39
32
  }
40
33
  finally {
41
- // Best-effort once the stream has failed: an error raised here would replace the one that brought
42
- // us here, which is the one worth reporting, and the rollback discards the cursor either way.
43
34
  const end = failed ? (sql) => exec(sql).catch(() => []) : exec;
44
35
  await end(`CLOSE ${cursor}`);
45
36
  if (ownsTransaction) {
@@ -338,7 +338,7 @@ export function canonicalToSql(type, dialect) {
338
338
  }
339
339
  return type.unsigned && features.supportsUnsigned ? `${sqlType} UNSIGNED` : sqlType;
340
340
  }
341
- /** See {@link EngineFeatures.stringSizing} for what each mode means. */
341
+ /** See {@link DialectFeatures.stringSizing} for what each mode means. */
342
342
  function formatStringSqlType(type, base, sizing) {
343
343
  if (sizing === 'text') {
344
344
  return base;
@@ -5,7 +5,7 @@
5
5
  * - Entity metadata (decorator-based entities)
6
6
  * - Database introspection results (TableSchema[])
7
7
  */
8
- import { getMeta, soleIdOf } from '../entity/metadata/definition.js';
8
+ import { foreignKeysOf, getMeta, soleIdOf } from '../entity/metadata/definition.js';
9
9
  import { declaredIndexes, indexNameParts, renderIndexColumn } from '../util/ddlExpression.util.js';
10
10
  import { isInlinedExpression } from '../util/field.util.js';
11
11
  import { isSoleIdField } from '../util/field.util.js';
@@ -124,54 +124,48 @@ function tableOf(ctx, meta) {
124
124
  return ctx.ast.getTable(qualifyName(ctx.resolveTableName(meta), ctx.resolveSchema(meta)));
125
125
  }
126
126
  /**
127
- * Add relationships from entity relation decorators.
127
+ * Add a relationship for each foreign key the entity holds, whether a relation declares it or a bare
128
+ * `@Field({ references })` does.
128
129
  */
129
130
  function addRelationshipsFromEntity(ctx, meta) {
130
131
  const table = tableOf(ctx, meta);
131
132
  if (!table)
132
133
  return;
133
- for (const [key, relation] of definedEntries(meta.relations)) {
134
- const relatedMeta = getMeta(relation.entity());
134
+ for (const foreignKey of foreignKeysOf(meta)) {
135
+ const relatedMeta = getMeta(foreignKey.entity());
135
136
  const relatedTable = tableOf(ctx, relatedMeta);
136
137
  if (!relatedTable)
137
138
  continue;
138
- // Only the owning side gets the FK. `mappedBy` marks the inverse side of a one-to-one, whose
139
- // `references` describe how to join back (its own primary key against the owner's FK column) -
140
- // reading those as a foreign key emitted a reversed constraint (`User(id) REFERENCES
141
- // user_profile(creatorId)`), which SQLite rejects outright as a foreign key mismatch.
142
- const ownsForeignKey = relation.cardinality === 'm1' || (relation.cardinality === '11' && !relation.mappedBy);
143
- if (ownsForeignKey) {
144
- // Every pair, not just the first: a composite key is one constraint over all its columns, and
145
- // the engine requires the referenced columns to match a unique constraint as a whole.
146
- const localColumns = [];
147
- const foreignColumns = [];
148
- for (const { local: localProp, foreign: foreignProp } of relation.references) {
149
- const localField = meta.fields[localProp];
150
- const foreignField = relatedMeta.fields[foreignProp];
151
- const localColumn = localField && table.columns.get(ctx.resolveColumnName(localProp, localField));
152
- const foreignColumn = foreignField && relatedTable.columns.get(ctx.resolveColumnName(foreignProp, foreignField));
153
- if (!localColumn || !foreignColumn)
154
- break;
155
- localColumns.push(localColumn);
156
- foreignColumns.push(foreignColumn);
157
- }
158
- // A pair that cannot be resolved drops the whole constraint: half of one enforces a rule
159
- // nobody declared, over a subset of the key.
160
- if (localColumns.length !== relation.references.length)
161
- continue;
162
- ctx.ast.addRelationship({
163
- name: derivedForeignKeyName(table.name, localColumns.map((column) => column.name)),
164
- type: relation.cardinality === 'm1' ? 'ManyToOne' : 'OneToOne',
165
- from: { table, columns: localColumns },
166
- to: { table: relatedTable, columns: foreignColumns },
167
- // Falls back to the FK column's own `onDelete`, which is what makes a bare `@Field({
168
- // references, onDelete })` work with no relation declared at all.
169
- onDelete: relation.onDelete ?? meta.fields[relation.references[0].local]?.onDelete ?? ctx.defaultForeignKeyAction,
170
- onUpdate: relation.onUpdate ?? ctx.defaultForeignKeyAction,
171
- confidence: 1.0,
172
- inferredFrom: 'entity_decorator',
173
- });
139
+ // Every pair, not just the first: a composite key is one constraint over all its columns, and
140
+ // the engine requires the referenced columns to match a unique constraint as a whole.
141
+ const localColumns = [];
142
+ const foreignColumns = [];
143
+ for (const { local: localProp, foreign: foreignProp } of foreignKey.references) {
144
+ const localField = meta.fields[localProp];
145
+ const foreignField = relatedMeta.fields[foreignProp];
146
+ const localColumn = localField && table.columns.get(ctx.resolveColumnName(localProp, localField));
147
+ const foreignColumn = foreignField && relatedTable.columns.get(ctx.resolveColumnName(foreignProp, foreignField));
148
+ if (!localColumn || !foreignColumn)
149
+ break;
150
+ localColumns.push(localColumn);
151
+ foreignColumns.push(foreignColumn);
174
152
  }
153
+ // A pair that cannot be resolved drops the whole constraint: half of one enforces a rule
154
+ // nobody declared, over a subset of the key.
155
+ if (localColumns.length !== foreignKey.references.length)
156
+ continue;
157
+ ctx.ast.addRelationship({
158
+ name: derivedForeignKeyName(table.name, localColumns.map((column) => column.name)),
159
+ type: foreignKey.cardinality === 'm1' ? 'ManyToOne' : 'OneToOne',
160
+ from: { table, columns: localColumns },
161
+ to: { table: relatedTable, columns: foreignColumns },
162
+ // Falls back to the FK column's own `onDelete`, which is what makes a bare `@Field({
163
+ // references, onDelete })` work with no relation declared at all.
164
+ onDelete: foreignKey.onDelete ?? meta.fields[foreignKey.references[0].local]?.onDelete ?? ctx.defaultForeignKeyAction,
165
+ onUpdate: foreignKey.onUpdate ?? ctx.defaultForeignKeyAction,
166
+ confidence: 1.0,
167
+ inferredFrom: 'entity_decorator',
168
+ });
175
169
  }
176
170
  }
177
171
  /**
@@ -15,14 +15,18 @@ export type SqliteExecution = {
15
15
  readonly changes: number;
16
16
  };
17
17
  /**
18
- * Querier for every SQLite driver. Each supplies one hook, {@link execute}; what they share - the
19
- * wide-integer decode, and a RETURNING statement counted by its rows - is written once here.
18
+ * Querier for every SQLite driver. Each supplies {@link execute}, and {@link iterate} where it steps a
19
+ * statement row by row; what they share - the values bound as SQLite takes them, the wide-integer
20
+ * decode, and a RETURNING statement counted by its rows - is written once here.
20
21
  */
21
22
  export declare abstract class AbstractSqliteQuerier extends AbstractSqlQuerier {
22
23
  /** Runs one statement, answering its rows as the driver read them. */
23
- protected abstract execute(query: string, values?: unknown[]): Promise<SqliteExecution>;
24
+ protected abstract execute(query: string, values: SqliteBindValue[]): Promise<SqliteExecution>;
25
+ /** A statement's rows as the driver answers them: read whole, unless it steps them one at a time. */
26
+ protected iterate(query: string, values: SqliteBindValue[]): Promise<Iterable<RawRow> | AsyncIterable<RawRow>>;
24
27
  internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
25
28
  internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
29
+ internalStream<T>(query: string, values?: unknown[]): AsyncGenerator<Awaited<T>, void, unknown>;
26
30
  /**
27
31
  * SQLite drivers hold a single shared handle rather than a connection from a pool, so releasing
28
32
  * a querier returns nothing at all. Drivers owning a closable per-querier connection override this.
@@ -1,22 +1,36 @@
1
1
  import { AbstractSqlQuerier } from '../querier/index.js';
2
2
  import { decodeBigInts } from '../util/wideNumber.js';
3
3
  /**
4
- * Querier for every SQLite driver. Each supplies one hook, {@link execute}; what they share - the
5
- * wide-integer decode, and a RETURNING statement counted by its rows - is written once here.
4
+ * Querier for every SQLite driver. Each supplies {@link execute}, and {@link iterate} where it steps a
5
+ * statement row by row; what they share - the values bound as SQLite takes them, the wide-integer
6
+ * decode, and a RETURNING statement counted by its rows - is written once here.
6
7
  */
7
8
  export class AbstractSqliteQuerier extends AbstractSqlQuerier {
9
+ /** A statement's rows as the driver answers them: read whole, unless it steps them one at a time. */
10
+ async iterate(query, values) {
11
+ return (await this.execute(query, values)).rows;
12
+ }
8
13
  async internalAll(query, values) {
9
- const { rows } = await this.execute(query, values);
14
+ const { rows } = await this.execute(query, toBindValues(values));
10
15
  return rows.map(decodeBigInts);
11
16
  }
12
17
  async internalRun(query, values) {
13
- const { rows, changes } = await this.execute(query, values);
18
+ const { rows, changes } = await this.execute(query, toBindValues(values));
14
19
  // A driver's own count is unreliable for a RETURNING statement (Hrana answers 0), so its rows answer.
15
20
  return this.buildUpdateResult({ rows: rows.map(decodeBigInts), changes: rows.length || changes });
16
21
  }
22
+ async *internalStream(query, values) {
23
+ for await (const row of await this.iterate(query, toBindValues(values))) {
24
+ yield decodeBigInts(row);
25
+ }
26
+ }
17
27
  /**
18
28
  * SQLite drivers hold a single shared handle rather than a connection from a pool, so releasing
19
29
  * a querier returns nothing at all. Drivers owning a closable per-querier connection override this.
20
30
  */
21
31
  async internalRelease() { }
22
32
  }
33
+ /** The compiler hands values over untyped, which the dialect has already normalized to what SQLite binds. */
34
+ function toBindValues(values = []) {
35
+ return values;
36
+ }
@@ -44,7 +44,7 @@ export declare class HranaQuerier extends AbstractSqliteQuerier {
44
44
  private readonly closeClientOnRelease;
45
45
  constructor(client: HranaClient, dialect: SqliteDialect, extra?: ExtraOptions | undefined, connection?: HranaQuerierConnectionOptions);
46
46
  /** Runs on the open transaction's handle when there is one. */
47
- protected execute(query: string, values?: unknown[]): Promise<{
47
+ protected execute(query: string, values: SqliteBindValue[]): Promise<{
48
48
  rows: RawRow[];
49
49
  changes: number;
50
50
  }>;