feat: add metadata catalog cleanup commands

This commit is contained in:
Codex
2026-08-28 00:37:43 +02:00
parent 79c4c925b5
commit 58ee9cffe4
19 changed files with 942 additions and 66 deletions
+103
View File
@@ -16,6 +16,8 @@ import {
CatalogConnectorError,
CatalogUnavailableError,
type CatalogColumn,
type CatalogDatabaseMetadataDeleteTarget,
type CatalogMetadataDeleteCounts,
type CatalogRelationship,
type CatalogSchemaDiff,
type CatalogSyncCounts,
@@ -24,6 +26,7 @@ import {
type CatalogSyncRunUpdate,
type CatalogSyncScope,
type CatalogTable,
type CatalogTableMetadataDeleteTarget,
type CatalogRepository,
type DatabaseBinding,
type DatabaseConfigurationInput,
@@ -559,6 +562,104 @@ export class KyselyCatalogRepository implements CatalogRepository {
}));
}
async deleteDatabaseMetadata(
databaseIds: readonly string[],
target: CatalogDatabaseMetadataDeleteTarget,
): Promise<CatalogMetadataDeleteCounts | undefined> {
const selectedDatabaseIds = [...new Set(databaseIds)];
if (selectedDatabaseIds.length === 0) return undefined;
return await this.db.transaction().execute(async (trx) => {
const databases = await trx.selectFrom("workspaceDatabases").select("id")
.where("id", "in", selectedDatabaseIds).orderBy("id").forUpdate().execute();
if (databases.length !== selectedDatabaseIds.length) return undefined;
if (target === "relationships") {
const count = await trx.selectFrom("catalogRelationships")
.select(sql<number>`count(*)::int`.as("count"))
.where("databaseId", "in", selectedDatabaseIds).executeTakeFirst();
await trx.deleteFrom("catalogRelationships")
.where("databaseId", "in", selectedDatabaseIds).execute();
await trx.updateTable("workspaceDatabases").set({
schemaSyncedVersion: null,
schemaSyncedAt: null,
}).where("id", "in", selectedDatabaseIds).execute();
return { tables: 0, columns: 0, relationships: Number(count?.count ?? 0) };
}
const tableCount = await trx.selectFrom("catalogTables")
.select(sql<number>`count(*)::int`.as("count"))
.where("databaseId", "in", selectedDatabaseIds).executeTakeFirst();
const columnCount = await trx.selectFrom("catalogColumns")
.innerJoin("catalogTables", "catalogTables.id", "catalogColumns.tableId")
.select(sql<number>`count(*)::int`.as("count"))
.where("catalogTables.databaseId", "in", selectedDatabaseIds).executeTakeFirst();
const relationshipCount = await trx.selectFrom("catalogRelationships")
.select(sql<number>`count(*)::int`.as("count"))
.where("databaseId", "in", selectedDatabaseIds).executeTakeFirst();
await trx.deleteFrom("catalogTables")
.where("databaseId", "in", selectedDatabaseIds).execute();
await trx.updateTable("workspaceDatabases").set({
schemaSyncedVersion: null,
schemaSyncedAt: null,
}).where("id", "in", selectedDatabaseIds).execute();
return {
tables: Number(tableCount?.count ?? 0),
columns: Number(columnCount?.count ?? 0),
relationships: Number(relationshipCount?.count ?? 0),
};
});
}
async deleteTableMetadata(
databaseId: string,
tableIds: readonly string[],
target: CatalogTableMetadataDeleteTarget,
): Promise<CatalogMetadataDeleteCounts | undefined> {
const selectedTableIds = [...new Set(tableIds)];
if (selectedTableIds.length === 0) return undefined;
return await this.db.transaction().execute(async (trx) => {
const database = await trx.selectFrom("workspaceDatabases").select("id")
.where("id", "=", databaseId).forUpdate().executeTakeFirst();
if (!database) return undefined;
const tables = await trx.selectFrom("catalogTables").select("id")
.where("databaseId", "=", databaseId)
.where("id", "in", selectedTableIds).execute();
if (tables.length !== selectedTableIds.length) return undefined;
if (target === "columns") {
const count = await trx.selectFrom("catalogColumns")
.select(sql<number>`count(*)::int`.as("count"))
.where("tableId", "in", selectedTableIds).executeTakeFirst();
await trx.deleteFrom("catalogColumns")
.where("tableId", "in", selectedTableIds).execute();
await trx.updateTable("workspaceDatabases").set({
schemaSyncedVersion: null,
schemaSyncedAt: null,
}).where("id", "=", databaseId).execute();
return { tables: 0, columns: Number(count?.count ?? 0), relationships: 0 };
}
const count = await trx.selectFrom("catalogRelationships")
.select(sql<number>`count(*)::int`.as("count"))
.where("databaseId", "=", databaseId)
.where((eb) => eb.or([
eb("sourceTableId", "in", selectedTableIds),
eb("targetTableId", "in", selectedTableIds),
])).executeTakeFirst();
await trx.deleteFrom("catalogRelationships")
.where("databaseId", "=", databaseId)
.where((eb) => eb.or([
eb("sourceTableId", "in", selectedTableIds),
eb("targetTableId", "in", selectedTableIds),
])).execute();
await trx.updateTable("workspaceDatabases").set({
schemaSyncedVersion: null,
schemaSyncedAt: null,
}).where("id", "=", databaseId).execute();
return { tables: 0, columns: 0, relationships: Number(count?.count ?? 0) };
});
}
async planSchemaSync(
databaseId: string,
scope: CatalogSyncScope,
@@ -1002,6 +1103,8 @@ export class UnavailableCatalogRepository implements CatalogRepository {
async getColumn(): Promise<CatalogColumn | undefined> { return this.fail(); }
async updateColumnMetadata(): Promise<CatalogColumn | undefined> { return this.fail(); }
async listRelationships(): Promise<CatalogRelationship[]> { return this.fail(); }
async deleteDatabaseMetadata(): Promise<CatalogMetadataDeleteCounts | undefined> { return this.fail(); }
async deleteTableMetadata(): Promise<CatalogMetadataDeleteCounts | undefined> { return this.fail(); }
async planSchemaSync(): Promise<CatalogSchemaDiff> { return this.fail(); }
async applySchemaSync(): Promise<CatalogSyncCounts | undefined> { return this.fail(); }
async createSyncRun(): Promise<CatalogSyncRun> { return this.fail(); }