| package integration |
| |
| import ( |
| "fmt" |
| "regexp" |
| |
| "github.com/apache/cloudberry-backup/backup" |
| "github.com/apache/cloudberry-backup/testutils" |
| "github.com/apache/cloudberry-backup/toc" |
| "github.com/apache/cloudberry-go-libs/structmatcher" |
| "github.com/apache/cloudberry-go-libs/testhelper" |
| |
| . "github.com/onsi/ginkgo/v2" |
| . "github.com/onsi/gomega" |
| . "github.com/onsi/gomega/gbytes" |
| ) |
| |
| var _ = Describe("backup integration create statement tests", func() { |
| BeforeEach(func() { |
| tocfile, backupfile = testutils.InitializeTestTOC(buffer, "predata") |
| }) |
| Describe("PrintCreateDatabaseStatement", func() { |
| emptyDB := backup.Database{} |
| emptyMetadataMap := backup.MetadataMap{} |
| BeforeEach(func() { |
| connectionPool.DBName = "create_test_db" |
| }) |
| AfterEach(func() { |
| connectionPool.DBName = "testdb" |
| }) |
| It("creates a basic database", func() { |
| db := backup.Database{Oid: 1, Name: "create_test_db", Tablespace: "pg_default", Encoding: "UTF8"} |
| backup.PrintCreateDatabaseStatement(backupfile, tocfile, emptyDB, db, emptyMetadataMap) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, "DROP DATABASE create_test_db") |
| |
| resultDB := backup.GetDatabaseInfo(connectionPool) |
| structmatcher.ExpectStructsToMatchExcluding(&db, &resultDB, "Oid", "Collate", "CType") |
| }) |
| It("creates a database with properties the same as defaults", func() { |
| defaultDB := backup.GetDefaultDatabaseEncodingInfo(connectionPool) |
| var db backup.Database |
| db = backup.Database{Oid: 1, Name: "create_test_db", Tablespace: "pg_default", Encoding: "UTF8", Collate: defaultDB.Collate, CType: defaultDB.Collate} |
| |
| backup.PrintCreateDatabaseStatement(backupfile, tocfile, defaultDB, db, emptyMetadataMap) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, "DROP DATABASE create_test_db") |
| |
| resultDB := backup.GetDatabaseInfo(connectionPool) |
| structmatcher.ExpectStructsToMatchExcluding(&db, &resultDB, "Oid") |
| }) |
| It("creates a database with all properties", func() { |
| var db backup.Database |
| if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("6") { |
| db = backup.Database{Oid: 1, Name: "create_test_db", Tablespace: "pg_default", Encoding: "UTF8", Collate: "", CType: ""} |
| } else { |
| db = backup.Database{Oid: 1, Name: "create_test_db", Tablespace: "pg_default", Encoding: "UTF8", Collate: "en_US.utf-8", CType: "en_US.utf-8"} |
| } |
| |
| backup.PrintCreateDatabaseStatement(backupfile, tocfile, emptyDB, db, emptyMetadataMap) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, "DROP DATABASE create_test_db") |
| |
| resultDB := backup.GetDatabaseInfo(connectionPool) |
| structmatcher.ExpectStructsToMatchExcluding(&db, &resultDB, "Oid") |
| }) |
| }) |
| Describe("PrintDatabaseGUCs", func() { |
| It("creates database GUCs with correct quoting", func() { |
| enableNestLoopGUC := "SET enable_nestloop TO 'true'" |
| searchPathGUC := "SET search_path TO pg_catalog, public" |
| defaultStorageGUC := "SET gp_default_storage_options TO 'appendonly=true, compresslevel=6, orientation=row, compresstype=none'" |
| if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB() { |
| defaultStorageGUC = "SET gp_default_storage_options TO 'compresslevel=6, compresstype=none'" |
| } |
| |
| gucs := []string{enableNestLoopGUC, searchPathGUC, defaultStorageGUC} |
| |
| backup.PrintDatabaseGUCs(backupfile, tocfile, gucs, "testdb") |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb RESET enable_nestloop") |
| defer testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb RESET search_path") |
| defer testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb RESET gp_default_storage_options") |
| resultGUCs := backup.GetDatabaseGUCs(connectionPool) |
| Expect(resultGUCs).To(Equal(gucs)) |
| }) |
| }) |
| Describe("PrintCreateResourceQueueStatements", func() { |
| It("creates a basic resource queue with a comment", func() { |
| basicQueue := backup.ResourceQueue{Oid: 1, Name: `"basicQueue"`, ActiveStatements: -1, MaxCost: "32.80", CostOvercommit: false, MinCost: "0.00", Priority: "medium", MemoryLimit: "-1"} |
| resQueueMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_RESOURCE_QUEUE, false, false, true, false) |
| resQueueMetadata := resQueueMetadataMap[basicQueue.GetUniqueID()] |
| |
| backup.PrintCreateResourceQueueStatements(backupfile, tocfile, []backup.ResourceQueue{basicQueue}, resQueueMetadataMap) |
| |
| // CREATE RESOURCE QUEUE statements can not be part of a multi-command statement, so |
| // feed the CREATE RESOURCE QUEUE and COMMENT ON statements separately. |
| hunks := regexp.MustCompile(";\n\n").Split(buffer.String(), 2) |
| testhelper.AssertQueryRuns(connectionPool, hunks[0]) |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE QUEUE "basicQueue"`) |
| testhelper.AssertQueryRuns(connectionPool, hunks[1]) |
| |
| resultResourceQueues := backup.GetResourceQueues(connectionPool) |
| resQueueUniqueID := testutils.UniqueIDFromObjectName(connectionPool, "", "basicQueue", backup.TYPE_RESOURCE_QUEUE) |
| resultMetadataMap := backup.GetCommentsForObjectType(connectionPool, backup.TYPE_RESOURCE_QUEUE) |
| resultMetadata := resultMetadataMap[resQueueUniqueID] |
| structmatcher.ExpectStructsToMatch(&resultMetadata, &resQueueMetadata) |
| |
| for _, resultQueue := range resultResourceQueues { |
| if resultQueue.Name == `"basicQueue"` { |
| structmatcher.ExpectStructsToMatchExcluding(&basicQueue, &resultQueue, "Oid") |
| return |
| } |
| } |
| }) |
| It("creates a resource queue with all attributes", func() { |
| everythingQueue := backup.ResourceQueue{Oid: 1, Name: `"everythingQueue"`, ActiveStatements: 7, MaxCost: "32.80", CostOvercommit: true, MinCost: "22.80", Priority: "low", MemoryLimit: "2GB"} |
| emptyMetadataMap := map[backup.UniqueID]backup.ObjectMetadata{} |
| |
| backup.PrintCreateResourceQueueStatements(backupfile, tocfile, []backup.ResourceQueue{everythingQueue}, emptyMetadataMap) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE QUEUE "everythingQueue"`) |
| |
| resultResourceQueues := backup.GetResourceQueues(connectionPool) |
| |
| for _, resultQueue := range resultResourceQueues { |
| if resultQueue.Name == `"everythingQueue"` { |
| structmatcher.ExpectStructsToMatchExcluding(&everythingQueue, &resultQueue, "Oid") |
| return |
| } |
| } |
| Fail("Could not find everythingQueue") |
| }) |
| }) |
| Describe("PrintCreateResourceGroupStatements", func() { |
| It("creates a basic resource group", func() { |
| emptyMetadataMap := map[backup.UniqueID]backup.ObjectMetadata{} |
| |
| if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("7") { |
| someGroup := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "some_group", Concurrency: "15", Cpuset: "-1"}, CPURateLimit: "10", MemoryLimit: "20", MemorySharedQuota: "25", MemorySpillRatio: "30", MemoryAuditor: "0"} |
| backup.PrintCreateResourceGroupStatementsBefore7(backupfile, tocfile, []backup.ResourceGroupBefore7{someGroup}, emptyMetadataMap) |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE GROUP some_group`) |
| |
| resultResourceGroups := backup.GetResourceGroups[backup.ResourceGroupBefore7](connectionPool) |
| for _, resultGroup := range resultResourceGroups { |
| if resultGroup.Name == "some_group" { |
| structmatcher.ExpectStructsToMatchExcluding(&someGroup, &resultGroup, "ResourceGroup.Oid") |
| return |
| } |
| } |
| } else { // GPDB7+ |
| someGroup := backup.ResourceGroupAtLeast7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "some_group", Concurrency: "15", Cpuset: "-1"}, CpuMaxPercent: "10", CpuWeight: "100"} |
| backup.PrintCreateResourceGroupStatementsAtLeast7(backupfile, tocfile, []backup.ResourceGroupAtLeast7{someGroup}, emptyMetadataMap) |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE GROUP some_group`) |
| |
| resultResourceGroups := backup.GetResourceGroups[backup.ResourceGroupAtLeast7](connectionPool) |
| for _, resultGroup := range resultResourceGroups { |
| if resultGroup.Name == "some_group" { |
| structmatcher.ExpectStructsToMatchExcluding(&someGroup, &resultGroup, "ResourceGroup.Oid") |
| return |
| } |
| } |
| } |
| Fail("Could not find some_group") |
| }) |
| It("creates a resource group with defaults", func() { |
| if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("7") { |
| expectedDefaults := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "some_group", Concurrency: concurrencyDefault, Cpuset: cpuSetDefault}, CPURateLimit: "10", MemoryLimit: "20", |
| MemorySharedQuota: memSharedDefault, MemorySpillRatio: memSpillDefault, MemoryAuditor: memAuditDefault} |
| |
| testhelper.AssertQueryRuns(connectionPool, "CREATE RESOURCE GROUP some_group WITH (CPU_RATE_LIMIT=10, MEMORY_LIMIT=20);") |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE GROUP some_group`) |
| |
| resultResourceGroups := backup.GetResourceGroups[backup.ResourceGroupBefore7](connectionPool) |
| |
| for _, resultGroup := range resultResourceGroups { |
| if resultGroup.Name == "some_group" { |
| structmatcher.ExpectStructsToMatchExcluding(&expectedDefaults, &resultGroup, "ResourceGroup.Oid") |
| return |
| } |
| } |
| } else { // GPDB7+ |
| expectedDefaults := backup.ResourceGroupAtLeast7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "some_group", Concurrency: concurrencyDefault, Cpuset: cpuSetDefault}, |
| CpuMaxPercent: "10", CpuWeight: "100"} |
| |
| testhelper.AssertQueryRuns(connectionPool, "CREATE RESOURCE GROUP some_group WITH (CPU_MAX_PERCENT=10, CPU_WEIGHT=100);") |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE GROUP some_group`) |
| |
| resultResourceGroups := backup.GetResourceGroups[backup.ResourceGroupAtLeast7](connectionPool) |
| |
| for _, resultGroup := range resultResourceGroups { |
| if resultGroup.Name == "some_group" { |
| structmatcher.ExpectStructsToMatchExcluding(&expectedDefaults, &resultGroup, "ResourceGroup.Oid") |
| return |
| } |
| } |
| } |
| Fail("Could not find some_group") |
| }) |
| It("creates a resource group using old format for MemorySpillRatio", func() { |
| // temporarily special case for 5x resource groups #temp5xResGroup |
| if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("5.20.0") { |
| Skip("Test only applicable to GPDB 5.20 and above") |
| } |
| |
| if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("7") { |
| expectedDefaults := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "some_group", Concurrency: concurrencyDefault, Cpuset: cpuSetDefault}, |
| CPURateLimit: "10", MemoryLimit: "20", MemorySharedQuota: memSharedDefault, MemorySpillRatio: "19", MemoryAuditor: memAuditDefault} |
| |
| testhelper.AssertQueryRuns(connectionPool, "CREATE RESOURCE GROUP some_group WITH (CPU_RATE_LIMIT=10, MEMORY_LIMIT=20, MEMORY_SPILL_RATIO=19);") |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE GROUP some_group`) |
| |
| resultResourceGroups := backup.GetResourceGroups[backup.ResourceGroupBefore7](connectionPool) |
| |
| for _, resultGroup := range resultResourceGroups { |
| if resultGroup.Name == "some_group" { |
| structmatcher.ExpectStructsToMatchExcluding(&expectedDefaults, &resultGroup, "ResourceGroup.Oid") |
| return |
| } |
| } |
| } else { // GPDB7+ |
| expectedDefaults := backup.ResourceGroupAtLeast7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "some_group", Concurrency: concurrencyDefault, Cpuset: cpuSetDefault}, |
| CpuMaxPercent: "10", CpuWeight: "100"} |
| |
| testhelper.AssertQueryRuns(connectionPool, "CREATE RESOURCE GROUP some_group WITH (CPU_MAX_PERCENT=10, CPU_WEIGHT=100);") |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE GROUP some_group`) |
| |
| resultResourceGroups := backup.GetResourceGroups[backup.ResourceGroupAtLeast7](connectionPool) |
| |
| for _, resultGroup := range resultResourceGroups { |
| if resultGroup.Name == "some_group" { |
| structmatcher.ExpectStructsToMatchExcluding(&expectedDefaults, &resultGroup, "ResourceGroup.Oid") |
| return |
| } |
| } |
| } |
| Fail("Could not find some_group") |
| }) |
| It("alters a default resource group", func() { |
| if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("7") { |
| defaultGroup := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "default_group", Concurrency: "15", Cpuset: "-1"}, |
| CPURateLimit: "10", MemoryLimit: "20", MemorySharedQuota: "25", MemorySpillRatio: "30", MemoryAuditor: "0"} |
| emptyMetadataMap := map[backup.UniqueID]backup.ObjectMetadata{} |
| |
| backup.PrintCreateResourceGroupStatementsBefore7(backupfile, tocfile, []backup.ResourceGroupBefore7{defaultGroup}, emptyMetadataMap) |
| |
| hunks := regexp.MustCompile(";\n\n").Split(buffer.String(), 5) |
| for i := 0; i < 5; i++ { |
| testhelper.AssertQueryRuns(connectionPool, hunks[i]) |
| } |
| resultResourceGroups := backup.GetResourceGroups[backup.ResourceGroupBefore7](connectionPool) |
| |
| for _, resultGroup := range resultResourceGroups { |
| if resultGroup.Name == "default_group" { |
| structmatcher.ExpectStructsToMatchExcluding(&defaultGroup, &resultGroup, "ResourceGroup.Oid") |
| return |
| } |
| } |
| } else { |
| defaultGroup := backup.ResourceGroupAtLeast7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "default_group", Concurrency: "15", Cpuset: "-1"}, |
| CpuMaxPercent: "10", CpuWeight: "100"} |
| emptyMetadataMap := map[backup.UniqueID]backup.ObjectMetadata{} |
| |
| backup.PrintCreateResourceGroupStatementsAtLeast7(backupfile, tocfile, []backup.ResourceGroupAtLeast7{defaultGroup}, emptyMetadataMap) |
| |
| hunks := regexp.MustCompile(";\n\n").Split(buffer.String(), 5) |
| for i := 0; i < 3; i++ { |
| testhelper.AssertQueryRuns(connectionPool, hunks[i]) |
| } |
| resultResourceGroups := backup.GetResourceGroups[backup.ResourceGroupAtLeast7](connectionPool) |
| |
| for _, resultGroup := range resultResourceGroups { |
| if resultGroup.Name == "default_group" { |
| structmatcher.ExpectStructsToMatchExcluding(&defaultGroup, &resultGroup, "ResourceGroup.Oid") |
| return |
| } |
| } |
| } |
| Fail("Could not find default_group") |
| }) |
| }) |
| Describe("PrintCreateRoleStatements", func() { |
| var role1 backup.Role |
| BeforeEach(func() { |
| role1 = backup.Role{ |
| Oid: 1, |
| Name: "role1", |
| Super: true, |
| Inherit: false, |
| CreateRole: false, |
| CreateDB: false, |
| CanLogin: false, |
| ConnectionLimit: -1, |
| Password: "", |
| ValidUntil: "", |
| ResQueue: "pg_default", |
| ResGroup: "default_group", |
| Createrexthttp: false, |
| Createrextgpfd: false, |
| Createwextgpfd: false, |
| Createrexthdfs: false, |
| Createwexthdfs: false, |
| TimeConstraints: nil, |
| } |
| }) |
| emptyMetadataMap := backup.MetadataMap{} |
| It("creates a basic role", func() { |
| |
| backup.PrintCreateRoleStatements(backupfile, tocfile, []backup.Role{role1}, emptyMetadataMap) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE "role1"`) |
| role1.Oid = testutils.OidFromObjectName(connectionPool, "", "role1", backup.TYPE_ROLE) |
| |
| resultRoles := backup.GetRoles(connectionPool) |
| for _, role := range resultRoles { |
| if role.Name == "role1" { |
| structmatcher.ExpectStructsToMatch(&role1, role) |
| return |
| } |
| } |
| Fail("Role 'role1' was not found") |
| }) |
| It("creates a role with all attributes", func() { |
| role1 := backup.Role{ |
| Oid: 1, |
| Name: "role1", |
| Super: false, |
| Inherit: true, |
| CreateRole: true, |
| CreateDB: true, |
| CanLogin: true, |
| ConnectionLimit: 4, |
| Password: "md5a8b2c77dfeba4705f29c094592eb3369", |
| ValidUntil: "2099-01-01 08:00:00-00", |
| ResQueue: "pg_default", |
| ResGroup: "", |
| Createrexthttp: true, |
| Createrextgpfd: true, |
| Createwextgpfd: true, |
| Createrexthdfs: true, |
| Createwexthdfs: true, |
| TimeConstraints: []backup.TimeConstraint{ |
| { |
| Oid: 0, |
| StartDay: 0, |
| StartTime: "13:30:00", |
| EndDay: 3, |
| EndTime: "14:30:00", |
| }, { |
| Oid: 0, |
| StartDay: 5, |
| StartTime: "00:00:00", |
| EndDay: 5, |
| EndTime: "24:00:00", |
| }, |
| }, |
| } |
| |
| role1.ResGroup = "default_group" |
| |
| if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("6")) || connectionPool.Version.IsCBDB() { |
| role1.Createrexthdfs = false |
| role1.Createwexthdfs = false |
| } |
| metadataMap := testutils.DefaultMetadataMap(toc.OBJ_ROLE, false, false, true, includeSecurityLabels) |
| |
| backup.PrintCreateRoleStatements(backupfile, tocfile, []backup.Role{role1}, metadataMap) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE "role1"`) |
| role1.Oid = testutils.OidFromObjectName(connectionPool, "", "role1", backup.TYPE_ROLE) |
| |
| resultRoles := backup.GetRoles(connectionPool) |
| for _, role := range resultRoles { |
| if role.Name == "role1" { |
| structmatcher.ExpectStructsToMatchExcluding(&role1, role, "TimeConstraints.Oid") |
| return |
| } |
| } |
| Fail("Role 'role1' was not found") |
| }) |
| It("creates a role with replication", func() { |
| testutils.SkipIfBefore6(connectionPool) |
| |
| role1.Replication = true |
| backup.PrintCreateRoleStatements(backupfile, tocfile, []backup.Role{role1}, emptyMetadataMap) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE "role1"`) |
| role1.Oid = testutils.OidFromObjectName(connectionPool, "", "role1", backup.TYPE_ROLE) |
| |
| resultRoles := backup.GetRoles(connectionPool) |
| for _, role := range resultRoles { |
| if role.Name == "role1" { |
| structmatcher.ExpectStructsToMatch(&role1, role) |
| return |
| } |
| } |
| Fail("Role 'role1' was not found") |
| }) |
| }) |
| Describe("PrintRoleMembershipStatements", func() { |
| BeforeEach(func() { |
| testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE usergroup`) |
| testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE testuser`) |
| }) |
| AfterEach(func() { |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE usergroup`) |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE testuser`) |
| }) |
| It("grants a role without ADMIN OPTION", func() { |
| numRoleMembers := len(backup.GetRoleMembers(connectionPool)) |
| expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: "testrole", IsAdmin: false} |
| backup.PrintRoleMembershipStatements(backupfile, tocfile, []backup.RoleMember{expectedRoleMember}) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| |
| resultRoleMembers := backup.GetRoleMembers(connectionPool) |
| Expect(resultRoleMembers).To(HaveLen(numRoleMembers + 1)) |
| for _, roleMember := range resultRoleMembers { |
| if roleMember.Role == "usergroup" { |
| structmatcher.ExpectStructsToMatch(&expectedRoleMember, &roleMember) |
| return |
| } |
| } |
| Fail("Role 'testuser' is not a member of role 'usergroup'") |
| }) |
| It("grants a role WITH ADMIN OPTION", func() { |
| numRoleMembers := len(backup.GetRoleMembers(connectionPool)) |
| expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: "testrole", IsAdmin: true} |
| backup.PrintRoleMembershipStatements(backupfile, tocfile, []backup.RoleMember{expectedRoleMember}) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| |
| resultRoleMembers := backup.GetRoleMembers(connectionPool) |
| Expect(resultRoleMembers).To(HaveLen(numRoleMembers + 1)) |
| for _, roleMember := range resultRoleMembers { |
| if roleMember.Role == "usergroup" { |
| structmatcher.ExpectStructsToMatch(&expectedRoleMember, &roleMember) |
| return |
| } |
| } |
| Fail("Role 'testuser' is not a member of role 'usergroup'") |
| }) |
| }) |
| Describe("PrintRoleGUCStatements", func() { |
| BeforeEach(func() { |
| testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE testuser`) |
| }) |
| AfterEach(func() { |
| testhelper.AssertQueryRuns(connectionPool, `DROP ROLE testuser`) |
| }) |
| It("Sets GUCs for a particular role", func() { |
| defaultStorageOptionsString := "appendonly=true, compresslevel=6, orientation=row, compresstype=none" |
| if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB() { |
| defaultStorageOptionsString = "compresslevel=6, compresstype=none" |
| } |
| |
| roleConfigMap := map[string][]backup.RoleGUC{ |
| "testuser": { |
| {RoleName: "testuser", Config: fmt.Sprintf("SET gp_default_storage_options TO '%s'", defaultStorageOptionsString)}, |
| {RoleName: "testuser", Config: "SET search_path TO public"}}, |
| } |
| |
| backup.PrintRoleGUCStatements(backupfile, tocfile, roleConfigMap) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| |
| resultGUCs := backup.GetRoleGUCs(connectionPool) |
| Expect(resultGUCs["testuser"]).To(ConsistOf(roleConfigMap["testuser"])) |
| }) |
| It("Sets GUCs for a role in a particular database", func() { |
| testutils.SkipIfBefore6(connectionPool) |
| |
| defaultStorageOptionsString := "appendonly=true, compresslevel=6, orientation=row, compresstype=none" |
| if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("7")) || connectionPool.Version.IsCBDB() { |
| defaultStorageOptionsString = "compresslevel=6, compresstype=none" |
| } |
| |
| roleConfigMap := map[string][]backup.RoleGUC{ |
| "testuser": { |
| {RoleName: "testuser", DbName: "testdb", Config: fmt.Sprintf("SET gp_default_storage_options TO '%s'", defaultStorageOptionsString)}, |
| {RoleName: "testuser", DbName: "testdb", Config: "SET search_path TO public"}}, |
| } |
| |
| backup.PrintRoleGUCStatements(backupfile, tocfile, roleConfigMap) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| |
| resultGUCs := backup.GetRoleGUCs(connectionPool) |
| Expect(resultGUCs["testuser"]).To(ConsistOf(roleConfigMap["testuser"])) |
| }) |
| }) |
| Describe("PrintCreateTablespaceStatements", func() { |
| var expectedTablespace backup.Tablespace |
| BeforeEach(func() { |
| if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("6")) || connectionPool.Version.IsCBDB() { |
| expectedTablespace = backup.Tablespace{Oid: 1, Tablespace: "test_tablespace", FileLocation: "'/tmp/test_dir'", SegmentLocations: []string{}} |
| } else { |
| expectedTablespace = backup.Tablespace{Oid: 1, Tablespace: "test_tablespace", FileLocation: "test_dir"} |
| } |
| }) |
| It("creates a basic tablespace", func() { |
| numTablespaces := len(backup.GetTablespaces(connectionPool)) |
| emptyMetadataMap := backup.MetadataMap{} |
| backup.PrintCreateTablespaceStatements(backupfile, tocfile, []backup.Tablespace{expectedTablespace}, emptyMetadataMap) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, "DROP TABLESPACE test_tablespace") |
| |
| resultTablespaces := backup.GetTablespaces(connectionPool) |
| Expect(resultTablespaces).To(HaveLen(numTablespaces + 1)) |
| for _, tablespace := range resultTablespaces { |
| if tablespace.Tablespace == "test_tablespace" { |
| structmatcher.ExpectStructsToMatchExcluding(&expectedTablespace, &tablespace, "Oid") |
| return |
| } |
| } |
| Fail("Tablespace 'test_tablespace' was not created") |
| }) |
| It("creates a basic tablespace with different segment locations and options", func() { |
| testutils.SkipIfBefore6(connectionPool) |
| |
| expectedTablespace = backup.Tablespace{ |
| Oid: 1, Tablespace: "test_tablespace", FileLocation: "'/tmp/test_dir'", |
| SegmentLocations: []string{"content0='/tmp/test_dir1'", "content1='/tmp/test_dir2'"}, |
| Options: "seq_page_cost=123", |
| } |
| numTablespaces := len(backup.GetTablespaces(connectionPool)) |
| emptyMetadataMap := backup.MetadataMap{} |
| backup.PrintCreateTablespaceStatements(backupfile, tocfile, []backup.Tablespace{expectedTablespace}, emptyMetadataMap) |
| |
| gbuffer := BufferWithBytes([]byte(buffer.String())) |
| entries, _ := testutils.SliceBufferByEntries(tocfile.GlobalEntries, gbuffer) |
| create, setOptions := entries[0], entries[1] |
| testhelper.AssertQueryRuns(connectionPool, create) |
| defer testhelper.AssertQueryRuns(connectionPool, "DROP TABLESPACE test_tablespace") |
| testhelper.AssertQueryRuns(connectionPool, setOptions) |
| |
| resultTablespaces := backup.GetTablespaces(connectionPool) |
| Expect(resultTablespaces).To(HaveLen(numTablespaces + 1)) |
| for _, tablespace := range resultTablespaces { |
| if tablespace.Tablespace == "test_tablespace" { |
| structmatcher.ExpectStructsToMatchExcluding(&expectedTablespace, &tablespace, "Oid") |
| return |
| } |
| } |
| Fail("Tablespace 'test_tablespace' was not created") |
| }) |
| It("creates a tablespace with permissions, an owner, security label, and a comment", func() { |
| numTablespaces := len(backup.GetTablespaces(connectionPool)) |
| tablespaceMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_TABLESPACE, true, true, true, includeSecurityLabels) |
| tablespaceMetadata := tablespaceMetadataMap[expectedTablespace.GetUniqueID()] |
| backup.PrintCreateTablespaceStatements(backupfile, tocfile, []backup.Tablespace{expectedTablespace}, tablespaceMetadataMap) |
| |
| if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("6")) || connectionPool.Version.IsCBDB() { |
| /* |
| * In GPDB 6 and later, a CREATE TABLESPACE statement can't be run in a multi-command string |
| * with other statements, so we execute it separately from the metadata statements. |
| */ |
| gbuffer := BufferWithBytes([]byte(buffer.String())) |
| entries, _ := testutils.SliceBufferByEntries(tocfile.GlobalEntries, gbuffer) |
| for _, entry := range entries { |
| testhelper.AssertQueryRuns(connectionPool, entry) |
| } |
| defer testhelper.AssertQueryRuns(connectionPool, "DROP TABLESPACE test_tablespace") |
| } else { |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, "DROP TABLESPACE test_tablespace") |
| } |
| |
| resultTablespaces := backup.GetTablespaces(connectionPool) |
| resultMetadataMap := backup.GetMetadataForObjectType(connectionPool, backup.TYPE_TABLESPACE) |
| Expect(resultTablespaces).To(HaveLen(numTablespaces + 1)) |
| for _, tablespace := range resultTablespaces { |
| if tablespace.Tablespace == "test_tablespace" { |
| resultMetadata := resultMetadataMap[tablespace.GetUniqueID()] |
| structmatcher.ExpectStructsToMatchExcluding(&tablespaceMetadata, &resultMetadata, "Oid") |
| structmatcher.ExpectStructsToMatchExcluding(&expectedTablespace, &tablespace, "Oid") |
| return |
| } |
| } |
| Fail("Tablespace 'test_tablespace' was not created") |
| }) |
| |
| It("creates a basic remote tablespace", func() { |
| // TODO: Re-enable this test once Cloudberry natively supports remote tablespaces. |
| // This test is skipped because creating a remote tablespace correctly requires a |
| // specific database extension to be installed. That extension intercepts the |
| // 'CREATE TABLESPACE' command to prevent the database from checking for a local |
| // directory, which does not exist for a remote tablespace and would otherwise |
| // cause an error. |
| Skip("Skipping remote tablespace test: requires a specific database plugin not present in the test environment.") |
| |
| // The test logic below is preserved for when this test can be re-enabled. |
| if !connectionPool.Version.IsCBDB() { |
| Skip("Test is for CBDB remote tablespaces only") |
| } |
| testhelper.AssertQueryRuns(connectionPool, `CREATE STORAGE SERVER test_server OPTIONS(protocol 's3', endpoint 's3.example.com')`) |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP STORAGE SERVER test_server`) |
| |
| tablespaceToCreate := backup.Tablespace{ |
| Tablespace: "test_remote_tablespace_basic", |
| Options: "server=test_server, path='/test/path', random_page_cost=4", |
| Spcfilehandlerbin: "$libdir/dfs_tablespace", |
| Spcfilehandlersrc: "remote_file_handler", |
| } |
| emptyMetadataMap := backup.MetadataMap{} |
| numTablespaces := len(backup.GetTablespaces(connectionPool)) |
| |
| backup.PrintCreateTablespaceStatements(backupfile, tocfile, []backup.Tablespace{tablespaceToCreate}, emptyMetadataMap) |
| |
| gbuffer := BufferWithBytes([]byte(buffer.String())) |
| entries, _ := testutils.SliceBufferByEntries(tocfile.GlobalEntries, gbuffer) |
| for _, entry := range entries { |
| testhelper.AssertQueryRuns(connectionPool, entry) |
| } |
| defer testhelper.AssertQueryRuns(connectionPool, "DROP TABLESPACE test_remote_tablespace_basic") |
| |
| resultTablespaces := backup.GetTablespaces(connectionPool) |
| Expect(resultTablespaces).To(HaveLen(numTablespaces + 1)) |
| |
| var resultTablespace backup.Tablespace |
| for _, ts := range resultTablespaces { |
| if ts.Tablespace == "test_remote_tablespace_basic" { |
| resultTablespace = ts |
| break |
| } |
| } |
| if resultTablespace.Tablespace == "" { |
| Fail("Tablespace 'test_remote_tablespace_basic' was not created") |
| } |
| |
| Expect(resultTablespace.Options).To(ContainSubstring("server=test_server")) |
| Expect(resultTablespace.Options).To(ContainSubstring("path='/test/path'")) |
| Expect(resultTablespace.Options).To(ContainSubstring("random_page_cost=4")) |
| Expect(resultTablespace.Spcfilehandlerbin).To(Equal(tablespaceToCreate.Spcfilehandlerbin)) |
| Expect(resultTablespace.Spcfilehandlersrc).To(Equal(tablespaceToCreate.Spcfilehandlersrc)) |
| Expect(resultTablespace.FileLocation).To(BeEmpty()) |
| }) |
| }) |
| |
| Describe("PrintCreateStorageServerStatements", func() { |
| It("creates a basic storage server with owner and comment", func() { |
| if !connectionPool.Version.IsCBDB() { |
| Skip("Test is for CBDB storage servers only") |
| } |
| |
| serverToCreate := backup.StorageServer{ |
| Oid: 1, |
| Server: "test_server", |
| ServerOptions: "protocol=s3, region=us-east-1, endpoint=s3.example.com", |
| } |
| serverMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_STORAGE_SERVER, false, true, true, false) |
| serverMetadata := serverMetadataMap[serverToCreate.GetUniqueID()] |
| serverMetadata.Owner = "testrole" |
| numServers := len(backup.GetStorageServers(connectionPool)) |
| |
| backup.PrintCreateStorageServerStatements(backupfile, tocfile, []backup.StorageServer{serverToCreate}, serverMetadataMap) |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, "DROP STORAGE SERVER test_server") |
| |
| resultServers := backup.GetStorageServers(connectionPool) |
| Expect(resultServers).To(HaveLen(numServers + 1)) |
| var resultServer backup.StorageServer |
| for _, srv := range resultServers { |
| if srv.Server == "test_server" { |
| resultServer = srv |
| break |
| } |
| } |
| if resultServer.Server == "" { |
| Fail("Storage Server 'test_server' was not created") |
| } |
| Expect(resultServer.ServerOptions).To(ContainSubstring("protocol=s3")) |
| Expect(resultServer.ServerOptions).To(ContainSubstring("region=us-east-1")) |
| Expect(resultServer.ServerOptions).To(ContainSubstring("endpoint=s3.example.com")) |
| }) |
| }) |
| |
| Describe("PrintCreateStorageUserMappingStatements", func() { |
| It("creates a basic storage user mapping with a comment", func() { |
| if !connectionPool.Version.IsCBDB() { |
| Skip("Test is for CBDB storage user mappings only") |
| } |
| |
| testhelper.AssertQueryRuns(connectionPool, `CREATE STORAGE SERVER test_server_for_mapping OPTIONS(protocol 's3', endpoint 's3.example.com')`) |
| defer testhelper.AssertQueryRuns(connectionPool, `DROP STORAGE SERVER test_server_for_mapping`) |
| |
| mappingToCreate := backup.StorageUserMapping{ |
| Oid: 1, |
| User: "testrole", |
| Server: "test_server_for_mapping", |
| Options: "accesskey=mykey, secretkey=mysecret", |
| } |
| numMappings := len(backup.GetStorageUserMapping(connectionPool)) |
| |
| backup.PrintCreateStorageUserMappingStatements(backupfile, tocfile, []backup.StorageUserMapping{mappingToCreate}) |
| |
| testhelper.AssertQueryRuns(connectionPool, buffer.String()) |
| defer testhelper.AssertQueryRuns(connectionPool, "DROP STORAGE USER MAPPING FOR testrole STORAGE SERVER test_server_for_mapping") |
| |
| resultMappings := backup.GetStorageUserMapping(connectionPool) |
| Expect(resultMappings).To(HaveLen(numMappings + 1)) |
| var resultMapping backup.StorageUserMapping |
| for _, m := range resultMappings { |
| if m.User == "testrole" && m.Server == "test_server_for_mapping" { |
| resultMapping = m |
| break |
| } |
| } |
| if resultMapping.User == "" { |
| Fail("Storage User Mapping for 'testrole' on 'test_server_for_mapping' was not created") |
| } |
| Expect(resultMapping.Options).To(ContainSubstring("accesskey=mykey")) |
| Expect(resultMapping.Options).To(ContainSubstring("secretkey=mysecret")) |
| |
| }) |
| }) |
| }) |