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.
- package/dist/dialect/vectorSqlDialect.d.ts +2 -0
- package/dist/dialect/vectorSqlDialect.js +4 -0
- package/dist/migrate/builder/expressions.d.ts +2 -0
- package/dist/migrate/builder/expressions.js +24 -0
- package/dist/migrate/cli.js +1 -1
- package/dist/migrate/codegen/entityCodeGenerator.js +2 -2
- package/dist/migrate/codegen/indexDecoratorSource.d.ts +3 -2
- package/dist/migrate/codegen/indexDecoratorSource.js +5 -23
- package/dist/migrate/ddl/mssqlTableDdl.d.ts +4 -4
- package/dist/migrate/ddl/mssqlTableDdl.js +20 -14
- package/dist/migrate/ddl/mysqlIndexDdl.d.ts +2 -2
- package/dist/migrate/ddl/mysqlIndexDdl.js +7 -6
- package/dist/migrate/ddl/pgIndexDdl.d.ts +2 -1
- package/dist/migrate/ddl/pgIndexDdl.js +8 -6
- package/dist/migrate/ddl/tableDdl.d.ts +3 -2
- package/dist/migrate/ddl/tableDdl.js +9 -7
- package/dist/migrate/drift/driftDetector.d.ts +4 -5
- package/dist/migrate/drift/driftDetector.js +21 -21
- package/dist/migrate/generator/definitionToNode.d.ts +1 -1
- package/dist/migrate/generator/definitionToNode.js +9 -20
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +1 -1
- package/dist/migrate/generator/mongoSchemaGenerator.js +11 -19
- package/dist/migrate/index.d.ts +2 -1
- package/dist/migrate/index.js +1 -0
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +5 -7
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +11 -18
- package/dist/migrate/introspection/baseSqlIntrospector.js +7 -18
- package/dist/migrate/introspection/mongoIntrospector.d.ts +1 -1
- package/dist/migrate/introspection/mongoIntrospector.js +3 -3
- package/dist/migrate/introspection/mssqlIntrospector.js +2 -1
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +15 -5
- package/dist/migrate/introspection/mysqlIntrospector.js +32 -4
- package/dist/migrate/introspection/postgresIntrospector.d.ts +29 -21
- package/dist/migrate/introspection/postgresIntrospector.js +63 -46
- package/dist/migrate/introspection/sqliteIntrospector.js +11 -9
- package/dist/migrate/migrator.d.ts +5 -0
- package/dist/migrate/migrator.js +33 -44
- package/dist/migrate/schemaChange.d.ts +18 -0
- package/dist/migrate/schemaChange.js +37 -0
- package/dist/migrate/schemaGenerator.d.ts +13 -14
- package/dist/migrate/schemaGenerator.js +83 -177
- package/dist/schema/indexDifferences.d.ts +22 -6
- package/dist/schema/indexDifferences.js +23 -8
- package/dist/schema/matchByKey.d.ts +10 -0
- package/dist/schema/matchByKey.js +18 -0
- package/dist/schema/schemaAST.d.ts +6 -2
- package/dist/schema/schemaAST.js +7 -3
- package/dist/schema/schemaASTBuilder.d.ts +2 -0
- package/dist/schema/schemaASTBuilder.js +15 -10
- package/dist/schema/schemaASTDiffer.d.ts +2 -3
- package/dist/schema/schemaASTDiffer.js +15 -36
- package/dist/schema/types.d.ts +14 -15
- package/dist/type/migration.d.ts +32 -50
- package/dist/util/ddlExpression.util.d.ts +5 -1
- package/dist/util/ddlExpression.util.js +6 -2
- 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.
|
|
112
|
-
...(diff.
|
|
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
|
-
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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`. */
|
package/dist/migrate/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/migrate/index.js
CHANGED
|
@@ -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<
|
|
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
|
|
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
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
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
|
-
|
|
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<
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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.
|
|
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
|
-
* `
|
|
83
|
-
*
|
|
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
|
|
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;
|