feat(postgresus): Add schema filter for pg_dump and pg_restore (#131)

Add optional "Schemas" field to PostgreSQL database settings allowing
users to specify which schemas to include in backups (comma-separated).

This solves permission issues when backing up some of databases that
have restricted internal schemas (auth, storage, realtime).

Changes:
- Add schemas column to postgresql_databases table (migration)
- Update PostgresqlDatabase model with Schemas field
- Modify buildPgDumpArgs() to append --schema flags for each schema
- Modify pg_restore args to support --schema filtering on restore
- Add Schemas input field to frontend edit form with tooltip
- Display schemas in read-only database view

Example usage: Setting schemas to "public,drizzle" generates:
  pg_dump ... --schema public --schema drizzle
  pg_restore ... --schema public --schema drizzle
This commit is contained in:
Leonardo Flores
2025-12-10 07:19:15 -03:00
committed by GitHub
parent 1f5c9d3d01
commit 5944d7c4b6
8 changed files with 72 additions and 1 deletions

View File

@@ -334,6 +334,17 @@ func (uc *CreatePostgresqlBackupUsecase) buildPgDumpArgs(pg *pgtypes.PostgresqlD
"--verbose",
}
// Add schema filters if specified
if pg.Schemas != nil && *pg.Schemas != "" {
schemas := strings.Split(*pg.Schemas, ",")
for _, schema := range schemas {
schema = strings.TrimSpace(schema)
if schema != "" {
args = append(args, "--schema", schema)
}
}
}
compressionArgs := uc.getCompressionArgs(pg.Version)
return append(args, compressionArgs...)
}

View File

@@ -29,6 +29,7 @@ type PostgresqlDatabase struct {
Password string `json:"password" gorm:"type:text;not null"`
Database *string `json:"database" gorm:"type:text"`
IsHttps bool `json:"isHttps" gorm:"type:boolean;default:false"`
Schemas *string `json:"schemas" gorm:"type:text"`
}
func (p *PostgresqlDatabase) TableName() string {
@@ -85,6 +86,7 @@ func (p *PostgresqlDatabase) Update(incoming *PostgresqlDatabase) {
p.Username = incoming.Username
p.Database = incoming.Database
p.IsHttps = incoming.IsHttps
p.Schemas = incoming.Schemas
if incoming.Password != "" {
p.Password = incoming.Password

View File

@@ -83,6 +83,17 @@ func (uc *RestorePostgresqlBackupUsecase) Execute(
"--no-acl", // Skip restoring access privileges (GRANT/REVOKE commands)
}
// Add schema filters if specified
if pg.Schemas != nil && *pg.Schemas != "" {
schemas := strings.Split(*pg.Schemas, ",")
for _, schema := range schemas {
schema = strings.TrimSpace(schema)
if schema != "" {
args = append(args, "--schema", schema)
}
}
}
return uc.restoreFromStorage(
originalDB,
tools.GetPostgresqlExecutable(

View File

@@ -0,0 +1,15 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE postgresql_databases
ADD COLUMN schemas TEXT;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE postgresql_databases
DROP COLUMN IF EXISTS schemas;
-- +goose StatementEnd

View File

@@ -11,4 +11,5 @@ export interface PostgresqlDatabase {
password: string;
database?: string;
isHttps: boolean;
schemas?: string;
}

View File

@@ -649,7 +649,7 @@ export const BackupsComponent = ({ database, isCanManageDBs, scrollContainerRef
{showingRestoresBackupId && (
<Modal
width={400}
width={420}
open={!!showingRestoresBackupId}
onCancel={() => setShowingRestoresBackupId(undefined)}
title="Restore from backup"

View File

@@ -275,6 +275,32 @@ export const EditDatabaseSpecificDataComponent = ({
</div>
)}
<div className="mb-1 flex w-full items-center">
<div className="min-w-[150px]">Schemas</div>
<Input
value={editingDatabase.postgresql?.schemas}
onChange={(e) => {
if (!editingDatabase.postgresql) return;
setEditingDatabase({
...editingDatabase,
postgresql: { ...editingDatabase.postgresql, schemas: e.target.value.trim() },
});
setIsConnectionTested(false);
}}
size="small"
className="max-w-[200px] grow"
placeholder="public,myschema (optional)"
/>
<Tooltip
className="cursor-pointer"
title="Comma-separated list of schemas to include. Leave empty for all schemas."
>
<InfoCircleOutlined className="ml-2" style={{ color: 'gray' }} />
</Tooltip>
</div>
<div className="mb-3 flex w-full items-center">
<div className="min-w-[150px]">Use HTTPS</div>
<Switch

View File

@@ -53,6 +53,11 @@ export const ShowDatabaseSpecificDataComponent = ({ database }: Props) => {
<div>{database.postgresql?.database || ''}</div>
</div>
<div className="mb-1 flex w-full items-center">
<div className="min-w-[150px]">Schemas</div>
<div>{database.postgresql?.schemas || 'All (full backup)'}</div>
</div>
<div className="mb-1 flex w-full items-center">
<div className="min-w-[150px]">Use HTTPS</div>
<div>{database.postgresql?.isHttps ? 'Yes' : 'No'}</div>