blob: 2063c3a1601c7946f24732ade0d75bae291da0bc [file]
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"))
})
})
})