added typeaheead and introspetion
This commit is contained in:
@@ -212,6 +212,57 @@ final class DatabaseModel: ObservableObject {
|
||||
return selectedDatabase.isEmpty ? (selected?.database ?? "") : selectedDatabase
|
||||
}
|
||||
|
||||
// Phase 62: compare successive successful schema introspections.
|
||||
struct SchemaChange: Identifiable {
|
||||
let id = UUID()
|
||||
let category: String
|
||||
let object: String
|
||||
let detail: String
|
||||
}
|
||||
struct SchemaChangeReport {
|
||||
let database: String
|
||||
let checkedAt: Date
|
||||
let changes: [SchemaChange]
|
||||
}
|
||||
@Published private(set) var schemaChangeReports: [String: SchemaChangeReport] = [:]
|
||||
|
||||
func schemaChangeReport(for database: String) -> SchemaChangeReport? {
|
||||
guard let profile = selected else { return nil }
|
||||
return schemaChangeReports[introspectionKey(database, profile.id)]
|
||||
}
|
||||
|
||||
private func compareSchemas(previous: SchemaIntrospection, current: SchemaIntrospection) -> [SchemaChange] {
|
||||
var changes: [SchemaChange] = []
|
||||
func compareObjects(_ kind: String, _ old: [String], _ new: [String]) {
|
||||
let oldSet = Set(old), newSet = Set(new)
|
||||
for name in newSet.subtracting(oldSet).sorted() {
|
||||
changes.append(SchemaChange(category: "Added \(kind)", object: name, detail: "New \(kind.lowercased())"))
|
||||
}
|
||||
for name in oldSet.subtracting(newSet).sorted() {
|
||||
changes.append(SchemaChange(category: "Removed \(kind)", object: name, detail: "No longer exists"))
|
||||
}
|
||||
}
|
||||
compareObjects("Table", previous.tables, current.tables)
|
||||
compareObjects("View", previous.views, current.views)
|
||||
let allObjects = Set(previous.columnsByTable.keys).union(current.columnsByTable.keys)
|
||||
for object in allObjects.sorted() {
|
||||
let oldColumns = Dictionary(uniqueKeysWithValues: (previous.columnsByTable[object] ?? []).map { ($0.name, $0) })
|
||||
let newColumns = Dictionary(uniqueKeysWithValues: (current.columnsByTable[object] ?? []).map { ($0.name, $0) })
|
||||
for name in Set(newColumns.keys).subtracting(oldColumns.keys).sorted() {
|
||||
changes.append(SchemaChange(category: "Added Column", object: "\(object).\(name)", detail: newColumns[name]?.dataType ?? ""))
|
||||
}
|
||||
for name in Set(oldColumns.keys).subtracting(newColumns.keys).sorted() {
|
||||
changes.append(SchemaChange(category: "Removed Column", object: "\(object).\(name)", detail: oldColumns[name]?.dataType ?? ""))
|
||||
}
|
||||
for name in Set(oldColumns.keys).intersection(newColumns.keys).sorted() {
|
||||
guard let before = oldColumns[name], let after = newColumns[name] else { continue }
|
||||
if before.dataType != after.dataType || before.nullable != after.nullable || before.key != after.key || before.defaultValue != after.defaultValue || before.extra != after.extra {
|
||||
changes.append(SchemaChange(category: "Changed Column", object: "\(object).\(name)", detail: "\(before.dataType) → \(after.dataType)"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return changes
|
||||
}
|
||||
@Published private(set) var introspections: [String: SchemaIntrospection] = [:]
|
||||
private let introspectionStorageKey = "DBSyn.introspection.snapshots.v2"
|
||||
private func persistIntrospections() {
|
||||
@@ -268,11 +319,19 @@ final class DatabaseModel: ObservableObject {
|
||||
}
|
||||
// Ignore results from a connection that has been removed while loading.
|
||||
guard profiles.contains(where: { $0.id == profile.id }) else { return }
|
||||
introspections[key] = SchemaIntrospection(
|
||||
let previous = introspections[key]
|
||||
let current = SchemaIntrospection(
|
||||
connectionID: profile.id, database: database,
|
||||
loadedAt: Date(), tables: tables, views: views, columnsByTable: columns
|
||||
)
|
||||
if let snapshot = introspections[key] { applyIntrospection(snapshot) }
|
||||
if let previous {
|
||||
schemaChangeReports[key] = SchemaChangeReport(
|
||||
database: database, checkedAt: current.loadedAt,
|
||||
changes: compareSchemas(previous: previous, current: current)
|
||||
)
|
||||
}
|
||||
introspections[key] = current
|
||||
applyIntrospection(current)
|
||||
persistIntrospections()
|
||||
status = "Introspection refreshed: \(tables.count) tables, \(views.count) views, \(columns.values.reduce(0) { $0 + $1.count }) columns in \(database)"
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user