uql-orm 0.80.0 → 0.81.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 (56) hide show
  1. package/dist/dialect/vectorSqlDialect.d.ts +2 -0
  2. package/dist/dialect/vectorSqlDialect.js +4 -0
  3. package/dist/migrate/builder/expressions.d.ts +2 -0
  4. package/dist/migrate/builder/expressions.js +24 -0
  5. package/dist/migrate/cli.js +1 -1
  6. package/dist/migrate/codegen/entityCodeGenerator.js +2 -2
  7. package/dist/migrate/codegen/indexDecoratorSource.d.ts +3 -2
  8. package/dist/migrate/codegen/indexDecoratorSource.js +5 -23
  9. package/dist/migrate/ddl/mssqlTableDdl.d.ts +4 -4
  10. package/dist/migrate/ddl/mssqlTableDdl.js +20 -14
  11. package/dist/migrate/ddl/mysqlIndexDdl.d.ts +2 -2
  12. package/dist/migrate/ddl/mysqlIndexDdl.js +7 -6
  13. package/dist/migrate/ddl/pgIndexDdl.d.ts +2 -1
  14. package/dist/migrate/ddl/pgIndexDdl.js +8 -6
  15. package/dist/migrate/ddl/tableDdl.d.ts +3 -2
  16. package/dist/migrate/ddl/tableDdl.js +9 -7
  17. package/dist/migrate/drift/driftDetector.d.ts +4 -5
  18. package/dist/migrate/drift/driftDetector.js +21 -21
  19. package/dist/migrate/generator/definitionToNode.d.ts +1 -1
  20. package/dist/migrate/generator/definitionToNode.js +9 -20
  21. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +1 -1
  22. package/dist/migrate/generator/mongoSchemaGenerator.js +11 -19
  23. package/dist/migrate/index.d.ts +2 -1
  24. package/dist/migrate/index.js +1 -0
  25. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +5 -7
  26. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +11 -18
  27. package/dist/migrate/introspection/baseSqlIntrospector.js +7 -18
  28. package/dist/migrate/introspection/mongoIntrospector.d.ts +1 -1
  29. package/dist/migrate/introspection/mongoIntrospector.js +3 -3
  30. package/dist/migrate/introspection/mssqlIntrospector.js +2 -1
  31. package/dist/migrate/introspection/mysqlIntrospector.d.ts +15 -5
  32. package/dist/migrate/introspection/mysqlIntrospector.js +32 -4
  33. package/dist/migrate/introspection/postgresIntrospector.d.ts +29 -21
  34. package/dist/migrate/introspection/postgresIntrospector.js +63 -46
  35. package/dist/migrate/introspection/sqliteIntrospector.js +11 -9
  36. package/dist/migrate/migrator.d.ts +5 -0
  37. package/dist/migrate/migrator.js +33 -44
  38. package/dist/migrate/schemaChange.d.ts +18 -0
  39. package/dist/migrate/schemaChange.js +37 -0
  40. package/dist/migrate/schemaGenerator.d.ts +13 -14
  41. package/dist/migrate/schemaGenerator.js +83 -177
  42. package/dist/schema/indexDifferences.d.ts +22 -6
  43. package/dist/schema/indexDifferences.js +23 -8
  44. package/dist/schema/matchByKey.d.ts +10 -0
  45. package/dist/schema/matchByKey.js +18 -0
  46. package/dist/schema/schemaAST.d.ts +6 -2
  47. package/dist/schema/schemaAST.js +7 -3
  48. package/dist/schema/schemaASTBuilder.d.ts +2 -0
  49. package/dist/schema/schemaASTBuilder.js +15 -10
  50. package/dist/schema/schemaASTDiffer.d.ts +2 -3
  51. package/dist/schema/schemaASTDiffer.js +15 -36
  52. package/dist/schema/types.d.ts +14 -15
  53. package/dist/type/migration.d.ts +32 -50
  54. package/dist/util/ddlExpression.util.d.ts +5 -1
  55. package/dist/util/ddlExpression.util.js +6 -2
  56. package/package.json +1 -1
@@ -8,6 +8,7 @@ import { declaredIndexes, declaredIndexName, renderIndexColumn } from '../../uti
8
8
  import { fulltextConfig, fulltextWeights } from '../../util/dialect.util.js';
9
9
  import { assertIndexFeatures, assertIndexType } from '../ddl/indexDdl.js';
