diff --git a/CHANGELOG.md b/CHANGELOG.md index f2e86f113..a17a07147 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- The `postgres` database shows in the database list again. It was marked as a system database, which hid it from the sidebar, Cmd+K, the database filter, and the Backup and Restore Dump pickers. PostgreSQL creates it for users and applications, so nothing about it is internal. CockroachDB's `defaultdb` and Redshift's `dev` were hidden the same way and now show too. (#1967) +- The database a connection is using always shows in the sidebar and the database switchers, even when it is a system database or the database filter excludes it. (#1967) - The license activation sheet now opens when you click **Activate License**. It was built and then failed to appear, and once that happened further clicks did nothing at all. - File > Import from Other App..., Open Project Folder..., Import Connections... and Export Connections... now work when no welcome window is open. They used to do nothing. - The tooltip on the welcome screen's **+** button shows the shortcut you actually have bound for New Connection instead of always claiming ⌘N. diff --git a/Plugins/PostgreSQLDriverPlugin/CockroachPluginDriver.swift b/Plugins/PostgreSQLDriverPlugin/CockroachPluginDriver.swift index e6bdcb77c..3638cc65d 100644 --- a/Plugins/PostgreSQLDriverPlugin/CockroachPluginDriver.swift +++ b/Plugins/PostgreSQLDriverPlugin/CockroachPluginDriver.swift @@ -230,12 +230,11 @@ final class CockroachPluginDriver: LibPQBackedDriver, @unchecked Sendable { .flatMap { $0.rows.first?.first?.asText } .flatMap { Int($0) } - let systemDatabases = ["postgres", "system", "defaultdb"] return PluginDatabaseMetadata( name: database, tableCount: tableCount, sizeBytes: nil, - isSystemDatabase: systemDatabases.contains(database) + isSystemDatabase: PostgreSQLSystemDatabases.cockroachDB.contains(database) ) } diff --git a/Plugins/PostgreSQLDriverPlugin/PostgreSQLPlugin.swift b/Plugins/PostgreSQLDriverPlugin/PostgreSQLPlugin.swift index 940df5b68..cd73679b5 100644 --- a/Plugins/PostgreSQLDriverPlugin/PostgreSQLPlugin.swift +++ b/Plugins/PostgreSQLDriverPlugin/PostgreSQLPlugin.swift @@ -46,7 +46,7 @@ final class PostgreSQLPlugin: NSObject, TableProPlugin, DriverPlugin { static let urlSchemes: [String] = ["postgresql", "postgres"] static let brandColorHex = "#336791" - static let systemDatabaseNames: [String] = ["postgres", "template0", "template1"] + static let systemDatabaseNames: [String] = PostgreSQLSystemDatabases.postgreSQL static let supportsSchemaSwitching = true static let postConnectActions: [PostConnectAction] = [.selectSchemaFromLastSession] static let explainVariants: [ExplainVariant] = [ diff --git a/Plugins/PostgreSQLDriverPlugin/PostgreSQLPluginDriver.swift b/Plugins/PostgreSQLDriverPlugin/PostgreSQLPluginDriver.swift index 9e6ac8e09..c021c3d7f 100644 --- a/Plugins/PostgreSQLDriverPlugin/PostgreSQLPluginDriver.swift +++ b/Plugins/PostgreSQLDriverPlugin/PostgreSQLPluginDriver.swift @@ -667,19 +667,15 @@ class PostgreSQLPluginDriver: LibPQBackedDriver, @unchecked Sendable { let tableCount = Int(row?[0].asText ?? "0") ?? 0 let sizeBytes = Int64(row?[1].asText ?? "0") ?? 0 - let systemDatabases = ["postgres", "template0", "template1"] - let isSystem = systemDatabases.contains(database) - return PluginDatabaseMetadata( name: database, tableCount: tableCount, sizeBytes: sizeBytes, - isSystemDatabase: isSystem + isSystemDatabase: PostgreSQLSystemDatabases.postgreSQL.contains(database) ) } func fetchAllDatabaseMetadata() async throws -> [PluginDatabaseMetadata] { - let systemDatabases = ["postgres", "template0", "template1"] let query = """ SELECT d.datname, pg_database_size(d.datname) FROM pg_database d @@ -690,8 +686,11 @@ class PostgreSQLPluginDriver: LibPQBackedDriver, @unchecked Sendable { return result.rows.compactMap { row -> PluginDatabaseMetadata? in guard let dbName = row[0].asText else { return nil } let sizeBytes = Int64(row[1].asText ?? "0") ?? 0 - let isSystem = systemDatabases.contains(dbName) - return PluginDatabaseMetadata(name: dbName, sizeBytes: sizeBytes, isSystemDatabase: isSystem) + return PluginDatabaseMetadata( + name: dbName, + sizeBytes: sizeBytes, + isSystemDatabase: PostgreSQLSystemDatabases.postgreSQL.contains(dbName) + ) } } diff --git a/Plugins/PostgreSQLDriverPlugin/PostgreSQLSystemDatabases.swift b/Plugins/PostgreSQLDriverPlugin/PostgreSQLSystemDatabases.swift new file mode 100644 index 000000000..0c632de93 --- /dev/null +++ b/Plugins/PostgreSQLDriverPlugin/PostgreSQLSystemDatabases.swift @@ -0,0 +1,12 @@ +// +// PostgreSQLSystemDatabases.swift +// PostgreSQLDriverPlugin +// + +import Foundation + +enum PostgreSQLSystemDatabases { + static let postgreSQL: [String] = [] + static let redshift: [String] = ["padb_harvest"] + static let cockroachDB: [String] = ["system"] +} diff --git a/Plugins/PostgreSQLDriverPlugin/RedshiftPluginDriver.swift b/Plugins/PostgreSQLDriverPlugin/RedshiftPluginDriver.swift index 95689a093..c77855dae 100644 --- a/Plugins/PostgreSQLDriverPlugin/RedshiftPluginDriver.swift +++ b/Plugins/PostgreSQLDriverPlugin/RedshiftPluginDriver.swift @@ -389,19 +389,15 @@ final class RedshiftPluginDriver: LibPQBackedDriver, @unchecked Sendable { let sizeMb = Int64(sizeRes.rows.first?[0].asText ?? "0") ?? 0 let sizeBytes = sizeMb * 1_024 * 1_024 - let systemDatabases = ["dev", "padb_harvest"] - let isSystem = systemDatabases.contains(database) - return PluginDatabaseMetadata( name: database, tableCount: tableCount, sizeBytes: sizeBytes, - isSystemDatabase: isSystem + isSystemDatabase: PostgreSQLSystemDatabases.redshift.contains(database) ) } func fetchAllDatabaseMetadata() async throws -> [PluginDatabaseMetadata] { - let systemDatabases = ["dev", "padb_harvest"] let dbResult = try await execute( query: "SELECT datname FROM pg_database WHERE datistemplate = false ORDER BY datname" ) @@ -423,13 +419,12 @@ final class RedshiftPluginDriver: LibPQBackedDriver, @unchecked Sendable { } return dbNames.map { dbName in - let isSystem = systemDatabases.contains(dbName) let info = metadataByName[dbName] return PluginDatabaseMetadata( name: dbName, tableCount: info?.tableCount, sizeBytes: info.map { $0.sizeMb * 1_024 * 1_024 }, - isSystemDatabase: isSystem + isSystemDatabase: PostgreSQLSystemDatabases.redshift.contains(dbName) ) } } diff --git a/TablePro/Core/Plugins/PluginMetadataRegistry.swift b/TablePro/Core/Plugins/PluginMetadataRegistry.swift index 9909756f4..3f6073d64 100644 --- a/TablePro/Core/Plugins/PluginMetadataRegistry.swift +++ b/TablePro/Core/Plugins/PluginMetadataRegistry.swift @@ -641,7 +641,7 @@ final class PluginMetadataRegistry: @unchecked Sendable { containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], - systemDatabaseNames: ["postgres", "template0", "template1"], + systemDatabaseNames: [], systemSchemaNames: [], fileExtensions: [], databaseGroupingStrategy: .bySchema, @@ -691,7 +691,7 @@ final class PluginMetadataRegistry: @unchecked Sendable { containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], - systemDatabaseNames: ["postgres", "template0", "template1"], + systemDatabaseNames: ["padb_harvest"], systemSchemaNames: [], fileExtensions: [], databaseGroupingStrategy: .bySchema, @@ -752,7 +752,7 @@ final class PluginMetadataRegistry: @unchecked Sendable { containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], - systemDatabaseNames: ["postgres", "system", "defaultdb"], + systemDatabaseNames: ["system"], systemSchemaNames: [], fileExtensions: [], databaseGroupingStrategy: .bySchema, @@ -806,7 +806,7 @@ final class PluginMetadataRegistry: @unchecked Sendable { containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], - systemDatabaseNames: ["postgres", "template0", "template1"], + systemDatabaseNames: [], systemSchemaNames: [], fileExtensions: [], databaseGroupingStrategy: .bySchema, diff --git a/TablePro/Core/Services/Query/DatabaseTreeVisibility.swift b/TablePro/Core/Services/Query/DatabaseTreeVisibility.swift index 8c7352186..5bd448d62 100644 --- a/TablePro/Core/Services/Query/DatabaseTreeVisibility.swift +++ b/TablePro/Core/Services/Query/DatabaseTreeVisibility.swift @@ -1,10 +1,17 @@ import Foundation enum DatabaseTreeVisibility { - static func visible(databases: [DatabaseMetadata], selected: Set) -> [DatabaseMetadata] { - let nonSystem = databases.filter { !$0.isSystemDatabase } - guard !selected.isEmpty else { return nonSystem } - return nonSystem.filter { selected.contains($0.name) } + static func visible( + databases: [DatabaseMetadata], + selected: Set, + activeDatabase: String? + ) -> [DatabaseMetadata] { + let active = activeDatabase.flatMap { $0.isEmpty ? nil : $0 } + return databases.filter { database in + if database.name == active { return true } + guard !database.isSystemDatabase else { return false } + return selected.isEmpty || selected.contains(database.name) + } } static func isFiltering(selected: Set) -> Bool { diff --git a/TablePro/ViewModels/DatabaseSwitcherViewModel.swift b/TablePro/ViewModels/DatabaseSwitcherViewModel.swift index 75f97c677..5fe3270b4 100644 --- a/TablePro/ViewModels/DatabaseSwitcherViewModel.swift +++ b/TablePro/ViewModels/DatabaseSwitcherViewModel.swift @@ -30,8 +30,12 @@ final class DatabaseSwitcherViewModel { private let sidebarState: SharedSidebarState? private var treeVisibleDatabases: [DatabaseMetadata] { - guard switchTarget == .database, let sidebarState else { return databases } - return DatabaseTreeVisibility.visible(databases: databases, selected: sidebarState.databaseFilterSelected) + guard switchTarget == .database else { return databases } + return DatabaseTreeVisibility.visible( + databases: databases, + selected: sidebarState?.databaseFilterSelected ?? [], + activeDatabase: currentDatabase + ) } var filteredDatabases: [DatabaseMetadata] { diff --git a/TablePro/ViewModels/QuickSwitcherViewModel.swift b/TablePro/ViewModels/QuickSwitcherViewModel.swift index 775d72503..462b748ca 100644 --- a/TablePro/ViewModels/QuickSwitcherViewModel.swift +++ b/TablePro/ViewModels/QuickSwitcherViewModel.swift @@ -127,11 +127,14 @@ internal final class QuickSwitcherViewModel { let switchTarget = services.pluginManager.containerSwitchTarget(for: databaseType) let databaseFilter = SharedSidebarState.forConnection(connectionId).databaseFilterSelected + let activeDatabase = services.databaseManager.session(for: connectionId) + .map { services.databaseManager.activeDatabaseName(for: $0.connection) } let visibleDatabaseNames = switchTarget == .database ? Set( DatabaseTreeVisibility.visible( databases: DatabaseTreeMetadataService.shared.databases(for: connectionId), - selected: databaseFilter + selected: databaseFilter, + activeDatabase: activeDatabase ).map(\.name) ) : [] @@ -146,7 +149,7 @@ internal final class QuickSwitcherViewModel { if switchTarget == .database { if !visibleDatabaseNames.isEmpty { if !visibleDatabaseNames.contains(db) { continue } - } else if !databaseFilter.isEmpty, !databaseFilter.contains(db) { + } else if !databaseFilter.isEmpty, db != activeDatabase, !databaseFilter.contains(db) { continue } } diff --git a/TablePro/Views/Main/Child/MainEditorContentView.swift b/TablePro/Views/Main/Child/MainEditorContentView.swift index 7578f7e5f..b84c6a4ba 100644 --- a/TablePro/Views/Main/Child/MainEditorContentView.swift +++ b/TablePro/Views/Main/Child/MainEditorContentView.swift @@ -300,13 +300,11 @@ struct MainEditorContentView: View { guard containerSwitchTarget == .database else { return [] } let all = treeService.databases(for: connectionId) let selected = SharedSidebarState.forConnection(connectionId).databaseFilterSelected - var visible = DatabaseTreeVisibility.visible(databases: all, selected: selected) - let bound = containerName(for: tab) - if !bound.isEmpty, !visible.contains(where: { $0.name == bound }), - let boundDatabase = all.first(where: { $0.name == bound }) { - visible.insert(boundDatabase, at: 0) - } - return visible + return DatabaseTreeVisibility.visible( + databases: all, + selected: selected, + activeDatabase: containerName(for: tab) + ) } private var isContainerSwitchReadOnly: Bool { diff --git a/TablePro/Views/Sidebar/DatabaseTreeOutlineCoordinator.swift b/TablePro/Views/Sidebar/DatabaseTreeOutlineCoordinator.swift index aa64a74ae..fc4c2e363 100644 --- a/TablePro/Views/Sidebar/DatabaseTreeOutlineCoordinator.swift +++ b/TablePro/Views/Sidebar/DatabaseTreeOutlineCoordinator.swift @@ -196,7 +196,8 @@ final class DatabaseTreeOutlineCoordinator: NSObject { } let visible = DatabaseTreeVisibility.visible( databases: service.databases(for: connectionId), - selected: sidebarState?.databaseFilterSelected ?? [] + selected: sidebarState?.databaseFilterSelected ?? [], + activeDatabase: mainCoordinator?.activeDatabaseName ?? activeDatabase ) let matched = searchText.isEmpty ? visible : visible.filter { databaseMatchesSearch($0) } var seen = Set() diff --git a/TablePro/Views/Sidebar/DatabaseTreeView.swift b/TablePro/Views/Sidebar/DatabaseTreeView.swift index 5f69e7426..1cd9f9c98 100644 --- a/TablePro/Views/Sidebar/DatabaseTreeView.swift +++ b/TablePro/Views/Sidebar/DatabaseTreeView.swift @@ -70,7 +70,11 @@ struct DatabaseTreeView: View { } private var filteredDatabases: [DatabaseMetadata] { - DatabaseTreeVisibility.visible(databases: databases, selected: sidebarState.databaseFilterSelected) + DatabaseTreeVisibility.visible( + databases: databases, + selected: sidebarState.databaseFilterSelected, + activeDatabase: activeDatabase + ) } private var isFilterHidingEverything: Bool { diff --git a/TableProTests/Core/Plugins/PluginMetadataRegistrySystemDatabaseTests.swift b/TableProTests/Core/Plugins/PluginMetadataRegistrySystemDatabaseTests.swift new file mode 100644 index 000000000..fa87d8a73 --- /dev/null +++ b/TableProTests/Core/Plugins/PluginMetadataRegistrySystemDatabaseTests.swift @@ -0,0 +1,62 @@ +// +// PluginMetadataRegistrySystemDatabaseTests.swift +// TableProTests +// +// The registry's curated defaults are the pre-plugin-load classification and +// must agree with what the PostgreSQL plugin reports once it registers. +// Redshift shipped a disagreement that flipped which database was hidden +// partway through loading the switcher (#1967). +// + +import Foundation +@testable import TablePro +import TableProPluginKit +import Testing + +@MainActor +@Suite("PluginMetadataRegistry system databases") +struct PluginMetadataRegistrySystemDatabaseTests { + private func systemDatabaseNames(forTypeId typeId: String) -> [String]? { + PluginMetadataRegistry.shared.snapshot(forTypeId: typeId)?.schema.systemDatabaseNames + } + + @Test("PostgreSQL default matches the plugin") + func postgreSQLMatchesPlugin() { + #expect(systemDatabaseNames(forTypeId: "PostgreSQL") == PostgreSQLSystemDatabases.postgreSQL) + } + + @Test("PGlite default matches the plugin") + func pgliteMatchesPlugin() { + #expect(systemDatabaseNames(forTypeId: "PGlite") == PostgreSQLSystemDatabases.postgreSQL) + } + + @Test("Redshift default matches the plugin") + func redshiftMatchesPlugin() { + #expect(systemDatabaseNames(forTypeId: "Redshift") == PostgreSQLSystemDatabases.redshift) + } + + @Test("CockroachDB default matches the plugin") + func cockroachMatchesPlugin() { + #expect(systemDatabaseNames(forTypeId: "CockroachDB") == PostgreSQLSystemDatabases.cockroachDB) + } + + @Test("No PostgreSQL-family engine hides its default landing database") + func defaultLandingDatabasesStayVisible() { + let defaults = [ + ("PostgreSQL", "postgres"), + ("PGlite", "postgres"), + ("Redshift", "dev"), + ("CockroachDB", "defaultdb"), + ] + for (typeId, database) in defaults { + guard let names = systemDatabaseNames(forTypeId: typeId) else { + Issue.record("Registry default for \(typeId) missing") + continue + } + #expect( + !names.contains(database), + "\(typeId) marks its default database \(database) as system, which hides it from every database list" + ) + } + } +} diff --git a/TableProTests/Core/Services/Query/DatabaseTreeVisibilityTests.swift b/TableProTests/Core/Services/Query/DatabaseTreeVisibilityTests.swift index 0b26d1516..2c07f30db 100644 --- a/TableProTests/Core/Services/Query/DatabaseTreeVisibilityTests.swift +++ b/TableProTests/Core/Services/Query/DatabaseTreeVisibilityTests.swift @@ -13,28 +13,68 @@ struct DatabaseTreeVisibilityTests { @Test("Empty selection shows all non-system databases") func emptyShowsAll() { - let visible = DatabaseTreeVisibility.visible(databases: databases, selected: []) + let visible = DatabaseTreeVisibility.visible(databases: databases, selected: [], activeDatabase: nil) #expect(visible.map(\.name) == ["analytics", "billing", "legacy_2019"]) } @Test("Non-empty selection shows only the selected non-system databases") func selectionShowsSubset() { - let visible = DatabaseTreeVisibility.visible(databases: databases, selected: ["billing", "legacy_2019"]) + let visible = DatabaseTreeVisibility.visible( + databases: databases, + selected: ["billing", "legacy_2019"], + activeDatabase: nil + ) #expect(visible.map(\.name) == ["billing", "legacy_2019"]) } - @Test("System databases are never shown even when selected") - func systemNeverShown() { - let visible = DatabaseTreeVisibility.visible(databases: databases, selected: ["mysql", "analytics"]) + @Test("System databases are hidden even when selected") + func systemHiddenWhenSelected() { + let visible = DatabaseTreeVisibility.visible( + databases: databases, + selected: ["mysql", "analytics"], + activeDatabase: nil + ) #expect(visible.map(\.name) == ["analytics"]) } @Test("Selecting a database that no longer exists yields an empty result") func staleSelectionEmpty() { - let visible = DatabaseTreeVisibility.visible(databases: databases, selected: ["dropped_db"]) + let visible = DatabaseTreeVisibility.visible(databases: databases, selected: ["dropped_db"], activeDatabase: nil) #expect(visible.isEmpty) } + @Test("The active database stays visible even when it is a system database") + func activeSystemDatabaseStaysVisible() { + let visible = DatabaseTreeVisibility.visible(databases: databases, selected: [], activeDatabase: "mysql") + #expect(visible.map(\.name) == ["analytics", "billing", "legacy_2019", "mysql"]) + } + + @Test("The active database stays visible when the filter excludes it") + func activeDatabaseSurvivesFilter() { + let visible = DatabaseTreeVisibility.visible( + databases: databases, + selected: ["billing"], + activeDatabase: "analytics" + ) + #expect(visible.map(\.name) == ["analytics", "billing"]) + } + + @Test("The active database keeps its position in the list") + func activeDatabaseKeepsPosition() { + let visible = DatabaseTreeVisibility.visible( + databases: databases, + selected: [], + activeDatabase: "information_schema" + ) + #expect(visible.map(\.name) == ["analytics", "billing", "legacy_2019", "information_schema"]) + } + + @Test("An empty active database name is treated as absent") + func emptyActiveDatabaseIgnored() { + let visible = DatabaseTreeVisibility.visible(databases: databases, selected: [], activeDatabase: "") + #expect(visible.map(\.name) == ["analytics", "billing", "legacy_2019"]) + } + @Test("isFiltering reflects whether a selection is active") func isFiltering() { #expect(DatabaseTreeVisibility.isFiltering(selected: []) == false) diff --git a/TableProTests/PluginTestSources/PostgreSQLSystemDatabases.swift b/TableProTests/PluginTestSources/PostgreSQLSystemDatabases.swift new file mode 120000 index 000000000..338296840 --- /dev/null +++ b/TableProTests/PluginTestSources/PostgreSQLSystemDatabases.swift @@ -0,0 +1 @@ +../../Plugins/PostgreSQLDriverPlugin/PostgreSQLSystemDatabases.swift \ No newline at end of file diff --git a/TableProTests/Plugins/PostgreSQLSystemDatabasesTests.swift b/TableProTests/Plugins/PostgreSQLSystemDatabasesTests.swift new file mode 100644 index 000000000..1815389be --- /dev/null +++ b/TableProTests/Plugins/PostgreSQLSystemDatabasesTests.swift @@ -0,0 +1,38 @@ +// +// PostgreSQLSystemDatabasesTests.swift +// TableProTests +// +// Regression cover for #1967: the "postgres" database is an ordinary user +// database created by initdb, not a system database, and hiding it made it +// unreachable from every database list. The same mistake hid CockroachDB's +// "defaultdb" and Redshift's "dev". +// + +import Foundation +import Testing + +@Suite("PostgreSQLSystemDatabases") +struct PostgreSQLSystemDatabasesTests { + @Test("PostgreSQL has no system databases") + func postgreSQLHasNone() { + #expect(PostgreSQLSystemDatabases.postgreSQL.isEmpty) + } + + @Test("The default postgres database is never a system database") + func postgresDatabaseIsNotSystem() { + #expect(!PostgreSQLSystemDatabases.postgreSQL.contains("postgres")) + #expect(!PostgreSQLSystemDatabases.cockroachDB.contains("postgres")) + } + + @Test("CockroachDB marks only its system catalog database") + func cockroachMarksSystemOnly() { + #expect(PostgreSQLSystemDatabases.cockroachDB == ["system"]) + #expect(!PostgreSQLSystemDatabases.cockroachDB.contains("defaultdb")) + } + + @Test("Redshift marks only its internal database") + func redshiftMarksInternalOnly() { + #expect(PostgreSQLSystemDatabases.redshift == ["padb_harvest"]) + #expect(!PostgreSQLSystemDatabases.redshift.contains("dev")) + } +} diff --git a/docs/databases/cockroachdb.mdx b/docs/databases/cockroachdb.mdx index 98a53900b..58db5b446 100644 --- a/docs/databases/cockroachdb.mdx +++ b/docs/databases/cockroachdb.mdx @@ -38,6 +38,8 @@ cockroachdb://user:password@host:26257/defaultdb **Schemas**: like PostgreSQL, default schema `public`. Cmd+K switches databases; the schema menu at the bottom of the sidebar switches schemas. +**Databases**: `defaultdb` and `postgres`, the two databases a new cluster starts with, are listed like any other. Only `system` is marked as a system database. + **DDL**: table and view definitions come from `SHOW CREATE`. Indexes come from `SHOW INDEXES`. Foreign keys are read from `information_schema`. **EXPLAIN**: `EXPLAIN` and `EXPLAIN ANALYZE` render as a visual plan tree. CockroachDB returns text plans, not JSON, and TablePro parses them into the tree. See [EXPLAIN Visualization](/features/explain-visualization). diff --git a/docs/databases/postgresql.mdx b/docs/databases/postgresql.mdx index 3ae12c436..3083c7b6d 100644 --- a/docs/databases/postgresql.mdx +++ b/docs/databases/postgresql.mdx @@ -37,6 +37,8 @@ Connect to RDS or Aurora with your AWS identity instead of a static password: se **Schemas**: The sidebar shows all accessible schemas and tables. Cmd+K switches databases; pick the active schema from the schema menu at the bottom of the sidebar. Table info shows columns, indexes, constraints, and DDL. +**Databases**: Every database on the server is listed, including `postgres`. It is an ordinary database that `initdb` creates for users and applications, not a system database. `template0` and `template1` are not listed. + **Types**: `jsonb` renders as formatted JSON. `array`, `uuid`, `inet`, `timestamp with time zone`, `interval`, and `bytea` display natively. **EXPLAIN**: `EXPLAIN` and `EXPLAIN ANALYZE` run with `FORMAT JSON` and render as a plan diagram or tree. See [EXPLAIN Visualization](/features/explain-visualization). diff --git a/docs/databases/redshift.mdx b/docs/databases/redshift.mdx index 1bc6f09c0..847de337d 100644 --- a/docs/databases/redshift.mdx +++ b/docs/databases/redshift.mdx @@ -31,6 +31,8 @@ See [Connection URL Reference](/databases/connection-urls). **Schemas**: like PostgreSQL, default schema `public`. Table metadata comes from `svv_table_info`: distribution style, sort keys, and table size. +**Databases**: `dev`, the database created with every cluster, is listed like any other. Only `padb_harvest` is marked as a system database. + **DDL**: table definitions include DISTKEY, SORTKEY, DISTSTYLE, and ENCODE directives. Foreign keys display as informational; Redshift does not enforce them.