added typeaheead and introspetion
This commit is contained in:
+38
-15
@@ -178,8 +178,8 @@ final class DatabaseModel: ObservableObject {
|
||||
@Published var schemaProcedures: [String: [String]] = [:]
|
||||
@Published var schemaFunctions: [String: [String]] = [:]
|
||||
@Published var schemaColumns: [String: [String]] = [:]
|
||||
// Introspection is a connection-scoped, RAM-only snapshot of table structures.
|
||||
struct IntrospectedColumn: Identifiable {
|
||||
// Connection-scoped metadata snapshots persist between launches.
|
||||
struct IntrospectedColumn: Identifiable, Codable {
|
||||
var id: String { "\(table).\(name)" }
|
||||
let table: String
|
||||
let name: String
|
||||
@@ -189,7 +189,7 @@ final class DatabaseModel: ObservableObject {
|
||||
let defaultValue: String?
|
||||
let extra: String
|
||||
}
|
||||
struct SchemaIntrospection {
|
||||
struct SchemaIntrospection: Codable {
|
||||
let connectionID: UUID
|
||||
let database: String
|
||||
let loadedAt: Date
|
||||
@@ -198,6 +198,21 @@ final class DatabaseModel: ObservableObject {
|
||||
let columnsByTable: [String: [IntrospectedColumn]]
|
||||
}
|
||||
@Published private(set) var introspections: [String: SchemaIntrospection] = [:]
|
||||
private let introspectionStorageKey = "DBSyn.introspection.snapshots.v2"
|
||||
private func persistIntrospections() {
|
||||
if let data = try? JSONEncoder().encode(introspections) {
|
||||
UserDefaults.standard.set(data, forKey: introspectionStorageKey)
|
||||
}
|
||||
}
|
||||
private func applyIntrospection(_ snapshot: SchemaIntrospection) {
|
||||
schemaTables[snapshot.database] = snapshot.tables
|
||||
schemaViews[snapshot.database] = snapshot.views
|
||||
schemaColumns[snapshot.database] = Array(Set(snapshot.columnsByTable.values.flatMap { $0 }.flatMap {
|
||||
[$0.name, "\($0.table).\($0.name)"]
|
||||
})).sorted()
|
||||
}
|
||||
func refreshIntrospection(database: String) { introspect(database: database) }
|
||||
|
||||
@Published private(set) var introspectionLoading: Set<String> = []
|
||||
private func introspectionKey(_ database: String, _ connectionID: UUID) -> String {
|
||||
"\(connectionID.uuidString):\(database)"
|
||||
@@ -242,12 +257,9 @@ final class DatabaseModel: ObservableObject {
|
||||
connectionID: profile.id, database: database,
|
||||
loadedAt: Date(), tables: tables, views: views, columnsByTable: columns
|
||||
)
|
||||
schemaTables[database] = tables
|
||||
schemaViews[database] = views
|
||||
schemaColumns[database] = Array(Set(columns.values.flatMap { $0 }.flatMap {
|
||||
[$0.name, "\($0.table).\($0.name)"]
|
||||
})).sorted()
|
||||
status = "Introspection complete: \(tables.count) tables, \(views.count) views, \(columns.values.reduce(0) { $0 + $1.count }) columns in \(database) (RAM)"
|
||||
if let snapshot = introspections[key] { applyIntrospection(snapshot) }
|
||||
persistIntrospections()
|
||||
status = "Introspection refreshed: \(tables.count) tables, \(views.count) views, \(columns.values.reduce(0) { $0 + $1.count }) columns in \(database)"
|
||||
} catch {
|
||||
status = "Introspection failed for \(database): \(error.localizedDescription)"
|
||||
}
|
||||
@@ -272,7 +284,16 @@ final class DatabaseModel: ObservableObject {
|
||||
if let data = UserDefaults.standard.data(forKey: storageKey),
|
||||
let decoded = try? JSONDecoder().decode([ConnectionProfile].self, from: data) { profiles = decoded }
|
||||
if profiles.isEmpty { profiles = [ConnectionProfile()] }
|
||||
if let data = UserDefaults.standard.data(forKey: introspectionStorageKey),
|
||||
let cached = try? JSONDecoder().decode([String: SchemaIntrospection].self, from: data) {
|
||||
introspections = cached.filter { entry in profiles.contains(where: { $0.id == entry.value.connectionID }) }
|
||||
}
|
||||
selectedID = profiles.first?.id
|
||||
if let id = selectedID {
|
||||
for snapshot in introspections.values where snapshot.connectionID == id {
|
||||
applyIntrospection(snapshot)
|
||||
}
|
||||
}
|
||||
activeQueryID = queryTabs.first?.id
|
||||
}
|
||||
|
||||
@@ -434,12 +455,12 @@ final class DatabaseModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
if let snapshot = introspection(for: database) {
|
||||
schemaTables[database] = snapshot.tables
|
||||
schemaViews[database] = snapshot.views
|
||||
} else {
|
||||
schemaTables[database] = tables
|
||||
schemaViews[database] = views
|
||||
schemaTables[database] = tables
|
||||
schemaViews[database] = views
|
||||
// Refresh cached structure when the server reports a changed object list.
|
||||
if let snapshot = introspection(for: database),
|
||||
(Set(snapshot.tables) != Set(tables) || Set(snapshot.views) != Set(views)) {
|
||||
introspect(database: database)
|
||||
}
|
||||
|
||||
// Routine metadata is separate from SHOW FULL TABLES.
|
||||
@@ -484,6 +505,8 @@ final class DatabaseModel: ObservableObject {
|
||||
}
|
||||
|
||||
status = "Loaded \(tables.count) tables and \(views.count) views from \(database)"
|
||||
// Revalidate column definitions as well as object names on reconnect/refresh.
|
||||
if introspection(for: database) != nil { introspect(database: database) }
|
||||
} catch {
|
||||
status = "Unable to load tables: \(error.localizedDescription)"
|
||||
result = QueryResult(message: error.localizedDescription)
|
||||
|
||||
Reference in New Issue
Block a user