uql-orm 0.33.0 → 0.34.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 (52) hide show
  1. package/dist/bunSql/bunSqlQuerierPool.js +2 -1
  2. package/dist/cockroachdb/crdbQuerierPool.js +2 -1
  3. package/dist/d1/d1QuerierPool.js +2 -1
  4. package/dist/dialect/abstractDialect.d.ts +27 -3
  5. package/dist/dialect/abstractDialect.js +34 -4
  6. package/dist/dialect/abstractSqlDialect.d.ts +27 -1
  7. package/dist/dialect/abstractSqlDialect.js +74 -36
  8. package/dist/dialect/mysqlLikeSqlDialect.js +1 -0
  9. package/dist/dialect/pgLikeSqlDialect.js +1 -0
  10. package/dist/entity/metadata/definition.js +9 -0
  11. package/dist/http/handler.js +16 -1
  12. package/dist/libsql/libsqlQuerierPool.js +2 -1
  13. package/dist/maria/mariadbQuerierPool.js +2 -1
  14. package/dist/migrate/builder/migrationBuilder.js +3 -1
  15. package/dist/migrate/builder/tableBuilder.js +2 -1
  16. package/dist/migrate/generator/definitionToNode.js +3 -3
  17. package/dist/migrate/generator/mongoSchemaGenerator.js +7 -6
  18. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +12 -1
  19. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +16 -2
  20. package/dist/migrate/introspection/baseSqlIntrospector.d.ts +7 -1
  21. package/dist/migrate/introspection/baseSqlIntrospector.js +11 -12
  22. package/dist/migrate/introspection/mongoIntrospector.js +0 -1
  23. package/dist/migrate/introspection/mysqlIntrospector.d.ts +1 -0
  24. package/dist/migrate/introspection/mysqlIntrospector.js +8 -6
  25. package/dist/migrate/introspection/postgresIntrospector.d.ts +1 -0
  26. package/dist/migrate/introspection/postgresIntrospector.js +6 -5
  27. package/dist/migrate/migrator.d.ts +10 -1
  28. package/dist/migrate/migrator.js +30 -8
  29. package/dist/migrate/schemaGenerator.d.ts +16 -6
  30. package/dist/migrate/schemaGenerator.js +53 -24
  31. package/dist/mongo/mongoDialect.js +5 -4
  32. package/dist/mongo/mongodbQuerierPool.js +2 -1
  33. package/dist/mysql/mysql2QuerierPool.js +2 -1
  34. package/dist/neon/neonQuerierPool.js +2 -1
  35. package/dist/pglite/pgliteQuerierPool.js +2 -1
  36. package/dist/postgres/pgQuerierPool.js +2 -1
  37. package/dist/schema/schemaAST.d.ts +8 -1
  38. package/dist/schema/schemaAST.js +22 -13
  39. package/dist/schema/schemaASTBuilder.d.ts +5 -3
  40. package/dist/schema/schemaASTBuilder.js +24 -31
  41. package/dist/schema/types.d.ts +10 -3
  42. package/dist/sqlite/localSqliteQuerierPool.js +2 -1
  43. package/dist/sqlite/sqliteDialect.js +2 -1
  44. package/dist/turso/tursoLocalQuerierPool.js +2 -1
  45. package/dist/turso/tursoQuerierPool.js +2 -1
  46. package/dist/type/dialect.d.ts +6 -0
  47. package/dist/type/entity.d.ts +7 -0
  48. package/dist/type/migration.d.ts +16 -2
  49. package/dist/type/querier.d.ts +5 -0
  50. package/dist/util/sql.util.d.ts +17 -0
  51. package/dist/util/sql.util.js +23 -0
  52. package/package.json +1 -1
@@ -2,6 +2,7 @@ import { AbstractDialect } from '../../dialect/abstractDialect.js';
2
2
  import { getMeta } from '../../entity/index.js';
3
3
  import { mongoDialectFeatures } from '../../mongo/mongoDialect.js';
4
4
  import { getKeys } from '../../util/index.js';
5
+ import { derivedIndexName } from '../../util/sql.util.js';
5
6
  import { indexNodeToSchema } from './indexNodeToSchema.js';