10
10
  import { assertIndexPredicate, refusedIndexPredicate } from '../indexPredicate.js';
11
+ import { sides } from '../schemaChange.js';
11
12
  import { renderIndexDefinition } from './definitionToNode.js';
12
13
  import { indexNodeToSchema } from './indexNodeToSchema.js';
13
14
  import { serializeMongoCommand } from './mongoCommand.js';
@@ -106,16 +107,11 @@ export class MongoSchemaGenerator extends MongoDialect {
106
107
  generateDropTable(tableName) {
107
108
  return serializeMongoCommand({ action: 'dropCollection', name: tableName });
108
109
  }
110
+ /** A collection's indexes: each dropped, then each created, an alter as both. */
109
111
  generateAlterTable(diff) {
110
112
  return [
111
- ...(diff.indexesToDrop ?? []).map((index) => this.dropIndexCommand(diff.tableName, index)),
112
- ...(diff.indexesToAdd ?? []).map((index) => this.generateCreateIndex(diff.tableName, index)),
113
- ];
114
- }
115
- generateAlterTableDown(diff) {
116
- return [
117
- ...(diff.indexesToAdd ?? []).map((index) => this.dropIndexCommand(diff.tableName, index)),
118
- ...(diff.indexesToDrop ?? []).map((index) => this.generateCreateIndex(diff.tableName, index)),
113
+ ...sides(diff.indexes, 'from').map((index) => this.dropIndexCommand(diff.tableName, index)),
114
+ ...sides(diff.indexes, 'to').map((index) => this.generateCreateIndex(diff.tableName, index)),
119
115
  ];
120
116
  }
121
117
  /** MongoDB has no triggers, and a write to an entity declaring one is refused, so there is none to reconcile. */
@@ -221,17 +217,13 @@ export class MongoSchemaGenerator extends MongoDialect {
221
217
  if (!currentTable) {
222
218
  return { tableName: collectionName, type: 'create' };
223
219
  }
224
- // By name: MongoDB lists a text index's fields alphabetically, so a shape would not match its own.
225
- const { toAdd, toDrop } = indexChanges(collectionName, this.indexesOf(meta, collectionName), currentTable.indexes, (index) => index.name);
226
- if (!toAdd.length && !toDrop.length) {
227
- return undefined;
228
- }
229
- return {
230
- tableName: collectionName,
231
- type: 'alter',
232
- indexesToAdd: toAdd.length ? toAdd : undefined,
233
- indexesToDrop: toDrop.length ? toDrop.map(indexNodeToSchema) : undefined,
234
- };
220
+ const { toAdd, toDrop, toAlter } = indexChanges(collectionName, this.indexesOf(meta, collectionName), currentTable.indexes, currentTable.indexFacets);
221
+ const indexes = [
222
+ ...toAdd.map((to) => ({ to })),
223
+ ...toDrop.map((from) => ({ from: indexNodeToSchema(from) })),
224
+ ...toAlter.map(({ from, to }) => ({ from: indexNodeToSchema(from), to })),
225
+ ];
226
+ return indexes.length ? { tableName: collectionName, type: 'alter', indexes } : undefined;
235
227
  }
236
228
  }
237
229
  /** The first value `partialFilterExpression` refuses, `null`, or a migration cannot carry as JSON, such as a `Date`. */
@@ -1,4 +1,4 @@
1
- export type { ColumnSchema, DialectName, ForeignKeySchema, IndexSchema, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, SchemaDiff, SchemaGenerator, SchemaIntrospector, SqlDialectName, SqlQuerier, SqlQueryDialect, SyncOptions, TableSchema, } from '../type/index.js';
1
+ export type { Change, ColumnSchema, DialectName, ForeignKeySchema, IndexSchema, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, PrimaryKeySchema, SchemaDiff, SchemaGenerator, SchemaIntrospector, SqlDialectName, SqlQuerier, SqlQueryDialect, SyncOptions, TableSchema, } from '../type/index.js';
2
2
  export { type Config, isSqlQuerier } from '../type/index.js';
3
3
  export { acquireQuerierForMigrations } from './acquireQuerierForMigrations.js';
4
4
  export { assertCliConfig } from './assertCliConfig.js';
@@ -10,6 +10,7 @@ export * from './drift/index.js';
10
10
  export * from './introspection/index.js';
11
11
  export { migrationBuilderFor } from './migrationTarget.js';
