| package toc_test |
| |
| import ( |
| "bytes" |
| "testing" |
| |
| "github.com/apache/cloudberry-backup/testutils" |
| "github.com/apache/cloudberry-backup/toc" |
| |
| . "github.com/onsi/ginkgo/v2" |
| . "github.com/onsi/gomega" |
| . "github.com/onsi/gomega/gbytes" |
| ) |
| |
| var ( |
| buffer *Buffer |
| tocfile *toc.TOC |
| ) |
| |
| func TestTOC(t *testing.T) { |
| RegisterFailHandler(Fail) |
| RunSpecs(t, "TOC Suite") |
| } |
| |
| var _ = Describe("utils/toc tests", func() { |
| table1 := toc.StatementWithType{Schema: "schema", Name: "table1", ObjectType: toc.OBJ_TABLE, Statement: "CREATE TABLE schema.table1", Tier: []uint32{0, 0}} |
| table1Len := uint64(len(table1.Statement)) |
| |
| capsTable := toc.StatementWithType{Schema: "schema", Name: "TABLE_CAPS", ObjectType: toc.OBJ_TABLE, Statement: "CREATE TABLE schema.TABLE_CAPS", Tier: []uint32{0, 0}} |
| capsTableLen := uint64(len(capsTable.Statement)) |
| |
| table2 := toc.StatementWithType{Schema: "schema2", Name: "table2", ObjectType: toc.OBJ_TABLE, Statement: "CREATE TABLE schema2.table2", Tier: []uint32{0, 0}} |
| table2Len := uint64(len(table2.Statement)) |
| |
| view := toc.StatementWithType{Schema: "schema", Name: "view", ObjectType: toc.OBJ_VIEW, Statement: "CREATE VIEW schema.view", Tier: []uint32{0, 0}} |
| viewLen := uint64(len(view.Statement)) |
| |
| matView := toc.StatementWithType{Schema: "schema", Name: "matView", ObjectType: toc.OBJ_MATERIALIZED_VIEW, Statement: "CREATE MATERIALIZED VIEW schema.mat_view", Tier: []uint32{0, 0}} |
| matViewLen := uint64(len(matView.Statement)) |
| |
| sequence := toc.StatementWithType{Schema: "schema", Name: "sequence", ObjectType: toc.OBJ_SEQUENCE, Statement: "CREATE SEQUENCE schema.sequence START 100", Tier: []uint32{0, 0}} |
| sequenceLen := uint64(len(sequence.Statement)) |
| |
| index := toc.StatementWithType{Schema: "schema2", Name: "someindex", ObjectType: toc.OBJ_INDEX, Statement: "CREATE INDEX someindex ON schema2.table2(i)", ReferenceObject: "schema2.table2", Tier: []uint32{0, 0}} |
| indexLen := uint64(len(index.Statement)) |
| |
| BeforeEach(func() { |
| tocfile, _ = testutils.InitializeTestTOC(buffer, "predata") |
| }) |
| Describe("GetSQLStatementForObjectTypes", func() { |
| // Dummy variables to help clarify which arguments are non-empty in a given test |
| var noInObj, noExObj, noInSchema, noExSchema, noInRelation, noExRelation []string |
| var metadataFile *bytes.Reader |
| BeforeEach(func() { |
| startCount := uint64(0) |
| endCount := table1Len |
| tocfile.AddMetadataEntry("predata", toc.MetadataEntry{Schema: "schema", Name: "table1", ObjectType: toc.OBJ_TABLE}, startCount, endCount, []uint32{0, 0}) |
| startCount = endCount |
| endCount += capsTableLen |
| tocfile.AddMetadataEntry("predata", toc.MetadataEntry{Schema: "schema", Name: "TABLE_CAPS", ObjectType: toc.OBJ_TABLE}, startCount, endCount, []uint32{0, 0}) |
| startCount = endCount |
| endCount += table2Len |
| tocfile.AddMetadataEntry("predata", toc.MetadataEntry{Schema: "schema2", Name: "table2", ObjectType: toc.OBJ_TABLE}, startCount, endCount, []uint32{0, 0}) |
| startCount = endCount |
| endCount += viewLen |
| tocfile.AddMetadataEntry("predata", toc.MetadataEntry{Schema: "schema", Name: "view", ObjectType: toc.OBJ_VIEW}, startCount, endCount, []uint32{0, 0}) |
| startCount = endCount |
| endCount += matViewLen |
| tocfile.AddMetadataEntry("predata", toc.MetadataEntry{Schema: "schema", Name: "matView", ObjectType: toc.OBJ_MATERIALIZED_VIEW}, startCount, endCount, []uint32{0, 0}) |
| startCount = endCount |
| endCount += sequenceLen |
| tocfile.AddMetadataEntry("predata", toc.MetadataEntry{Schema: "schema", Name: "sequence", ObjectType: toc.OBJ_SEQUENCE}, startCount, endCount, []uint32{0, 0}) |
| startCount = endCount |
| endCount += indexLen |
| tocfile.AddMetadataEntry("predata", toc.MetadataEntry{Schema: "schema2", Name: "someindex", ObjectType: toc.OBJ_INDEX, ReferenceObject: "schema2.table2"}, startCount, endCount, []uint32{0, 0}) |
| |
| metadataFile = bytes.NewReader([]byte(table1.Statement + capsTable.Statement + table2.Statement + view.Statement + matView.Statement + sequence.Statement + index.Statement)) |
| }) |
| It("returns statement for a single object type", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, []string{toc.OBJ_VIEW}, noExObj, noInSchema, noExSchema, noInRelation, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{view})) |
| }) |
| It("returns statement for multiple object types", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, []string{toc.OBJ_TABLE, toc.OBJ_VIEW}, noExObj, noInSchema, noExSchema, noInRelation, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{table1, capsTable, table2, view})) |
| }) |
| It("does not return a statement type listed in the exclude list", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, []string{toc.OBJ_TABLE}, noInSchema, noExSchema, noInRelation, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{view, matView, sequence, index})) |
| }) |
| It("returns empty statement when no object types are found", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, []string{toc.OBJ_FUNCTION}, noExObj, noInSchema, noExSchema, noInRelation, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{})) |
| }) |
| It("returns statement for a single object type with matching schema", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, []string{toc.OBJ_TABLE}, noExObj, []string{"schema2"}, noExSchema, noInRelation, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{table2})) |
| }) |
| It("returns statement for any object type in the include schema", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, []string{"schema"}, noExSchema, noInRelation, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{table1, capsTable, view, matView, sequence})) |
| }) |
| It("returns statement for any object type not in the exclude schema", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, []string{"schema2"}, noInRelation, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{table1, capsTable, view, matView, sequence})) |
| }) |
| It("returns statement for a table matching an included table", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, []string{"schema.table1"}, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{table1})) |
| }) |
| It("returns statement for a table matching an included table in caps", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, []string{"schema.TABLE_CAPS"}, noExRelation) |
| |
| tableCaps := toc.StatementWithType{Schema: "schema", Name: "TABLE_CAPS", ObjectType: toc.OBJ_TABLE, Statement: "CREATE TABLE schema.TABLE_CAPS", Tier: []uint32{0, 0}} |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{tableCaps})) |
| }) |
| It("returns statement for a view matching an included view", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, []string{"schema.view"}, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{view})) |
| }) |
| It("returns statement for a materialized view matching an included materialized view", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, []string{"schema.matView"}, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{matView})) |
| }) |
| It("returns no statements for an excluded view, materialized view or sequence", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, noInRelation, []string{"schema.view", "schema.matView", "schema.sequence"}) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{table1, capsTable, table2, index})) |
| }) |
| It("returns no statements for a non-relation object with matching name from relation list", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, []string{"schema.someindex"}, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{})) |
| }) |
| |
| Context("With reference object", func() { |
| sequenceTable := toc.StatementWithType{Schema: "schema", Name: "sequence_table", ObjectType: toc.OBJ_TABLE, |
| Statement: "CREATE TABLE schema.sequence_table", Tier: []uint32{0, 0}} |
| sequenceTableLen := uint64(len(sequenceTable.Statement)) |
| |
| sequenceOwner := toc.StatementWithType{Schema: "schema", Name: "sequence", ObjectType: toc.OBJ_SEQUENCE_OWNER, |
| Statement: "ALTER SEQUENCE schema.sequence OWNED BY schema.sequence_table", ReferenceObject: "schema.sequence_table", Tier: []uint32{0, 0}} |
| sequenceOwnerLen := uint64(len(sequenceOwner.Statement)) |
| |
| BeforeEach(func() { |
| startCount := table1Len + capsTableLen + table2Len + viewLen + matViewLen + sequenceLen + indexLen |
| endCount := startCount + sequenceTableLen |
| tocfile.AddMetadataEntry("predata", toc.MetadataEntry{Schema: "schema", Name: "sequence_table", ObjectType: toc.OBJ_TABLE}, startCount, endCount, []uint32{0, 0}) |
| startCount = endCount |
| endCount += sequenceOwnerLen |
| tocfile.AddMetadataEntry("predata", toc.MetadataEntry{Schema: "schema", Name: "sequence", ObjectType: toc.OBJ_SEQUENCE_OWNER, ReferenceObject: "schema.sequence_table"}, startCount, endCount, []uint32{0, 0}) |
| |
| metadataFile = bytes.NewReader([]byte(table1.Statement + capsTable.Statement + table2.Statement + view.Statement + matView.Statement + sequence.Statement + index.Statement + sequenceTable.Statement + sequenceOwner.Statement)) |
| }) |
| It("does not return sequence owner statement when owning table is not included", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, []string{"schema.sequence"}, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{sequence})) |
| }) |
| It("does not return sequence owner statement when sequence is not included", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, []string{"schema.sequence_table"}, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{sequenceTable})) |
| }) |
| It("returns sequence owner statement when owning table and sequence are both included", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, []string{"schema.sequence_table", "schema.sequence"}, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{sequence, sequenceTable, sequenceOwner})) |
| }) |
| It("returns statement for non sequence-owners with matching reference object", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, []string{"schema2.table2"}, noExRelation) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{table2, index})) |
| }) |
| It("returns statement for any object type or reference object not matching an excluded table", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, noInRelation, []string{"schema.table1"}) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{capsTable, table2, view, matView, sequence, index, sequenceTable, sequenceOwner})) |
| }) |
| It("returns no statements for any object type with reference object matching an excluded table", func() { |
| statements := tocfile.GetSQLStatementForObjectTypes("predata", metadataFile, noInObj, noExObj, noInSchema, noExSchema, noInRelation, []string{"schema2.table2"}) |
| |
| Expect(statements).To(Equal([]toc.StatementWithType{table1, capsTable, view, matView, sequence, sequenceTable, sequenceOwner})) |
| }) |
| }) |
| }) |
| Describe("GetDataEntriesMatching", func() { |
| BeforeEach(func() { |
| tocfile.AddCoordinatorDataEntry("schema1", "table1", 1, "(i)", 0, "", "", false) |
| tocfile.AddCoordinatorDataEntry("schema2", "table2", 1, "(i)", 0, "", "", false) |
| tocfile.AddCoordinatorDataEntry("schema3", "table3", 1, "(i)", 0, "", "", false) |
| tocfile.AddCoordinatorDataEntry("schema3", "table3_partition1", 1, "(i)", 0, "table3", "", false) |
| tocfile.AddCoordinatorDataEntry("schema3", "table3_partition2", 1, "(i)", 0, "table3", "", false) |
| }) |
| Context("Non-empty restore plan", func() { |
| restorePlanTableFQNs := []string{"schema1.table1", "schema2.table2", "schema3.table3", "schema3.table3_partition1", "schema3.table3_partition2"} |
| |
| It("returns matching entry on include schema", func() { |
| includeSchemas := []string{"schema1"} |
| |
| matchingEntries := tocfile.GetDataEntriesMatching(includeSchemas, []string{}, |
| []string{}, []string{}, restorePlanTableFQNs) |
| |
| Expect(matchingEntries).To(Equal( |
| []toc.CoordinatorDataEntry{ |
| {Schema: "schema1", Name: "table1", Oid: 1, AttributeString: "(i)"}, |
| }, |
| )) |
| }) |
| It("returns matching entry on exclude schema", func() { |
| excludeSchemas := []string{"schema2"} |
| |
| matchingEntries := tocfile.GetDataEntriesMatching([]string{}, excludeSchemas, |
| []string{}, []string{}, restorePlanTableFQNs) |
| |
| Expect(matchingEntries).To(ConsistOf( |
| []toc.CoordinatorDataEntry{ |
| {Schema: "schema1", Name: "table1", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema3", Name: "table3", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema3", Name: "table3_partition1", Oid: 1, AttributeString: "(i)", PartitionRoot: "table3"}, |
| {Schema: "schema3", Name: "table3_partition2", Oid: 1, AttributeString: "(i)", PartitionRoot: "table3"}, |
| }, |
| )) |
| }) |
| It("returns matching entry on include table", func() { |
| includeTables := []string{"schema1.table1"} |
| |
| matchingEntries := tocfile.GetDataEntriesMatching([]string{}, []string{}, |
| includeTables, []string{}, restorePlanTableFQNs) |
| |
| Expect(matchingEntries).To(Equal( |
| []toc.CoordinatorDataEntry{ |
| {Schema: "schema1", Name: "table1", Oid: 1, AttributeString: "(i)"}, |
| }, |
| )) |
| }) |
| It("returns matching entry on exclude table", func() { |
| excludeTables := []string{"schema2.table2"} |
| |
| matchingEntries := tocfile.GetDataEntriesMatching([]string{}, []string{}, |
| []string{}, excludeTables, restorePlanTableFQNs) |
| |
| Expect(matchingEntries).To(ConsistOf( |
| []toc.CoordinatorDataEntry{ |
| {Schema: "schema1", Name: "table1", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema3", Name: "table3", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema3", Name: "table3_partition1", Oid: 1, AttributeString: "(i)", PartitionRoot: "table3"}, |
| {Schema: "schema3", Name: "table3_partition2", Oid: 1, AttributeString: "(i)", PartitionRoot: "table3"}, |
| }, |
| )) |
| }) |
| It("returns all entries when not schema-filtered or table-filtered", func() { |
| matchingEntries := tocfile.GetDataEntriesMatching([]string{}, []string{}, |
| []string{}, []string{}, restorePlanTableFQNs) |
| |
| Expect(matchingEntries).To(ConsistOf( |
| []toc.CoordinatorDataEntry{ |
| {Schema: "schema1", Name: "table1", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema2", Name: "table2", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema3", Name: "table3", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema3", Name: "table3_partition1", Oid: 1, AttributeString: "(i)", PartitionRoot: "table3"}, |
| {Schema: "schema3", Name: "table3_partition2", Oid: 1, AttributeString: "(i)", PartitionRoot: "table3"}, |
| }, |
| )) |
| }) |
| It("returns matching entry and its leaf partitions on include table", func() { |
| includeTables := []string{"schema3.table3"} |
| |
| matchingEntries := tocfile.GetDataEntriesMatching([]string{}, []string{}, |
| includeTables, []string{}, restorePlanTableFQNs) |
| |
| Expect(matchingEntries).To(ConsistOf( |
| []toc.CoordinatorDataEntry{ |
| {Schema: "schema3", Name: "table3", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema3", Name: "table3_partition1", Oid: 1, AttributeString: "(i)", PartitionRoot: "table3"}, |
| {Schema: "schema3", Name: "table3_partition2", Oid: 1, AttributeString: "(i)", PartitionRoot: "table3"}, |
| }, |
| )) |
| }) |
| It("does not return leaf partitions in include tables but not in the restore plan", func() { |
| includeTables := []string{"schema3.table3"} |
| customRestorePlanTableFQNs := []string{"schema1.table1", "schema2.table2", "schema3.table3", "schema3.table3_partition1"} |
| |
| matchingEntries := tocfile.GetDataEntriesMatching([]string{}, []string{}, |
| includeTables, []string{}, customRestorePlanTableFQNs) |
| |
| Expect(matchingEntries).To(ConsistOf( |
| []toc.CoordinatorDataEntry{ |
| {Schema: "schema3", Name: "table3", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema3", Name: "table3_partition1", Oid: 1, AttributeString: "(i)", PartitionRoot: "table3"}, |
| }, |
| )) |
| }) |
| It("returns matching entry on exclude root partition table", func() { |
| excludeTables := []string{"schema3.table3"} |
| |
| matchingEntries := tocfile.GetDataEntriesMatching([]string{}, []string{}, |
| []string{}, excludeTables, restorePlanTableFQNs) |
| |
| Expect(matchingEntries).To(Equal( |
| []toc.CoordinatorDataEntry{ |
| {Schema: "schema1", Name: "table1", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema2", Name: "table2", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| }, |
| )) |
| }) |
| It("returns matching entry on exclude leaf partition table", func() { |
| excludeTables := []string{"schema3.table3_partition2"} |
| |
| matchingEntries := tocfile.GetDataEntriesMatching([]string{}, []string{}, |
| []string{}, excludeTables, restorePlanTableFQNs) |
| |
| Expect(matchingEntries).To(Equal( |
| []toc.CoordinatorDataEntry{ |
| {Schema: "schema1", Name: "table1", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema2", Name: "table2", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema3", Name: "table3", Oid: 1, AttributeString: "(i)", PartitionRoot: ""}, |
| {Schema: "schema3", Name: "table3_partition1", Oid: 1, AttributeString: "(i)", PartitionRoot: "table3"}, |
| }, |
| )) |
| }) |
| }) |
| |
| Context("Empty restore plan", func() { |
| restorePlanTableFQNs := make([]string, 0) |
| |
| Specify("That there are no matching entries", func() { |
| matchingEntries := tocfile.GetDataEntriesMatching([]string{}, []string{}, |
| []string{}, []string{}, restorePlanTableFQNs) |
| |
| Expect(matchingEntries).To(BeEmpty()) |
| }) |
| }) |
| }) |
| Describe("SubstituteRedirectDatabaseInStatements", func() { |
| create := toc.StatementWithType{Schema: "", Name: "somedatabase", ObjectType: toc.OBJ_DATABASE, Statement: "CREATE DATABASE somedatabase TEMPLATE template0;\n"} |
| wrongCreate := toc.StatementWithType{ObjectType: toc.OBJ_TABLE, Statement: "CREATE DATABASE somedatabase;\n"} |
| encoding := toc.StatementWithType{ObjectType: toc.OBJ_DATABASE, Statement: "CREATE DATABASE somedatabase TEMPLATE template0 ENCODING 'UTF8';\n"} |
| gucs := toc.StatementWithType{ObjectType: toc.OBJ_DATABASE_GUC, Statement: "ALTER DATABASE somedatabase SET fsync TO off;\n"} |
| metadata := toc.StatementWithType{ObjectType: toc.OBJ_DATABASE_METADATA, Statement: "ALTER DATABASE somedatabase OWNER TO testrole;\n\nREVOKE ALL ON DATABASE somedatabase FROM public;\nGRANT ALL ON DATABASE somedatabase TO gpadmin;"} |
| oldSpecial := toc.StatementWithType{ObjectType: toc.OBJ_DATABASE, Statement: `CREATE DATABASE "db-special-chär$" TEMPLATE template0 TABLESPACE test_tablespace; |
| |
| COMMENT ON DATABASE "db-special-chär$" IS 'this is a database comment';`} |
| It("can substitute a database name in a CREATE DATABASE statement", func() { |
| statements := toc.SubstituteRedirectDatabaseInStatements([]toc.StatementWithType{create}, "somedatabase", "newdatabase") |
| Expect(statements[0].Statement).To(Equal("CREATE DATABASE newdatabase TEMPLATE template0;\n")) |
| }) |
| It("can substitute a database name in a CREATE DATABASE statement with encoding", func() { |
| statements := toc.SubstituteRedirectDatabaseInStatements([]toc.StatementWithType{encoding}, "somedatabase", "newdatabase") |
| Expect(statements[0].Statement).To(Equal("CREATE DATABASE newdatabase TEMPLATE template0 ENCODING 'UTF8';\n")) |
| }) |
| It("can substitute a database name in an ALTER DATABASE OWNER statement", func() { |
| statements := toc.SubstituteRedirectDatabaseInStatements([]toc.StatementWithType{metadata}, "somedatabase", "newdatabase") |
| Expect(statements[0].Statement).To(Equal("ALTER DATABASE newdatabase OWNER TO testrole;\n\nREVOKE ALL ON DATABASE newdatabase FROM public;\nGRANT ALL ON DATABASE newdatabase TO gpadmin;")) |
| }) |
| It("can substitute a database name in a database GUC statement", func() { |
| statements := toc.SubstituteRedirectDatabaseInStatements([]toc.StatementWithType{gucs}, "somedatabase", "newdatabase") |
| Expect(statements[0].Statement).To(Equal("ALTER DATABASE newdatabase SET fsync TO off;\n")) |
| }) |
| It("can substitute a database name in a database GUC statement", func() { |
| statements := toc.SubstituteRedirectDatabaseInStatements([]toc.StatementWithType{gucs}, "somedatabase", "newdatabase") |
| Expect(statements[0].Statement).To(Equal("ALTER DATABASE newdatabase SET fsync TO off;\n")) |
| }) |
| It("doesn't modify a statement of the wrong type", func() { |
| statements := toc.SubstituteRedirectDatabaseInStatements([]toc.StatementWithType{wrongCreate}, "somedatabase", "newdatabase") |
| Expect(statements[0].Statement).To(Equal("CREATE DATABASE somedatabase;\n")) |
| }) |
| It("can substitute a database name if the old name contained special characters with a tablespace and comment", func() { |
| statements := toc.SubstituteRedirectDatabaseInStatements([]toc.StatementWithType{oldSpecial}, `"db-special-chär$"`, "newdatabase") |
| Expect(statements[0].Statement).To(Equal("CREATE DATABASE newdatabase TEMPLATE template0 TABLESPACE test_tablespace;\n\nCOMMENT ON DATABASE newdatabase IS 'this is a database comment';")) |
| }) |
| It("can substitute a database name if the new name contained special characters", func() { |
| statements := toc.SubstituteRedirectDatabaseInStatements([]toc.StatementWithType{create}, "somedatabase", `"db-special-chär$"`) |
| Expect(statements[0].Statement).To(Equal(`CREATE DATABASE "db-special-chär$" TEMPLATE template0; |
| `)) |
| }) |
| }) |
| Describe("RemoveActiveRoles", func() { |
| user1 := toc.StatementWithType{Name: "user1", ObjectType: toc.OBJ_ROLE, Statement: "CREATE ROLE user1 SUPERUSER;\n"} |
| user2 := toc.StatementWithType{Name: "user2", ObjectType: toc.OBJ_ROLE, Statement: "CREATE ROLE user2;\n"} |
| It("Removes current user from the list of roles to restore", func() { |
| resultStatements := toc.RemoveActiveRole("user1", []toc.StatementWithType{user1, user2}) |
| |
| Expect(resultStatements).To(Equal([]toc.StatementWithType{user2})) |
| }) |
| It("Returns the same list if current user is not in it", func() { |
| resultStatements := toc.RemoveActiveRole("user3", []toc.StatementWithType{user1, user2}) |
| |
| Expect(resultStatements).To(Equal([]toc.StatementWithType{user1, user2})) |
| }) |
| }) |
| Describe("GetIncludedPartitionRoots", func() { |
| It("does not return anything if relations are not leaf partitions", func() { |
| tocfile.AddCoordinatorDataEntry("schema0", "name0", 0, "attribute0", 1, "", "", false) |
| tocfile.AddCoordinatorDataEntry("schema1", "name1", 1, "attribute0", 1, "", "", false) |
| roots := toc.GetIncludedPartitionRoots(tocfile.DataEntries, []string{"schema0.name0", "schema1.name1"}) |
| Expect(roots).To(BeEmpty()) |
| }) |
| It("returns root parition of leaf partitions", func() { |
| tocfile.AddCoordinatorDataEntry("schema0", "name0", 2, "attribute0", 1, "root0", "", false) |
| tocfile.AddCoordinatorDataEntry("schema1", "name1", 3, "attribute0", 1, "root1", "", false) |
| roots := toc.GetIncludedPartitionRoots(tocfile.DataEntries, []string{"schema0.name0", "schema1.name1"}) |
| Expect(roots).To(ConsistOf("schema0.root0", "schema1.root1")) |
| }) |
| It("only returns root partitions of leaf partitions", func() { |
| tocfile.AddCoordinatorDataEntry("schema0", "name0", 0, "attribute0", 1, "", "", false) |
| tocfile.AddCoordinatorDataEntry("schema1", "name1", 1, "attribute0", 1, "", "", false) |
| tocfile.AddCoordinatorDataEntry("schema2", "name2", 2, "attribute0", 1, "root2", "", false) |
| tocfile.AddCoordinatorDataEntry("schema3", "name3", 3, "attribute0", 1, "root3", "", false) |
| roots := toc.GetIncludedPartitionRoots(tocfile.DataEntries, []string{"schema2.name2", "schema3.name3"}) |
| Expect(roots).To(ConsistOf("schema2.root2", "schema3.root3")) |
| }) |
| It("returns nothing if toc data entries are empty", func() { |
| roots := toc.GetIncludedPartitionRoots(tocfile.DataEntries, []string{"schema2.name2", "schema3.name3"}) |
| Expect(roots).To(BeEmpty()) |
| }) |
| It("returns nothing if relation is not part of TOC data entries", func() { |
| tocfile.AddCoordinatorDataEntry("schema0", "name0", 0, "attribute0", 1, "", "", false) |
| tocfile.AddCoordinatorDataEntry("schema1", "name1", 1, "attribute0", 1, "", "", false) |
| tocfile.AddCoordinatorDataEntry("schema2", "name2", 2, "attribute0", 1, "root2", "", false) |
| tocfile.AddCoordinatorDataEntry("schema3", "name3", 3, "attribute0", 1, "root3", "", false) |
| roots := toc.GetIncludedPartitionRoots(tocfile.DataEntries, []string{"schema4.name4", "schema5.name5"}) |
| Expect(roots).To(BeEmpty()) |
| }) |
| It("returns empty if no relations are passed in", func() { |
| tocfile.AddCoordinatorDataEntry("schema0", "name0", 0, "attribute0", 1, "", "", false) |
| tocfile.AddCoordinatorDataEntry("schema1", "name1", 1, "attribute0", 1, "", "", false) |
| tocfile.AddCoordinatorDataEntry("schema2", "name2", 2, "attribute0", 1, "root2", "", false) |
| tocfile.AddCoordinatorDataEntry("schema3", "name3", 3, "attribute0", 1, "root3", "", false) |
| roots := toc.GetIncludedPartitionRoots(tocfile.DataEntries, []string{}) |
| Expect(roots).To(BeEmpty()) |
| }) |
| }) |
| }) |