diff --git a/backup/backup.go b/backup/backup.go index 675192c20..137fe6fc9 100644 --- a/backup/backup.go +++ b/backup/backup.go @@ -226,10 +226,10 @@ func backupPredata(metadataFile *utils.FileWithByteCount, tables []Table, tableO objects := make([]Sortable, 0) metadataMap := make(MetadataMap) + objects = append(objects, convertToSortableSlice(tables)...) if !tableOnly { functions, funcInfoMap = retrieveFunctions(&objects, metadataMap) } - objects = append(objects, convertToSortableSlice(tables)...) relationMetadata := GetMetadataForObjectType(connectionPool, TYPE_RELATION) addToMetadataMap(relationMetadata, metadataMap) diff --git a/backup/dependencies.go b/backup/dependencies.go index 6f3f80274..8ecd28fbf 100644 --- a/backup/dependencies.go +++ b/backup/dependencies.go @@ -2,6 +2,7 @@ package backup import ( "fmt" + "sort" "strings" "github.com/greenplum-db/gp-common-go-libs/dbconn" @@ -85,6 +86,24 @@ type Sortable interface { GetUniqueID() UniqueID } +type Sortables []Sortable + +func (s Sortables) Len() int { + return len(s) +} +func (s Sortables) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s Sortables) Less(i, j int) bool { + return s[i].GetUniqueID().Oid < s[j].GetUniqueID().Oid +} + +func SortByOid(sortables []Sortable) []Sortable { + s := Sortables(sortables) + sort.Sort(s) + return []Sortable(s) +} + func TopologicalSort(slice []Sortable, dependencies DependencyMap) []Sortable { inDegrees := make(map[UniqueID]int) dependencyIndexes := make(map[UniqueID]int) diff --git a/backup/wrappers.go b/backup/wrappers.go index 90ff95fe7..295af9a39 100644 --- a/backup/wrappers.go +++ b/backup/wrappers.go @@ -566,6 +566,7 @@ func backupDependentObjects(metadataFile *utils.FileWithByteCount, tables []Tabl gplog.Verbose("Writing CREATE statements for dependent objects to metadata file") + sortables = SortByOid(sortables) backupSet := createBackupSet(sortables) relevantDeps := GetDependencies(connectionPool, backupSet) if connectionPool.Version.Is("4") && !tableOnly { diff --git a/end_to_end/end_to_end_suite_test.go b/end_to_end/end_to_end_suite_test.go index 5d83baf45..d4ba253e9 100644 --- a/end_to_end/end_to_end_suite_test.go +++ b/end_to_end/end_to_end_suite_test.go @@ -1445,6 +1445,31 @@ var _ = Describe("backup and restore end to end tests", func() { assertArtifactsCleaned(restoreConn, timestamp) }) + It("runs gpbackup and gprestore to backup functions depending on table row's type", func() { + skipIfOldBackupVersionBefore("1.19.0") + + testhelper.AssertQueryRuns(backupConn, "CREATE TABLE table_provides_type (n int);") + defer testhelper.AssertQueryRuns(backupConn, "DROP TABLE table_provides_type;") + + testhelper.AssertQueryRuns(backupConn, "INSERT INTO table_provides_type values (1);") + testhelper.AssertQueryRuns(backupConn, "CREATE OR REPLACE FUNCTION func_depends_on_row_type(arg table_provides_type[]) RETURNS void AS $$ BEGIN; SELECT NULL; END; $$ LANGUAGE SQL;") + + defer testhelper.AssertQueryRuns(backupConn, "DROP FUNCTION func_depends_on_row_type(arg table_provides_type[]);") + + timestamp := gpbackup(gpbackupPath, backupHelperPath) + gprestore(gprestorePath, restoreHelperPath, timestamp, + "--redirect-db", "restoredb") + + assertRelationsCreated(restoreConn, TOTAL_RELATIONS+1) // for 1 new table + assertDataRestored(restoreConn, schema2TupleCounts) + assertDataRestored(restoreConn, map[string]int{ + "public.foo": 40000, + "public.holds": 50000, + "public.sales": 13, + "public.table_provides_type": 1}) + + assertArtifactsCleaned(restoreConn, timestamp) + }) It("Can restore xml with xmloption set to document", func() { testutils.SkipIfBefore6(backupConn) // Set up the XML table that contains XML content