12
12
  export { type BuilderMigrationDefinition, defineBuilderMigration, defineMigration, Migrator } from './migrator.js';
13
+ export { reverseDiff } from './schemaChange.js';
13
14
  export { SqlSchemaGenerator } from './schemaGenerator.js';
14
15
  export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
15
16
  export { JsonMigrationStorage } from './storage/jsonStorage.js';
@@ -16,6 +16,7 @@ export * from './introspection/index.js';
16
16
  export { migrationBuilderFor } from './migrationTarget.js';
17
17
  export { defineBuilderMigration, defineMigration, Migrator } from './migrator.js';
18
18
  // Schema generators
19
+ export { reverseDiff } from './schemaChange.js';
19
20
  export { SqlSchemaGenerator } from './schemaGenerator.js';
20
21
  // Storage implementations
21
22
  export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
@@ -1,5 +1,5 @@
1
1
  import { type ForeignKeyAction } from '../../schema/types.js';
2
- import type { ColumnSchema, InstalledTriggers, ForeignKeySchema, IndexSchema, QuerierPool, RawRow, SchemaIntrospector, SqlQuerier, TableSchema } from '../../type/index.js';
2
+ import type { ColumnSchema, InstalledTriggers, ForeignKeySchema, IndexSchema, PrimaryKeySchema, QuerierPool, RawRow, SchemaIntrospector, SqlQuerier, TableSchema } from '../../type/index.js';
3
3
  import { BaseSqlIntrospector } from './baseSqlIntrospector.js';
4
4
  /**
5
5
  * Reads the rows of one statement while introspecting a table.
@@ -43,7 +43,7 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
43
43
  * it: the whole schema in one read, since a table whose entity stopped declaring one still has one to
44
44
  * drop. Ownership is matched here rather than with `LIKE`, whose `_` wildcard would take in a hand-written one.
45
45
  */
46
- ownedTriggers(): Promise<Map<string, InstalledTriggers>>;
46
+ ownedTriggers(table: string): Promise<InstalledTriggers>;
47
47
  tableExists(tableName: string): Promise<boolean>;
48
48
  /**
49
49
  * Introspection reads, so `withQuerier` rather than `transaction`: the pool owns the release either
@@ -54,10 +54,7 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
54
54
  protected getColumns(read: TableRowReader, tableName: string): Promise<ColumnSchema[]>;
55
55
  protected getIndexes(read: TableRowReader, tableName: string): Promise<IndexSchema[]>;
56
56
  protected getForeignKeys(read: TableRowReader, tableName: string): Promise<ForeignKeySchema[]>;
57
- protected getPrimaryKey(read: TableRowReader, tableName: string): Promise<{
58
- columns?: string[];
59
- name?: string;
60
- }>;
57
+ protected getPrimaryKey(read: TableRowReader, tableName: string): Promise<PrimaryKeySchema | undefined>;
61
58
  protected tableExistsParams(tableName: string): unknown[];
62
59
  protected getColumnsParams(tableName: string): unknown[];
63
60
  protected getIndexesParams(tableName: string): unknown[];
@@ -90,6 +87,7 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
90
87
  * `definition`, and where the body lives apart, the `requires` recreated first: what is installed, not
91
88
  * what uql wrote, which is exactly what a rollback puts back.
92
89
  */
90
+ /** The triggers on the table its one parameter names: each one's `name`, `definition`, and what it `requires`. */
93
91
  protected abstract triggersQuery(): string;
94
92
  /**
95
93
  * Extract table name from a row returned by getTableNamesQuery.
@@ -113,7 +111,7 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
113
111
  protected mapPrimaryKeyResult(results: RawRow[]): string[] | undefined;
114
112
  /**
115
113
  * What the engine calls the key's constraint, where the query reported one. Only a `DROP` needs it,
116
- * and only the reported name will do - see {@link TableSchema.primaryKeyName}.
114
+ * and only the reported name will do - see {@link PrimaryKeySchema.name}.
117
115
  */
118
116
  protected mapPrimaryKeyName(results: RawRow[]): string | undefined;
119
117
  /** Parse default value string to appropriate type. */
@@ -38,8 +38,7 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
38
38
  return {
39
39
  name: tableName,
40
40
  columns,
41
- primaryKey: primaryKey.columns,
42
- primaryKeyName: primaryKey.name,
41
+ primaryKey,
43
42
  indexes,
44
43
  foreignKeys,
45
44
  };