6
7
  import { serializeMongoCommand } from './mongoCommand.js';
7
8
  export class MongoSchemaGenerator extends AbstractDialect {
@@ -22,24 +23,24 @@ export class MongoSchemaGenerator extends AbstractDialect {
22
23
  return this.selected(entities, options?.only).flatMap((entity) => this.generateCreateTable(entity, options));
23
24
  }
24
25
  generateDropSchema(entities) {
25
- return this.selected(entities).map((entity) => this.generateDropTable(this.resolveTableName(entity, getMeta(entity))));
26
+ return this.selected(entities).map((entity) => this.generateDropTable(this.resolveTableName(getMeta(entity))));
26
27
  }
27
28
  selected(entities, only) {
28
29
  if (!only) {
29
30
  return entities;
30
31
  }
31
32
  const wanted = new Set(only);
32
- return entities.filter((entity) => wanted.has(this.resolveTableName(entity, getMeta(entity))));
33
+ return entities.filter((entity) => wanted.has(this.resolveTableName(getMeta(entity))));
33
34
  }
34
35
  generateCreateTable(entity, _options) {
35
36
  const meta = getMeta(entity);
36
- const collectionName = this.resolveTableName(entity, meta);
37
+ const collectionName = this.resolveTableName(meta);
37
38
  const indexes = [];
38
39
  for (const key of getKeys(meta.fields)) {
39
40
  const field = meta.fields[key];
40
41
  if (field?.index) {
41
42
  const columnName = this.resolveColumnName(key, field);
42
- const indexName = typeof field.index === 'string' ? field.index : `idx_${collectionName}_${columnName}`;
43
+ const indexName = typeof field.index === 'string' ? field.index : derivedIndexName(collectionName, [columnName]);
43
44
  indexes.push({
44
45
  name: indexName,
45
46
  entries: [{ column: columnName }],
@@ -132,7 +133,7 @@ export class MongoSchemaGenerator extends AbstractDialect {
132
133
  }
133
134
  diffSchema(entity, currentTable) {
134
135
  const meta = getMeta(entity);
135
- const collectionName = this.resolveTableName(entity, meta);
136
+ const collectionName = this.resolveTableName(meta);
136
137
  if (!currentTable) {
137
138
  return { tableName: collectionName, type: 'create' };
138
139
  }
@@ -142,7 +143,7 @@ export class MongoSchemaGenerator extends AbstractDialect {
142
143
  const field = meta.fields[key];
143
144
  if (field?.index) {
144
145
  const columnName = this.resolveColumnName(key, field);
145
- const indexName = typeof field.index === 'string' ? field.index : `idx_${collectionName}_${columnName}`;
146
+ const indexName = typeof field.index === 'string' ? field.index : derivedIndexName(collectionName, [columnName]);
146
147
  if (!existingIndexes.has(indexName)) {
147
148
  indexesToAdd.push({
148
149
  name: indexName,
@@ -36,7 +36,18 @@ export type TableRowReader = <T extends RawRow>(sql: string, params?: unknown[])
36
36
  */
37
37
  export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector implements SchemaIntrospector {
38
38
  protected readonly pool: QuerierPool;
39
- constructor(pool: QuerierPool);
39
+ constructor(pool: QuerierPool, schema?: string);
40
+ /**
41
+ * The schema every catalogue query filters on, as SQL: the one that was asked for, or the engine's
42
+ * expression for the connection's default. A literal rather than a bind parameter because these
43
+ * queries are assembled as text and several use it more than once.
44
+ */
45
+ protected get schemaExpr(): string;
46
+ /**
47
+ * How this engine names the connection's current schema (Postgres) or database (MySQL). Empty on
48
+ * an engine with no schemas, whose catalogue queries never reference one.
49
+ */
50
+ protected readonly defaultSchemaExpr: string;
40
51
  getTableSchema(tableName: string): Promise<TableSchema | undefined>;
41
52
  getTableNames(): Promise<string[]>;
42
53
  tableExists(tableName: string): Promise<boolean>;
@@ -1,4 +1,5 @@
1
1
  import { isSqlQuerier } from '../../type/index.js';
2
+ import { escapeAnsiSqlLiteral } from '../../util/sqlLiteral.js';
2
3
  import { BaseSqlIntrospector } from './baseSqlIntrospector.js';
3
4
  /**
4
5
  * Abstract base class for SQL schema introspectors.
@@ -21,10 +22,23 @@ import { BaseSqlIntrospector } from './baseSqlIntrospector.js';
21
22
  */
22
23
  export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
23
24
  pool;
24
- constructor(pool) {
25
- super(pool.dialect);
25
+ constructor(pool, schema) {
26
+ super(pool.dialect, schema);
26
27
  this.pool = pool;
27
28
  }
29
+ /**
30
+ * The schema every catalogue query filters on, as SQL: the one that was asked for, or the engine's
31
+ * expression for the connection's default. A literal rather than a bind parameter because these
32
+ * queries are assembled as text and several use it more than once.
33
+ */
34
+ get schemaExpr() {
35
+ return this.schema === undefined ? this.defaultSchemaExpr : escapeAnsiSqlLiteral(this.schema);
36
+ }
37
+ /**
38
+ * How this engine names the connection's current schema (Postgres) or database (MySQL). Empty on
39
+ * an engine with no schemas, whose catalogue queries never reference one.
40
+ */
41
+ defaultSchemaExpr = '';
28
42
  async getTableSchema(tableName) {
29
43
  return this.withSqlQuerier(async (querier) => {
30
44
  const read = createTableRowReader(querier);
@@ -7,9 +7,15 @@ import type { TableSchema } from '../../type/migration.js';
7
7
  */
8
8
  export declare abstract class BaseSqlIntrospector {
9
9
  protected readonly dialect: AbstractSqlDialect;
10
+ readonly schema?: string | undefined;
10
11
  /** Columns and uniqueness only; each introspector opts in to what its catalogue queries report. */
11
12
  readonly indexFacets: ReadonlySet<IndexFacet>;
12
- constructor(dialect: AbstractSqlDialect);
13
+ /**
14
+ * The one schema these queries read, `undefined` for the connection's own default. Every table
15
+ * reported is stamped with it, so a diff compares like with like: entity and database both say
16
+ * `undefined` for "wherever the connection points", and name a schema only when one was asked for.
17
+ */
18
+ constructor(dialect: AbstractSqlDialect, schema?: string | undefined);
13
19
  protected escapeId(identifier: string): string;
14
20
  /**
15
21
  * Introspect entire database schema and return SchemaAST.
@@ -1,15 +1,22 @@
1
1
  import { canonicalColumnType } from '../../schema/canonicalType.js';
2
- import { SchemaAST } from '../../schema/schemaAST.js';
2
+ import { createTableNode, SchemaAST } from '../../schema/schemaAST.js';
3
3
  import { escapeSqlId } from '../../util/index.js';
4
4
  /**
5
5
  * Base class for SQL introspectors with shared AST building logic.
6
6
  */
7
7
  export class BaseSqlIntrospector {
8
8
  dialect;
9
+ schema;
9
10
  /** Columns and uniqueness only; each introspector opts in to what its catalogue queries report. */
10
11
  indexFacets = new Set();
11
- constructor(dialect) {
12
+ /**
13
+ * The one schema these queries read, `undefined` for the connection's own default. Every table
14
+ * reported is stamped with it, so a diff compares like with like: entity and database both say
15
+ * `undefined` for "wherever the connection points", and name a schema only when one was asked for.
16
+ */
17
+ constructor(dialect, schema) {
12
18
  this.dialect = dialect;
19
+ this.schema = schema;
13
20
  }
14
21
  escapeId(identifier) {
15
22
  return escapeSqlId(identifier, this.dialect.escapeIdChar);
@@ -41,16 +48,8 @@ export class BaseSqlIntrospector {
41
48
  }
42
49
  buildTables(ast, tableNodes, tableSchemas) {
43
50
  for (const schema of tableSchemas) {
44
- const columns = new Map();
45
- const table = {
46
- name: schema.name,
47
- columns,
48
- primaryKey: [],
49
- indexes: [],
50
- schema: ast,
51
- incomingRelations: [],
52
- outgoingRelations: [],
53
- };
51
+ const table = createTableNode(schema.name, this.schema);
52
+ const { columns } = table;
54
53
  for (const col of schema.columns) {
55
54
  const column = {
56
55
  name: col.name,
@@ -22,7 +22,6 @@ export class MongoSchemaIntrospector {
22
22
  columns,
23
23
  primaryKey: [],
24
24
  indexes: [],
25
- schema: ast,
26
25
  incomingRelations: [],
27
26
  outgoingRelations: [],
28
27
  };
@@ -5,6 +5,7 @@ import { AbstractSqlSchemaIntrospector, type TableRowReader } from './abstractSq
5
5
  * Works with both MySQL and MariaDB as they share the same information_schema structure.
6
6
  */
7
7
  export declare class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
8
+ protected readonly defaultSchemaExpr = "DATABASE()";
8
9
  protected getTableNamesQuery(): string;
9
10
  protected tableExistsQuery(): string;
10
11
  protected parseTableExistsResult(results: {
@@ -4,11 +4,13 @@ import { AbstractSqlSchemaIntrospector } from './abstractSqlSchemaIntrospector.j
4
4
  * Works with both MySQL and MariaDB as they share the same information_schema structure.
5
5
  */
6
6
  export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
7
+ // A MySQL "schema" is a database, so the connection's own is what `DATABASE()` reports.
8
+ defaultSchemaExpr = 'DATABASE()';
7
9
  getTableNamesQuery() {
8
10
  return /*sql*/ `
9
11
  SELECT TABLE_NAME as table_name
10
12
  FROM information_schema.TABLES
11
- WHERE TABLE_SCHEMA = DATABASE()
13
+ WHERE TABLE_SCHEMA = ${this.schemaExpr}
12
14
  AND TABLE_TYPE = 'BASE TABLE'
13
15
  ORDER BY TABLE_NAME
14
16
  `;
@@ -17,7 +19,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
17
19
  return /*sql*/ `
18
20
  SELECT COUNT(*) as count
19
21
  FROM information_schema.TABLES
20
- WHERE TABLE_SCHEMA = DATABASE()
22
+ WHERE TABLE_SCHEMA = ${this.schemaExpr}
21
23
  AND TABLE_NAME = ?
22
24
  `;
23
25
  }
@@ -43,7 +45,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
43
45
  EXTRA as extra,
44
46
  COLUMN_COMMENT as column_comment
45
47
  FROM information_schema.COLUMNS
46
- WHERE TABLE_SCHEMA = DATABASE()
48
+ WHERE TABLE_SCHEMA = ${this.schemaExpr}
47
49
  AND TABLE_NAME = ?
48
50
  ORDER BY ORDINAL_POSITION
49
51
  `;
@@ -55,7 +57,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
55
57
  GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) as columns,
56
58
  NOT NON_UNIQUE as is_unique
57
59
  FROM information_schema.STATISTICS
58
- WHERE TABLE_SCHEMA = DATABASE()
60
+ WHERE TABLE_SCHEMA = ${this.schemaExpr}
59
61
  AND TABLE_NAME = ?
60
62
  AND INDEX_NAME != 'PRIMARY'
61
63
  GROUP BY INDEX_NAME, NON_UNIQUE
@@ -75,7 +77,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
75
77
  JOIN information_schema.REFERENTIAL_CONSTRAINTS rc
76
78
  ON kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
77
79
  AND kcu.TABLE_SCHEMA = rc.CONSTRAINT_SCHEMA
78
- WHERE kcu.TABLE_SCHEMA = DATABASE()
80
+ WHERE kcu.TABLE_SCHEMA = ${this.schemaExpr}
79
81
  AND kcu.TABLE_NAME = ?
80
82
  AND kcu.REFERENCED_TABLE_NAME IS NOT NULL
81
83
  GROUP BY kcu.CONSTRAINT_NAME, kcu.REFERENCED_TABLE_NAME, rc.DELETE_RULE, rc.UPDATE_RULE
@@ -86,7 +88,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
86
88
  return /*sql*/ `
87
89
  SELECT COLUMN_NAME as column_name
88
90
  FROM information_schema.KEY_COLUMN_USAGE
89
- WHERE TABLE_SCHEMA = DATABASE()
91
+ WHERE TABLE_SCHEMA = ${this.schemaExpr}
90
92
  AND TABLE_NAME = ?
91
93
  AND CONSTRAINT_NAME = 'PRIMARY'
92
94
  ORDER BY ORDINAL_POSITION
@@ -5,6 +5,7 @@ import { AbstractSqlSchemaIntrospector, type TableRowReader } from './abstractSq
5
5
  * PostgreSQL schema introspector
6
6
  */
7
7
  export declare class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
8
+ protected readonly defaultSchemaExpr = "current_schema()";
8
9
  /**
9
10
  * Expressions and predicates are read back too, for `generate:from-db`, but they are text the
10
11
  * database reprints in its own words, so they are not comparable and are not claimed here.
@@ -4,6 +4,7 @@ import { AbstractSqlSchemaIntrospector } from './abstractSqlSchemaIntrospector.j
4
4
  * PostgreSQL schema introspector
5
5
  */
6
6
  export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
7
+ defaultSchemaExpr = 'current_schema()';
7
8
  /**
8
9
  * Expressions and predicates are read back too, for `generate:from-db`, but they are text the
9
10
  * database reprints in its own words, so they are not comparable and are not claimed here.
@@ -19,7 +20,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
19
20
  return /*sql*/ `
20
21
  SELECT table_name
21
22
  FROM information_schema.tables
22
- WHERE table_schema = 'public'
23
+ WHERE table_schema = ${this.schemaExpr}
23
24
  AND table_type = 'BASE TABLE'
24
25
  ORDER BY table_name
25
26
  `;
@@ -28,7 +29,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
28
29
  return /*sql*/ `
29
30
  SELECT EXISTS (
30
31
  SELECT FROM information_schema.tables
31
- WHERE table_schema = 'public'
32
+ WHERE table_schema = ${this.schemaExpr}
32
33
  AND table_name = $1
33
34
  ) AS exists
34
35
  `;
@@ -74,7 +75,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
74
75
  c.ordinal_position
75
76
  ) AS column_comment
76
77
  FROM information_schema.columns c
77
- WHERE c.table_schema = 'public'
78
+ WHERE c.table_schema = ${this.schemaExpr}
78
79
  AND c.table_name = $1
79
80
  ORDER BY c.ordinal_position
80
81
  `;
@@ -152,7 +153,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
152
153
  AND rc.constraint_schema = tc.table_schema
153
154
  WHERE tc.constraint_type = 'FOREIGN KEY'
154
155
  AND tc.table_name = $1
155
- AND tc.table_schema = 'public'
156
+ AND tc.table_schema = ${this.schemaExpr}
156
157
  GROUP BY tc.constraint_name, ccu.table_name, rc.delete_rule, rc.update_rule
157
158
  ORDER BY tc.constraint_name
158
159
  `;
@@ -166,7 +167,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
166
167
  AND tc.table_schema = kcu.table_schema
167
168
  WHERE tc.constraint_type = 'PRIMARY KEY'
168
169
  AND tc.table_name = $1
169
- AND tc.table_schema = 'public'
170
+ AND tc.table_schema = ${this.schemaExpr}
170
171
  ORDER BY kcu.ordinal_position
171
172
  `;
172
173
  }
@@ -25,7 +25,8 @@ export declare class Migrator {
25
25
  * Set the schema generator for DDL operations
26
26
  */
27
27
  setSchemaGenerator(generator: SchemaGenerator): void;
28
- protected createIntrospector(): SchemaIntrospector | undefined;
28
+ /** `schema` reads one namespace instead of the connection's own; see {@link BaseSqlIntrospector.schema}. */
29
+ protected createIntrospector(schema?: string): SchemaIntrospector | undefined;
29
30
  protected createGenerator(): SchemaGenerator | undefined;
30
31
  /**
31
32
  * Get all discovered migrations from the migrations directory
@@ -77,6 +78,14 @@ export declare class Migrator {
77
78
  * Get all schema differences between entities and database
78
79
  */
79
80
  getDiffs(): Promise<SchemaDiff[]>;
81
+ /**
82
+ * One AST spanning every schema the entities claim, each table stamped with the schema it was read
83
+ * from. Read per schema rather than all at once, so a table comes back keyed exactly as the entity
84
+ * that wants it spells the key: `undefined` on both sides for the connection's default, a name on
85
+ * both sides otherwise. Ordinarily that is one schema and one pass, as before, and that pass keeps
86
+ * {@link schemaIntrospector} so a caller that replaced it still wins.
87
+ */
88
+ private introspectClaimedSchemas;
80
89
  findEntityForTable(tableName: string): Promise<Type<unknown> | undefined>;
81
90
  /**
82
91
  * Sync schema directly (for development only - not for production!)
@@ -2,7 +2,7 @@ import { mkdir, readdir, writeFile } from 'node:fs/promises';
2
2
  import { basename, extname, join } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { getEntities, getMeta } from '../entity/index.js';
5
- import { introspectSchema } from '../schema/index.js';
5
+ import { introspectSchema, SchemaAST } from '../schema/index.js';
6
6
  import { isKnownMigratorDialect, isSqlQuerier } from '../type/index.js';
7
7
  import { LoggerWrapper } from '../util/index.js';
8
8
  import { withQuerierForMigrations, withSqlQuerierForMigrations } from './acquireQuerierForMigrations.js';
@@ -71,19 +71,20 @@ export class Migrator {
71
71
  setSchemaGenerator(generator) {
72
72
  this.schemaGenerator = generator;
73
73
  }
74
- createIntrospector() {
74
+ /** `schema` reads one namespace instead of the connection's own; see {@link BaseSqlIntrospector.schema}. */
75
+ createIntrospector(schema) {
75
76
  const d = this.dialectName;
76
77
  if (!isKnownMigratorDialect(d)) {
77
78
  return undefined;
78
79
  }
79
80
  switch (d) {
80
81
  case 'postgres':
81
- return new PostgresSchemaIntrospector(this.pool);
82
+ return new PostgresSchemaIntrospector(this.pool, schema);
82
83
  case 'cockroachdb':
83
- return new CockroachSchemaIntrospector(this.pool);
84
+ return new CockroachSchemaIntrospector(this.pool, schema);
84
85
  case 'mysql':
85
86
  case 'mariadb':
86
- return new MysqlSchemaIntrospector(this.pool);
87
+ return new MysqlSchemaIntrospector(this.pool, schema);
87
88
  case 'sqlite':
88
89
  return new SqliteSchemaIntrospector(this.pool);
89
90
  case 'mongodb':
@@ -279,11 +280,11 @@ export class Migrator {
279
280
  if (!this.schemaGenerator || !this.schemaIntrospector) {
280
281
  throw new TypeError('Schema generator and introspector must be set');
281
282
  }
282
- const ast = await introspectSchema(this.schemaIntrospector);
283
+ const ast = await this.introspectClaimedSchemas();
283
284
  const diffs = [];
284
285
  for (const entity of this.entities) {
285
286
  const meta = getMeta(entity);
286
- const tableName = this.schemaGenerator.resolveTableName(entity, meta);
287
+ const tableName = this.schemaGenerator.resolveTableName(meta);
287
288
  const currentTable = ast.getTable(tableName);
288
289
  const diff = this.schemaGenerator.diffSchema(entity, currentTable);
289
290
  if (diff) {
@@ -292,6 +293,27 @@ export class Migrator {
292
293
  }
293
294
  return diffs;
294
295
  }
296
+ /**
297
+ * One AST spanning every schema the entities claim, each table stamped with the schema it was read
298
+ * from. Read per schema rather than all at once, so a table comes back keyed exactly as the entity
299
+ * that wants it spells the key: `undefined` on both sides for the connection's default, a name on
300
+ * both sides otherwise. Ordinarily that is one schema and one pass, as before, and that pass keeps
301
+ * {@link schemaIntrospector} so a caller that replaced it still wins.
302
+ */
303
+ async introspectClaimedSchemas() {
304
+ const claimed = new Set(this.entities.map((entity) => this.pool.dialect.resolveSchema(getMeta(entity))));
305
+ const merged = new SchemaAST();
306
+ for (const schema of claimed) {
307
+ const introspector = schema === undefined ? this.schemaIntrospector : this.createIntrospector(schema);
308
+ if (!introspector) {
309
+ continue;
310
+ }
311
+ for (const table of (await introspectSchema(introspector)).getTables()) {
312
+ merged.addTable(table);
313
+ }
314
+ }
315
+ return merged;
316
+ }
295
317
  async findEntityForTable(tableName) {
296
318
  await this.ensureSchemaGenerator();
297
319
  if (!this.schemaGenerator) {
@@ -299,7 +321,7 @@ export class Migrator {
299
321
  }
300
322
  for (const entity of this.entities) {
301
323
  const meta = getMeta(entity);
302
- const name = this.schemaGenerator.resolveTableName(entity, meta);
324
+ const name = this.schemaGenerator.resolveTableName(meta);
303
325
  if (name === tableName) {
304
326
  return entity;
305
327
  }
@@ -13,11 +13,11 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
13
13
  constructor(dialect: AbstractSqlDialect, defaultForeignKeyAction?: ForeignKeyAction);
14
14
  get namingStrategy(): NamingStrategy | undefined;
15
15
  get features(): DialectFeatures;
16
- resolveTableName<E>(entity: Type<E>, meta: EntityMeta<E>): string;
16
+ resolveTableName<E>(meta: EntityMeta<E>): string;
17
+ resolveTableAlias<E>(meta: EntityMeta<E>): string;
18
+ resolveSchema<E>(meta: EntityMeta<E>): string | undefined;
17
19
  resolveColumnName(key: string, field: FieldOptions): string;
18
- /**
19
- * Escape an identifier (table name, column name, etc.)
20
- */
20
+ /** Escape an identifier (table name, column name, etc.) */
21
21
  protected escapeId(identifier: string): string;
22
22
  /**
23
23
  * Primary key type for auto-increment integer IDs
@@ -38,6 +38,12 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
38
38
  * foreign key in, but it resolves targets lazily, so a forward reference is fine there.
39
39
  */
40
40
  generateCreateSchema(entities: readonly Type<unknown>[], options?: CreateSchemaOptions): string[];
41
+ /**
42
+ * One statement per distinct schema the tables being created live in, in first-seen order. Only
43
+ * the tables actually being created, so a narrowed `only` does not declare namespaces it is not
44
+ * about to fill. Empty on an engine without schemas, whose tables are never qualified.
45
+ */
46
+ private generateCreateSchemas;
41
47
  generateDropSchema(entities: readonly Type<unknown>[], options?: DropSchemaOptions): string[];
42
48
  /**
43
49
  * The tables of `entities` in dependency order, optionally narrowed to `only`. The AST always spans
@@ -51,7 +57,11 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
51
57
  generateCreateIndex(tableName: string, index: IndexSchema, options?: {
52
58
  ifNotExists?: boolean;
53
59
  }): string;
54
- generateDropIndex(tableName: string, indexName: string): string;
60
+ /**
61
+ * `schema` is the table's, because that is where its indexes live. MySQL takes it from the table
62
+ * operand instead, which is already qualified.
63
+ */
64
+ generateDropIndex(tableName: string, indexName: string, schema?: string): string;
55
65
  /**
56
66
  * Generate a column definition from a {@link ColumnSchema}, whose type is the engine's own spelling
57
67
  * and may already carry its precision (or even `PRIMARY KEY`, for a serial).
@@ -156,7 +166,7 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
156
166
  * other way and the table is created under one name and compared under another, which reports every
157
167
  * table of a project using a naming strategy as both missing and unexpected.
158
168
  */
159
- export declare function buildEntityAST(generator: Pick<SchemaGenerator, 'resolveTableName' | 'resolveColumnName'>, entities: readonly Type<unknown>[], defaultForeignKeyAction?: ForeignKeyAction): SchemaAST;
169
+ export declare function buildEntityAST(generator: Pick<SchemaGenerator, 'resolveTableAlias' | 'resolveSchema' | 'resolveColumnName'>, entities: readonly Type<unknown>[], defaultForeignKeyAction?: ForeignKeyAction): SchemaAST;
160
170
  /**
161
171
  * Synchronous factory for SQL schema generators only.
162
172
  * For MongoDB, use `createSchemaGeneratorAsync` from `./schemaGeneratorAsync.js` so the optional `mongodb` peer is not loaded at import time.