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"))

		})
	})
})