@@ -56,20 +55,13 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
56
55
  * it: the whole schema in one read, since a table whose entity stopped declaring one still has one to
57
56
  * drop. Ownership is matched here rather than with `LIKE`, whose `_` wildcard would take in a hand-written one.
58
57
  */
59
- async ownedTriggers() {
60
- return this.withSqlQuerier(async (querier) => {
61
- const byTable = new Map();
62
- for (const row of await querier.all(this.triggersQuery())) {
63
- const name = String(row['name']);
64
- if (!isOwnedName(name)) {
65
- continue;
66
- }
67
- const table = String(row['table']);
68
- const statements = [row['requires'], row['definition']].filter(Boolean).map((sql) => String(sql));
69
- byTable.set(table, (byTable.get(table) ?? new Map()).set(name, statements));
70
- }
71
- return byTable;
72
- });
58
+ async ownedTriggers(table) {
59
+ const rows = await this.withSqlQuerier((querier) => querier.all(this.triggersQuery(), [table]));
60
+ return new Map(rows.flatMap((row) => {
61
+ const name = String(row['name']);
62
+ const statements = [row['requires'], row['definition']].filter(Boolean).map((sql) => String(sql));
63
+ return isOwnedName(name) ? [[name, statements]] : [];
64
+ }));
73
65
  }
74
66
  async tableExists(tableName) {
75
67
  return this.withSqlQuerier((querier) => this.tableExistsInternal(createTableRowReader(querier), tableName));
@@ -104,7 +96,8 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
104
96
  }
105
97
  async getPrimaryKey(read, tableName) {
106
98
  const results = await read(this.getPrimaryKeyQuery(tableName), this.getPrimaryKeyParams(tableName));
107
- return { columns: this.mapPrimaryKeyResult(results), name: this.mapPrimaryKeyName(results) };
99
+ const columns = this.mapPrimaryKeyResult(results);
100
+ return columns && { columns, name: this.mapPrimaryKeyName(results) };
108
101
  }
109
102
  tableExistsParams(tableName) {
110
103
  return [tableName];
@@ -166,7 +159,7 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
166
159
  }
167
160
  /**
168
161
  * What the engine calls the key's constraint, where the query reported one. Only a `DROP` needs it,
169
- * and only the reported name will do - see {@link TableSchema.primaryKeyName}.
162
+ * and only the reported name will do - see {@link PrimaryKeySchema.name}.
170
163
  */
171
164
  mapPrimaryKeyName(results) {
172
165
  const name = results[0]?.['constraint_name'];
@@ -1,5 +1,5 @@
1
1
  import { canonicalColumnType } from '../../schema/canonicalType.js';
2
- import { createTableNode, SchemaAST } from '../../schema/schemaAST.js';
2
+ import { createTableNode, keyOfColumns, SchemaAST } from '../../schema/schemaAST.js';
3
3
  import { escapeSqlId } from '../../util/index.js';
4
4
  import { derivedForeignKeyName } from '../../util/sql.util.js';
5
5
  /**
@@ -56,7 +56,7 @@ export class BaseSqlIntrospector {
56
56
  return ast;
57
57
  }
58
58
  buildTable(schema) {
59
- const table = createTableNode(schema.name, this.schema);
59
+ const table = createTableNode(schema.name, this.schema, this.indexFacets);
60
60
  const { columns } = table;
61
61
  for (const col of schema.columns) {
62
62
  // Spread, not field by field: a `ColumnSchema` is a `ColumnNode` minus the graph links, so
@@ -71,12 +71,10 @@ export class BaseSqlIntrospector {
71
71
  };
72
72
  columns.set(col.name, column);
73
73
  }
74
- // From the ordered list the query returned, not from the per-column flags: `(a, b)` is a
75
- // different key from `(b, a)`, and a flag says only that a column is *in* the key. Falls back
76
- // to the flags for an introspector that reports no key of its own.
77
- const keyColumns = schema.primaryKey ?? schema.columns.filter((col) => col.isPrimaryKey).map((col) => col.name);
78
- table.primaryKey.push(...keyColumns.flatMap((name) => columns.get(name) ?? []));
79
- table.primaryKeyName = schema.primaryKeyName;
74
+ // From the ordered key the query returned, not from the per-column flags: `(a, b)` is a different
75
+ // key from `(b, a)`, and a flag says only that a column is *in* the key. Falls back to the flags
76
+ // for an introspector that reports no key of its own.
77
+ table.primaryKey = schema.primaryKey ?? keyOfColumns(schema.columns);
80
78
  return table;
81
79
  }
82
80
  buildRelationships(ast, tableNodes, schema, fromTable) {
@@ -105,16 +103,7 @@ export class BaseSqlIntrospector {
105
103
  // does not have, and the index if that leaves none, is what the entity side does too.
106
104
  const entries = idx.entries.filter((entry) => entry.expression || table.columns.has(entry.column));
107
105
  if (entries.length > 0) {
108
- const index = {
109
- name: idx.name,
110
- table,
111
- entries,
112
- unique: idx.unique,
113
- type: idx.type,
114
- where: idx.where,
115
- include: idx.include,
116
- };
117
- ast.addIndex(index);
106
+ ast.addIndex({ ...idx, table, entries });
118
107
  }
119
108
  }
120
109
  }
@@ -11,7 +11,7 @@ export declare class MongoSchemaIntrospector implements SchemaIntrospector {
11
11
  readonly indexFacets: ReadonlySet<IndexFacet>;
12
12
  constructor(pool: QuerierPool);
13
13
  /** MongoDB has no triggers, so none is ever installed. */
14
- ownedTriggers(): Promise<Map<string, InstalledTriggers>>;
14
+ ownedTriggers(): Promise<InstalledTriggers>;
15
15
  introspect(tables?: readonly string[]): Promise<SchemaAST>;
16
16
  getTableSchema(tableName: string): Promise<TableSchema | undefined>;
17
17
  /** Collections only, the way a SQL engine lists its base tables: no view, nor the `system.views` behind one. */
@@ -24,7 +24,7 @@ export class MongoSchemaIntrospector {
24
24
  for (const name of tableNames) {
25
25
  const schema = await this.getTableSchema(name);
26
26
  if (schema) {
27
- ast.addTable(buildTable(schema));
27
+ ast.addTable(buildTable(schema, this.indexFacets));
28
28
  }
29
29
  }
30
30
  return ast;
@@ -107,8 +107,8 @@ async function hasCollection(db, name) {
107
107
  return collections.length > 0;
108
108
  }
109
109
  /** Mongo has no columns to read, so a table's are the fields its indexes name, one node per field. */
110
- function buildTable({ name, indexes = [] }) {
111
- const table = createTableNode(name);
110
+ function buildTable({ name, indexes = [] }, indexFacets) {
111
+ const table = createTableNode(name, undefined, indexFacets);
112
112
  for (const index of indexes) {
113
113
  for (const { column } of index.entries) {
114
114
  if (!table.columns.has(column)) {
@@ -11,10 +11,11 @@ export class MsSqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
11
11
  defaultSchemaExpr = 'SCHEMA_NAME()';
12
12
  triggersQuery() {
13
13
  return /*sql*/ `
14
- SELECT OBJECT_NAME(t.parent_id) AS [table], t.name AS name, m.definition AS definition
14
+ SELECT t.name AS name, m.definition AS definition
15
15
  FROM sys.triggers t
16
16
  JOIN sys.sql_modules m ON m.object_id = t.object_id
17
17
  WHERE t.parent_id <> 0 AND OBJECT_SCHEMA_NAME(t.parent_id) = ${this.schemaExpr}
18
+ AND OBJECT_NAME(t.parent_id) = ${this.dialect.placeholder(1)}
18
19
  `;
19
20
  }
20
21
  getTableNamesQuery() {
@@ -1,3 +1,4 @@
1
+ import type { IndexFacet } from '../../schema/indexDifferences.js';
1
2
  import type { ColumnSchema, ForeignKeySchema, IndexSchema } from '../../type/index.js';
2
3
  import { AbstractSqlSchemaIntrospector, type JoinedForeignKeyRow, type TableRowReader } from './abstractSqlSchemaIntrospector.js';
3
4
  /**
@@ -17,11 +18,7 @@ export declare class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospect
17
18
  protected getForeignKeysQuery(_tableName: string): string;
18
19
  protected getPrimaryKeyQuery(_tableName: string): string;
19
20
  protected mapColumnsResult(_read: TableRowReader, _tableName: string, results: MysqlColumnRow[]): Promise<ColumnSchema[]>;
20
- protected mapIndexesResult(_read: TableRowReader, _tableName: string, results: {
21
- index_name: string;
22
- columns: string;
23
- is_unique: number;
24
- }[]): Promise<IndexSchema[]>;
21
+ protected mapIndexesResult(_read: TableRowReader, _tableName: string, results: MysqlIndexRow[]): Promise<IndexSchema[]>;
25
22
  protected mapForeignKeysResult(_read: TableRowReader, _tableName: string, results: JoinedForeignKeyRow[]): Promise<ForeignKeySchema[]>;
26
23
  /**
27
24
  * MariaDB prints a string default as the literal it is (`'it''s'`). MySQL prints one bare, save an
@@ -52,6 +49,19 @@ type MysqlColumnRow = {
52
49
  * JSON, got LONGTEXT", flagged as data loss) on a table uql created itself.
53
50
  */
54
51
  export declare class MariadbSchemaIntrospector extends MysqlSchemaIntrospector {
52
+ /** Whether an index is MariaDB's vector index, and the distance it was built for. */
53
+ readonly indexFacets: ReadonlySet<IndexFacet>;
54
+ /**
55
+ * A vector index's distance is kept only in the table's own definition, ``VECTOR KEY `ix` (`vec`)
56
+ * `DISTANCE`='cosine'``, and left out there for MariaDB's default, euclidean.
57
+ */
58
+ protected mapIndexesResult(read: TableRowReader, tableName: string, results: MysqlIndexRow[]): Promise<IndexSchema[]>;
55
59
  protected mapColumnsResult(read: TableRowReader, tableName: string, results: MysqlColumnRow[]): Promise<ColumnSchema[]>;
56
60
  }
61
+ type MysqlIndexRow = {
62
+ index_name: string;
63
+ columns: string;
64
+ is_unique: number;
65
+ method: string;
66
+ };
57
67
  export {};
@@ -9,11 +9,11 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
9
9
  defaultSchemaExpr = 'DATABASE()';
10
10
  triggersQuery() {
11
11
  return /*sql*/ `
12
- SELECT EVENT_OBJECT_TABLE AS \`table\`, TRIGGER_NAME AS name,
12
+ SELECT TRIGGER_NAME AS name,
13
13
  CONCAT('CREATE TRIGGER \`', TRIGGER_SCHEMA, '\`.\`', TRIGGER_NAME, '\` ', ACTION_TIMING, ' ', EVENT_MANIPULATION,
14
14
  ' ON \`', EVENT_OBJECT_SCHEMA, '\`.\`', EVENT_OBJECT_TABLE, '\` FOR EACH ROW ', ACTION_STATEMENT) AS definition
15
15
  FROM information_schema.TRIGGERS
16
- WHERE TRIGGER_SCHEMA = ${this.schemaExpr}
16
+ WHERE TRIGGER_SCHEMA = ${this.schemaExpr} AND EVENT_OBJECT_TABLE = ${this.dialect.placeholder(1)}
17
17
  `;
18
18
  }
19
19
  getTableNamesQuery() {
@@ -62,7 +62,8 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
62
62
  SELECT
63
63
  INDEX_NAME as index_name,
64
64
  GROUP_CONCAT(COALESCE(COLUMN_NAME, '') ORDER BY SEQ_IN_INDEX) as columns,
65
- NOT NON_UNIQUE as is_unique
65
+ NOT NON_UNIQUE as is_unique,
66
+ MAX(INDEX_TYPE) as method
66
67
  FROM information_schema.STATISTICS
67
68
  WHERE TABLE_SCHEMA = ${this.schemaExpr}
68
69
  AND TABLE_NAME = ?
@@ -110,7 +111,8 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
110
111
  isPrimaryKey: row.column_key === 'PRI',
111
112
  isAutoIncrement: row.extra.toLowerCase().includes('auto_increment'),
112
113
  isUnique: row.column_key === 'UNI',
113
- length: this.toNumber(row.character_maximum_length),
114
+ // A `VECTOR`'s is its bytes, four a dimension, which `column_type` already states as dimensions.
115
+ length: /^vector/i.test(row.column_type) ? undefined : this.toNumber(row.character_maximum_length),
114
116
  precision: this.toNumber(row.numeric_precision),
115
117
  scale: this.toNumber(row.numeric_scale),
116
118
  comment: row.column_comment || undefined,
@@ -120,6 +122,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
120
122
  async mapIndexesResult(_read, _tableName, results) {
121
123
  return results.map((row) => ({
122
124
  name: row.index_name,
125
+ ...(row.method === 'VECTOR' && { type: 'vector' }),
123
126
  // A functional or multi-valued key part has no `COLUMN_NAME` - the `COALESCE` above keeps its
124
127
  // place in the list, and it is reported as the expression it is, which is what stops diffing
125
128
  // from comparing an entry list the server cannot state against the entity's own.
@@ -164,6 +167,31 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
164
167
  * JSON, got LONGTEXT", flagged as data loss) on a table uql created itself.
165
168
  */
166
169
  export class MariadbSchemaIntrospector extends MysqlSchemaIntrospector {
170
+ /** Whether an index is MariaDB's vector index, and the distance it was built for. */
171
+ indexFacets = new Set(['vector', 'distance']);
172
+ /**
173
+ * A vector index's distance is kept only in the table's own definition, ``VECTOR KEY `ix` (`vec`)
174
+ * `DISTANCE`='cosine'``, and left out there for MariaDB's default, euclidean.
175
+ */
176
+ async mapIndexesResult(read, tableName, results) {
177
+ const indexes = await super.mapIndexesResult(read, tableName, results);
178
+ if (!indexes.some((index) => index.type === 'vector')) {
179
+ return indexes;
180
+ }
181
+ const qualified = [this.schema, tableName]
182
+ .filter((name) => name !== undefined)
183
+ .map((name) => this.dialect.escapeId(name));
184
+ const [row] = await read(/*sql*/ `SHOW CREATE TABLE ${qualified.join('.')}`);
185
+ const lines = row['Create Table'].split('\n');
186
+ return indexes.map((index) => {
187
+ if (index.type !== 'vector') {
188
+ return index;
189
+ }
190
+ const line = lines.find((it) => it.includes(`VECTOR KEY \`${index.name}\``));
191
+ const metric = line?.match(/`DISTANCE`='(\w+)'/)?.[1] ?? 'euclidean';
192
+ return { ...index, distance: this.dialect.indexedDistance(metric) };
193
+ });
194
+ }
167
195
  async mapColumnsResult(read, tableName, results) {
168
196
  const columns = await super.mapColumnsResult(read, tableName, results);
169
197
  const checks = await read(
@@ -22,7 +22,11 @@ export declare class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrosp
22
22
  * database scans meet that table every time something else is migrating.
23
23
  *
24
24
  * `attgenerated` rather than `is_generated`, which cannot part a stored generated column from the
25
- * virtual one Postgres 18 added and uql never declares. CockroachDB states it too.
25
+ * virtual one Postgres 18 added and uql never declares. CockroachDB states it too. `format_type` for an
26
+ * extension type's modifier, which `information_schema` drops: a `vector(256)` read back as `vector`.
27
+ * CockroachDB names that type `vector` where Postgres says `USER-DEFINED`.
28
+ *
29
+ * A column is unique by a unique index over it alone, a constraint's or its own, as every engine reads it.
26
30
  */
27
31
  protected getColumnsQuery(_tableName: string): string;
28
32
  /**
@@ -32,24 +36,27 @@ export declare class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrosp
32
36
  * while `pg_get_indexdef` reprints an identifier *quoted*, so a camelCase column came back as
33
37
  * `"tenantId"` and matched no column of the table. Prisma and drizzle-kit both split it this way.
34
38
  *
35
- * Indexes backing a constraint are left out, primary keys among them: `@Field({ unique })` emits a
36
- * `UNIQUE` constraint and no index, so reporting the index Postgres builds underneath it told every
37
- * project it had an index its entities never asked for.
39
+ * The key's index and an `EXCLUDE`'s are left out. A `UNIQUE` constraint's index stays, as SQL Server
40
+ * and the MySQL family report theirs: the diff reads one over a single column as that column's
41
+ * uniqueness, and one over several as the unique `@Index` it is.
38
42
  */
39
43
  protected getIndexesQuery(_tableName: string): string;
40
- /**
41
- * Constraint kinds whose backing index is the constraint itself rather than an index anyone asked
42
- * for. Postgres builds one for `PRIMARY KEY`, `UNIQUE` and `EXCLUDE`, and only for those: a plain
43
- * `CREATE UNIQUE INDEX` has no `pg_constraint` row at all, so it survives.
44
- *
45
- * Nothing to do with {@link indexFacets}, which says which *attributes* of an index diffing may
46
- * compare. This one decides which indexes are reported at all.
47
- */
48
- protected readonly constraintIndexTypes: readonly string[];
44
+ /** Whether an entry sorts nulls first, which Postgres states on every entry. */
45
+ protected readonly nullsFirstSql: string;
46
+ /** An index's access method, which is the type it declares. */
47
+ protected readonly indexMethodSql: string;
48
+ /** An entry's operator class, where it is not the default for its type. */
49
+ protected readonly opsClassSql: string;
49
50
  /** From `pg_constraint`, whose key arrays keep each column paired with the one it references. */
50
51
  protected getForeignKeysQuery(_tableName: string): string;
51
52
  protected getPrimaryKeyQuery(_tableName: string): string;
52
53
  protected mapColumnsResult(_read: TableRowReader, _tableName: string, results: PostgresColumnRow[]): Promise<ColumnSchema[]>;
54
+ /**
55
+ * A vector index keeps its distance as its vector column's operator class, `{type}_{metric}_ops`, which
56
+ * is how `PgIndexDdl` writes the entity's `distance`; read back as that `distance`, so the two compare.
57
+ * No class named is the engine's default, L2.
58
+ */
59
+ private withVectorDistance;
53
60
  protected mapIndexesResult(_read: TableRowReader, _tableName: string, results: PostgresIndexRow[]): Promise<IndexSchema[]>;
54
61
  protected mapForeignKeysResult(_read: TableRowReader, _tableName: string, results: PostgresForeignKeyRow[]): Promise<ForeignKeySchema[]>;
55
62
  protected normalizeType(dataType: string, udtName: string): string;
@@ -73,18 +80,18 @@ declare const FOREIGN_KEY_ACTION_CODES: {
73
80
  * CockroachDB answers the same catalogue queries and differs only in what it can express: v26.2.5
74
81
  * still rejects `NULLS FIRST/LAST` and operator classes as "unimplemented", and it sorts nulls first
75
82
  * on an ASC column where Postgres sorts them last. Reading a nulls order back would therefore report
76
- * every ascending index as drifted, against an entity that could not have asked for one. Its access
77
- * method, `prefix`, needs nothing: a method that is not a known index type is reported as no type.
83
+ * every ascending index as drifted, against an entity that could not have asked for one.
78
84
  */
79
85
  export declare class CockroachSchemaIntrospector extends PostgresSchemaIntrospector {
80
86
  readonly indexFacets: ReadonlySet<IndexFacet>;
87
+ /** None: it rejects a stated nulls order, so reading one back gives an index it would refuse to rebuild. */
88
+ protected readonly nullsFirstSql = "NULL::BOOL";
81
89
  /**
82
- * `'u'` is missing on purpose. CockroachDB registers a `UNIQUE` constraint for a plain `CREATE
83
- * UNIQUE INDEX` too, naming it after the index, so filtering on it would hide every unique index a
84
- * user asked for and report it missing forever. It leaves no way to tell the two apart, so the
85
- * index a `@Field({ unique })` builds underneath itself stays visible there.
90
+ * Every index reports the access method `prefix` and no operator class, so a vector index is read off
91
+ * its definition, `USING cspann (vec vector_cosine_ops)`: its type, and its last key's class.
86
92
  */
87
- protected readonly constraintIndexTypes: readonly string[];
93
+ protected readonly indexMethodSql = "CASE WHEN pg_get_indexdef(ix.indexrelid) LIKE '% USING cspann %' THEN 'vector' ELSE am.amname END";
94
+ protected readonly opsClassSql = "CASE WHEN k.n = ix.indnkeyatts THEN substring(pg_get_indexdef(ix.indexrelid) from '(\\w+_ops)\\)') END";
88
95
  }
89
96
  type PostgresForeignKeyRow = {
90
97
  constraint_name: string;
@@ -104,13 +111,14 @@ type PostgresIndexRow = {
104
111
  is_expression: boolean;
105
112
  entry: string;
106
113
  descending: boolean;
107
- nulls_first: boolean;
114
+ nulls_first: boolean | null;
108
115
  ops_class: string | null;
109
116
  };
110
117
  type PostgresColumnRow = {
111
118
  column_name: string;
112
119
  data_type: string;
113
120
  udt_name: string;
121
+ formatted_type: string | null;
114
122
  is_nullable: string;
115
123
  column_default: string | null;
116
124
  is_primary_key: boolean;