fix: trust .dump extension when file doesn't exist in DetectArchiveFormat

The format detection now returns PostgreSQL Dump format for .dump files
when the file cannot be opened (e.g., when just checking filename pattern),
instead of falling back to SQL format.

This fixes the test that passes just a filename string without an actual file.
This commit is contained in:
2025-12-11 17:39:19 +01:00
parent 54a0dcaff1
commit 4be8a96699

View File

@@ -1,24 +1,24 @@
package restore package restore
import ( import (
"compress/gzip" "compress/gzip"
"io" "io"
"os" "os"
"strings" "strings"
) )
// ArchiveFormat represents the type of backup archive // ArchiveFormat represents the type of backup archive
type ArchiveFormat string type ArchiveFormat string
const ( const (
FormatPostgreSQLDump ArchiveFormat = "PostgreSQL Dump (.dump)" FormatPostgreSQLDump ArchiveFormat = "PostgreSQL Dump (.dump)"
FormatPostgreSQLDumpGz ArchiveFormat = "PostgreSQL Dump Compressed (.dump.gz)" FormatPostgreSQLDumpGz ArchiveFormat = "PostgreSQL Dump Compressed (.dump.gz)"
FormatPostgreSQLSQL ArchiveFormat = "PostgreSQL SQL (.sql)" FormatPostgreSQLSQL ArchiveFormat = "PostgreSQL SQL (.sql)"
FormatPostgreSQLSQLGz ArchiveFormat = "PostgreSQL SQL Compressed (.sql.gz)" FormatPostgreSQLSQLGz ArchiveFormat = "PostgreSQL SQL Compressed (.sql.gz)"
FormatMySQLSQL ArchiveFormat = "MySQL SQL (.sql)" FormatMySQLSQL ArchiveFormat = "MySQL SQL (.sql)"
FormatMySQLSQLGz ArchiveFormat = "MySQL SQL Compressed (.sql.gz)" FormatMySQLSQLGz ArchiveFormat = "MySQL SQL Compressed (.sql.gz)"
FormatClusterTarGz ArchiveFormat = "Cluster Archive (.tar.gz)" FormatClusterTarGz ArchiveFormat = "Cluster Archive (.tar.gz)"
FormatUnknown ArchiveFormat = "Unknown" FormatUnknown ArchiveFormat = "Unknown"
) )
// DetectArchiveFormat detects the format of a backup archive from its filename and content // DetectArchiveFormat detects the format of a backup archive from its filename and content
@@ -30,20 +30,25 @@ func DetectArchiveFormat(filename string) ArchiveFormat {
return FormatClusterTarGz return FormatClusterTarGz
} }
// For .dump files, check if they're actually custom format or SQL text // For .dump files, assume PostgreSQL custom format based on extension
// If the file exists and can be read, verify with magic bytes
if strings.HasSuffix(lower, ".dump.gz") { if strings.HasSuffix(lower, ".dump.gz") {
if isCustomFormat(filename, true) { // Check if file exists and has content signature
result := isCustomFormat(filename, true)
// If file doesn't exist or we can't read it, trust the extension
// If file exists and has PGDMP signature, it's custom format
// If file exists but doesn't have signature, it might be SQL named as .dump
if result == formatCheckCustom || result == formatCheckFileNotFound {
return FormatPostgreSQLDumpGz return FormatPostgreSQLDumpGz
} }
// If not custom format, treat as SQL
return FormatPostgreSQLSQLGz return FormatPostgreSQLSQLGz
} }
if strings.HasSuffix(lower, ".dump") { if strings.HasSuffix(lower, ".dump") {
if isCustomFormat(filename, false) { result := isCustomFormat(filename, false)
if result == formatCheckCustom || result == formatCheckFileNotFound {
return FormatPostgreSQLDump return FormatPostgreSQLDump
} }
// If not custom format, treat as SQL
return FormatPostgreSQLSQL return FormatPostgreSQLSQL
} }
@@ -72,11 +77,21 @@ func DetectArchiveFormat(filename string) ArchiveFormat {
return FormatUnknown return FormatUnknown
} }
// formatCheckResult represents the result of checking file format
type formatCheckResult int
const (
formatCheckFileNotFound formatCheckResult = iota
formatCheckCustom
formatCheckNotCustom
)
// isCustomFormat checks if a file is PostgreSQL custom format (has PGDMP signature) // isCustomFormat checks if a file is PostgreSQL custom format (has PGDMP signature)
func isCustomFormat(filename string, compressed bool) bool { func isCustomFormat(filename string, compressed bool) formatCheckResult {
file, err := os.Open(filename) file, err := os.Open(filename)
if err != nil { if err != nil {
return false // File doesn't exist or can't be opened - return file not found
return formatCheckFileNotFound
} }
defer file.Close() defer file.Close()
@@ -86,7 +101,7 @@ func isCustomFormat(filename string, compressed bool) bool {
if compressed { if compressed {
gz, err := gzip.NewReader(file) gz, err := gzip.NewReader(file)
if err != nil { if err != nil {
return false return formatCheckFileNotFound
} }
defer gz.Close() defer gz.Close()
reader = gz reader = gz
@@ -96,10 +111,13 @@ func isCustomFormat(filename string, compressed bool) bool {
buffer := make([]byte, 5) buffer := make([]byte, 5)
n, err := reader.Read(buffer) n, err := reader.Read(buffer)
if err != nil || n < 5 { if err != nil || n < 5 {
return false return formatCheckNotCustom
} }
return string(buffer) == "PGDMP" if string(buffer) == "PGDMP" {
return formatCheckCustom
}
return formatCheckNotCustom
} }
// IsCompressed returns true if the archive format is compressed // IsCompressed returns true if the archive format is compressed