github.com/tuhaihe/gpbackup@v1.0.3/integration/metadata_globals_queries_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/tuhaihe/gp-common-go-libs/structmatcher"
     7  	"github.com/tuhaihe/gp-common-go-libs/testhelper"
     8  	"github.com/tuhaihe/gpbackup/backup"
     9  	"github.com/tuhaihe/gpbackup/testutils"
    10  
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  )
    14  
    15  var _ = Describe("backup integration tests", func() {
    16  	Describe("GetSessionGUCs", func() {
    17  		It("returns a slice of values for session level GUCs", func() {
    18  			/*
    19  			 * We shouldn't need to run any setup queries, because we're using
    20  			 * the default values of these GUCs.
    21  			 */
    22  			results := backup.GetSessionGUCs(connectionPool)
    23  			Expect(results.ClientEncoding).To(Equal("UTF8"))
    24  		})
    25  	})
    26  	Describe("GetDatabaseGUCs", func() {
    27  		It("returns a slice of values for database level GUCs", func() {
    28  			testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb SET enable_nestloop TO true")
    29  			defer testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb RESET enable_nestloop")
    30  			testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb SET search_path TO public,pg_catalog")
    31  			defer testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb RESET search_path")
    32  			testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb SET lc_time TO 'C'")
    33  			defer testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb RESET lc_time")
    34  			results := backup.GetDatabaseGUCs(connectionPool)
    35  			Expect(results).To(HaveLen(3))
    36  			Expect(results[0]).To(Equal(`SET enable_nestloop TO 'true'`))
    37  			Expect(results[1]).To(Equal("SET search_path TO public, pg_catalog"))
    38  			Expect(results[2]).To(Equal(`SET lc_time TO 'C'`))
    39  		})
    40  		It("only gets GUCs that are non role specific", func() {
    41  			testutils.SkipIfBefore6(connectionPool)
    42  			testhelper.AssertQueryRuns(connectionPool, "ALTER ROLE testrole IN DATABASE testdb SET enable_nestloop TO true")
    43  			defer testhelper.AssertQueryRuns(connectionPool, "ALTER ROLE testrole IN DATABASE testdb RESET enable_nestloop")
    44  			testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb SET enable_nestloop TO false")
    45  			defer testhelper.AssertQueryRuns(connectionPool, "ALTER DATABASE testdb RESET enable_nestloop")
    46  			results := backup.GetDatabaseGUCs(connectionPool)
    47  			Expect(results).To(HaveLen(1))
    48  			Expect(results[0]).To(Equal(`SET enable_nestloop TO 'false'`))
    49  		})
    50  	})
    51  	Describe("GetDefaultDatabaseEncodingInfo", func() {
    52  		It("queries default values from template0", func() {
    53  			result := backup.GetDefaultDatabaseEncodingInfo(connectionPool)
    54  
    55  			Expect(result.Name).To(Equal("template0"))
    56  			Expect(result.Encoding).To(Equal("UTF8"))
    57  			if true {
    58  				/*
    59  				 * These values are slightly different between mac and linux
    60  				 * so we use a regexp to match them
    61  				 */
    62  				Expect(result.Collate).To(MatchRegexp("en_US.(utf|UTF)-?8"))
    63  				Expect(result.CType).To(MatchRegexp("en_US.(utf|UTF)-?8"))
    64  			}
    65  		})
    66  	})
    67  	Describe("GetDatabaseInfo", func() {
    68  		It("returns a database info struct for a basic database", func() {
    69  			result := backup.GetDatabaseInfo(connectionPool)
    70  
    71  			testdbExpected := backup.Database{Oid: 0, Name: "testdb", Tablespace: "pg_default", Encoding: "UTF8"}
    72  			structmatcher.ExpectStructsToMatchExcluding(&testdbExpected, &result, "Oid", "Collate", "CType")
    73  		})
    74  		It("returns a database info struct for a complex database", func() {
    75  			var expectedDB backup.Database
    76  			if false {
    77  				testhelper.AssertQueryRuns(connectionPool, "CREATE DATABASE create_test_db ENCODING 'UTF8' TEMPLATE template0")
    78  				expectedDB = backup.Database{Oid: 1, Name: "create_test_db", Tablespace: "pg_default", Encoding: "UTF8", Collate: "", CType: ""}
    79  			} else {
    80  				testhelper.AssertQueryRuns(connectionPool, "CREATE DATABASE create_test_db ENCODING 'UTF8' LC_COLLATE 'en_US.utf-8' LC_CTYPE 'en_US.utf-8' TEMPLATE template0")
    81  				expectedDB = backup.Database{Oid: 1, Name: "create_test_db", Tablespace: "pg_default", Encoding: "UTF8", Collate: "en_US.utf-8", CType: "en_US.utf-8"}
    82  			}
    83  			defer testhelper.AssertQueryRuns(connectionPool, "DROP DATABASE create_test_db")
    84  
    85  			connectionPool.DBName = `create_test_db`
    86  			result := backup.GetDatabaseInfo(connectionPool)
    87  			connectionPool.DBName = `testdb`
    88  
    89  			structmatcher.ExpectStructsToMatchExcluding(&expectedDB, &result, "Oid")
    90  		})
    91  		It("returns a database info struct if database contains single quote", func() {
    92  			testhelper.AssertQueryRuns(connectionPool, `CREATE DATABASE "test'db"`)
    93  			defer testhelper.AssertQueryRuns(connectionPool, `DROP DATABASE "test'db"`)
    94  			connectionPool.DBName = `test'db`
    95  			result := backup.GetDatabaseInfo(connectionPool)
    96  			connectionPool.DBName = `testdb`
    97  
    98  			testdbExpected := backup.Database{Oid: 0, Name: `"test'db"`, Tablespace: "pg_default", Encoding: "UTF8"}
    99  			structmatcher.ExpectStructsToMatchExcluding(&testdbExpected, &result, "Oid", "Collate", "CType")
   100  		})
   101  	})
   102  	Describe("GetResourceQueues", func() {
   103  		It("returns a slice for a resource queue with only ACTIVE_STATEMENTS", func() {
   104  			testhelper.AssertQueryRuns(connectionPool, `CREATE RESOURCE QUEUE "statementsQueue" WITH (ACTIVE_STATEMENTS=7);`)
   105  			defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE QUEUE "statementsQueue"`)
   106  
   107  			results := backup.GetResourceQueues(connectionPool)
   108  
   109  			statementsQueue := backup.ResourceQueue{Oid: 1, Name: `"statementsQueue"`, ActiveStatements: 7, MaxCost: "-1.00", CostOvercommit: false, MinCost: "0.00", Priority: "medium", MemoryLimit: "-1"}
   110  
   111  			//Since resource queues are global, we can't be sure this is the only one
   112  			for _, resultQueue := range results {
   113  				if resultQueue.Name == `"statementsQueue"` {
   114  					structmatcher.ExpectStructsToMatchExcluding(&statementsQueue, &resultQueue, "Oid")
   115  					return
   116  				}
   117  			}
   118  			Fail("Resource queue 'statementsQueue' was not found.")
   119  		})
   120  		It("returns a slice for a resource queue with only MAX_COST", func() {
   121  			testhelper.AssertQueryRuns(connectionPool, `CREATE RESOURCE QUEUE "maxCostQueue" WITH (MAX_COST=32.8);`)
   122  			defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE QUEUE "maxCostQueue"`)
   123  
   124  			results := backup.GetResourceQueues(connectionPool)
   125  
   126  			maxCostQueue := backup.ResourceQueue{Oid: 1, Name: `"maxCostQueue"`, ActiveStatements: -1, MaxCost: "32.80", CostOvercommit: false, MinCost: "0.00", Priority: "medium", MemoryLimit: "-1"}
   127  
   128  			for _, resultQueue := range results {
   129  				if resultQueue.Name == `"maxCostQueue"` {
   130  					structmatcher.ExpectStructsToMatchExcluding(&maxCostQueue, &resultQueue, "Oid")
   131  					return
   132  				}
   133  			}
   134  			Fail("Resource queue 'maxCostQueue' was not found.")
   135  		})
   136  		It("returns a slice for a resource queue with everything", func() {
   137  			testhelper.AssertQueryRuns(connectionPool, `CREATE RESOURCE QUEUE "everyQueue" WITH (ACTIVE_STATEMENTS=7, MAX_COST=3e+4, COST_OVERCOMMIT=TRUE, MIN_COST=22.53, PRIORITY=LOW, MEMORY_LIMIT='2GB');`)
   138  			defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE QUEUE "everyQueue"`)
   139  
   140  			results := backup.GetResourceQueues(connectionPool)
   141  
   142  			everyQueue := backup.ResourceQueue{Oid: 1, Name: `"everyQueue"`, ActiveStatements: 7, MaxCost: "30000.00", CostOvercommit: true, MinCost: "22.53", Priority: "low", MemoryLimit: "2GB"}
   143  
   144  			for _, resultQueue := range results {
   145  				if resultQueue.Name == `"everyQueue"` {
   146  					structmatcher.ExpectStructsToMatchExcluding(&everyQueue, &resultQueue, "Oid")
   147  					return
   148  				}
   149  			}
   150  			Fail("Resource queue 'everyQueue' was not found.")
   151  		})
   152  
   153  	})
   154  	Describe("GetResourceGroups", func() {
   155  		BeforeEach(func() {
   156  			testutils.SkipIfBefore5(connectionPool)
   157  		})
   158  		It("returns a slice for a resource group with everything", func() {
   159  			testhelper.AssertQueryRuns(connectionPool, `CREATE RESOURCE GROUP "someGroup" WITH (CPU_RATE_LIMIT=10, MEMORY_LIMIT=20, MEMORY_SHARED_QUOTA=25, MEMORY_SPILL_RATIO=30, CONCURRENCY=15);`)
   160  			defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE GROUP "someGroup"`)
   161  
   162  			results := backup.GetResourceGroups(connectionPool)
   163  
   164  			someGroup := backup.ResourceGroup{Oid: 1, Name: `"someGroup"`, CPURateLimit: "10", MemoryLimit: "20", Concurrency: "15", MemorySharedQuota: "25", MemorySpillRatio: "30", MemoryAuditor: "0", Cpuset: "-1"}
   165  
   166  			for _, resultGroup := range results {
   167  				if resultGroup.Name == `"someGroup"` {
   168  					structmatcher.ExpectStructsToMatchExcluding(&someGroup, &resultGroup, "Oid")
   169  					return
   170  				}
   171  			}
   172  			Fail("Resource group 'someGroup' was not found.")
   173  		})
   174  		It("returns a slice for a resource group with memory_auditor=vmtracker", func() {
   175  			testhelper.AssertQueryRuns(connectionPool, `CREATE RESOURCE GROUP "someGroup" WITH (CPU_RATE_LIMIT=10, MEMORY_LIMIT=20, MEMORY_SHARED_QUOTA=25, MEMORY_SPILL_RATIO=30, CONCURRENCY=0, MEMORY_AUDITOR=vmtracker);`)
   176  			defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE GROUP "someGroup"`)
   177  
   178  			results := backup.GetResourceGroups(connectionPool)
   179  
   180  			someGroup := backup.ResourceGroup{Oid: 1, Name: `"someGroup"`, CPURateLimit: "10", MemoryLimit: "20", Concurrency: "0", MemorySharedQuota: "25", MemorySpillRatio: "30", MemoryAuditor: "0", Cpuset: "-1"}
   181  
   182  			for _, resultGroup := range results {
   183  				if resultGroup.Name == `"someGroup"` {
   184  					structmatcher.ExpectStructsToMatchExcluding(&someGroup, &resultGroup, "Oid")
   185  					return
   186  				}
   187  			}
   188  			Fail("Resource group 'someGroup' was not found.")
   189  		})
   190  		It("returns a resource group with defaults", func() {
   191  			testhelper.AssertQueryRuns(connectionPool, `CREATE RESOURCE GROUP "someGroup" WITH (CPU_RATE_LIMIT=10, MEMORY_LIMIT=20);`)
   192  			defer testhelper.AssertQueryRuns(connectionPool, `DROP RESOURCE GROUP "someGroup"`)
   193  
   194  			results := backup.GetResourceGroups(connectionPool)
   195  
   196  			expectedDefaults := backup.ResourceGroup{Oid: 1, Name: `"someGroup"`, CPURateLimit: "10", MemoryLimit: "20", Concurrency: concurrencyDefault, MemorySharedQuota: memSharedDefault, MemorySpillRatio: memSpillDefault, MemoryAuditor: memAuditDefault, Cpuset: cpuSetDefault}
   197  
   198  			for _, resultGroup := range results {
   199  				if resultGroup.Name == `"someGroup"` {
   200  					structmatcher.ExpectStructsToMatchExcluding(&expectedDefaults, &resultGroup, "Oid")
   201  					return
   202  				}
   203  			}
   204  			Fail("Resource group 'someGroup' was not found.")
   205  		})
   206  	})
   207  	Describe("GetDatabaseRoles", func() {
   208  		It("returns a role with default properties", func() {
   209  			testhelper.AssertQueryRuns(connectionPool, "CREATE ROLE role1 SUPERUSER NOINHERIT")
   210  			defer testhelper.AssertQueryRuns(connectionPool, "DROP ROLE role1")
   211  
   212  			results := backup.GetRoles(connectionPool)
   213  
   214  			roleOid := testutils.OidFromObjectName(connectionPool, "", "role1", backup.TYPE_ROLE)
   215  			expectedRole := backup.Role{
   216  				Oid:             roleOid,
   217  				Name:            "role1",
   218  				Super:           true,
   219  				Inherit:         false,
   220  				CreateRole:      false,
   221  				CreateDB:        false,
   222  				CanLogin:        false,
   223  				Replication:     false,
   224  				ConnectionLimit: -1,
   225  				Password:        "",
   226  				ValidUntil:      "",
   227  				ResQueue:        "pg_default",
   228  				ResGroup:        "admin_group",
   229  				Createrexthttp:  false,
   230  				Createrextgpfd:  false,
   231  				Createwextgpfd:  false,
   232  				Createrexthdfs:  false,
   233  				Createwexthdfs:  false,
   234  				TimeConstraints: nil,
   235  			}
   236  			if false {
   237  				expectedRole.ResGroup = ""
   238  			}
   239  			for _, role := range results {
   240  				if role.Name == "role1" {
   241  					structmatcher.ExpectStructsToMatch(&expectedRole, role)
   242  					return
   243  				}
   244  			}
   245  			Fail("Role 'role1' was not found")
   246  		})
   247  		It("returns a role with all properties specified", func() {
   248  			testhelper.AssertQueryRuns(connectionPool, "CREATE ROLE role1")
   249  			defer testhelper.AssertQueryRuns(connectionPool, "DROP ROLE role1")
   250  			setupQuery := `
   251  ALTER ROLE role1 WITH NOSUPERUSER INHERIT CREATEROLE CREATEDB LOGIN
   252  CONNECTION LIMIT 4 PASSWORD 'swordfish' VALID UNTIL '2099-01-01 00:00:00-08'
   253  CREATEEXTTABLE (protocol='http')
   254  CREATEEXTTABLE (protocol='gpfdist', type='readable')
   255  CREATEEXTTABLE (protocol='gpfdist', type='writable')`
   256  			if false {
   257  				setupQuery += `
   258  CREATEEXTTABLE (protocol='gphdfs', type='readable')
   259  CREATEEXTTABLE (protocol='gphdfs', type='writable')`
   260  			}
   261  			testhelper.AssertQueryRuns(connectionPool, setupQuery)
   262  			testhelper.AssertQueryRuns(connectionPool, "ALTER ROLE role1 DENY BETWEEN DAY 'Sunday' TIME '1:30 PM' AND DAY 'Wednesday' TIME '14:30:00'")
   263  			testhelper.AssertQueryRuns(connectionPool, "ALTER ROLE role1 DENY DAY 'Friday'")
   264  			testhelper.AssertQueryRuns(connectionPool, "COMMENT ON ROLE role1 IS 'this is a role comment'")
   265  
   266  			results := backup.GetRoles(connectionPool)
   267  
   268  			roleOid := testutils.OidFromObjectName(connectionPool, "", "role1", backup.TYPE_ROLE)
   269  			expectedRole := backup.Role{
   270  				Oid:             roleOid,
   271  				Name:            "role1",
   272  				Super:           false,
   273  				Inherit:         true,
   274  				CreateRole:      true,
   275  				CreateDB:        true,
   276  				CanLogin:        true,
   277  				ConnectionLimit: 4,
   278  				Password:        "md5a8b2c77dfeba4705f29c094592eb3369",
   279  				ValidUntil:      "2099-01-01 08:00:00-00",
   280  				ResQueue:        "pg_default",
   281  				ResGroup:        "",
   282  				Createrexthttp:  true,
   283  				Createrextgpfd:  true,
   284  				Createwextgpfd:  true,
   285  				Createrexthdfs:  true,
   286  				Createwexthdfs:  true,
   287  				TimeConstraints: []backup.TimeConstraint{
   288  					{
   289  						Oid:       0,
   290  						StartDay:  0,
   291  						StartTime: "13:30:00",
   292  						EndDay:    3,
   293  						EndTime:   "14:30:00",
   294  					}, {
   295  						Oid:       0,
   296  						StartDay:  5,
   297  						StartTime: "00:00:00",
   298  						EndDay:    5,
   299  						EndTime:   "24:00:00",
   300  					},
   301  				},
   302  			}
   303  
   304  			if true {
   305  				expectedRole.ResGroup = "default_group"
   306  			}
   307  			if true {
   308  				expectedRole.Createrexthdfs = false
   309  				expectedRole.Createwexthdfs = false
   310  			}
   311  
   312  			for _, role := range results {
   313  				if role.Name == "role1" {
   314  					structmatcher.ExpectStructsToMatchExcluding(&expectedRole, role, "TimeConstraints.Oid")
   315  					return
   316  				}
   317  			}
   318  			Fail("Role 'role1' was not found")
   319  		})
   320  		It("returns a role with replication", func() {
   321  			testutils.SkipIfBefore6(connectionPool)
   322  
   323  			testhelper.AssertQueryRuns(connectionPool, "CREATE ROLE role1 WITH SUPERUSER NOINHERIT REPLICATION")
   324  			defer testhelper.AssertQueryRuns(connectionPool, "DROP ROLE role1")
   325  
   326  			results := backup.GetRoles(connectionPool)
   327  
   328  			roleOid := testutils.OidFromObjectName(connectionPool, "", "role1", backup.TYPE_ROLE)
   329  			expectedRole := backup.Role{
   330  				Oid:             roleOid,
   331  				Name:            "role1",
   332  				Super:           true,
   333  				Inherit:         false,
   334  				CreateRole:      false,
   335  				CreateDB:        false,
   336  				CanLogin:        false,
   337  				Replication:     true,
   338  				ConnectionLimit: -1,
   339  				Password:        "",
   340  				ValidUntil:      "",
   341  				ResQueue:        "pg_default",
   342  				ResGroup:        "admin_group",
   343  				Createrexthttp:  false,
   344  				Createrextgpfd:  false,
   345  				Createwextgpfd:  false,
   346  				Createrexthdfs:  false,
   347  				Createwexthdfs:  false,
   348  				TimeConstraints: nil,
   349  			}
   350  
   351  			for _, role := range results {
   352  				if role.Name == "role1" {
   353  					structmatcher.ExpectStructsToMatch(&expectedRole, role)
   354  					return
   355  				}
   356  			}
   357  			Fail("Role 'role1' was not found")
   358  		})
   359  		It("returns a role with inf/-inf ValidUntil field", func() {
   360  			testhelper.AssertQueryRuns(connectionPool, "CREATE ROLE role1inf WITH VALID UNTIL 'infinity' SUPERUSER NOINHERIT")
   361  			testhelper.AssertQueryRuns(connectionPool, "CREATE ROLE role1neginf WITH VALID UNTIL '-infinity' SUPERUSER NOINHERIT")
   362  			defer testhelper.AssertQueryRuns(connectionPool, "DROP ROLE role1inf")
   363  			defer testhelper.AssertQueryRuns(connectionPool, "DROP ROLE role1neginf")
   364  
   365  			results := backup.GetRoles(connectionPool)
   366  
   367  			expectedRoleInf := backup.Role{
   368  				Oid:             0,
   369  				Name:            "role1inf",
   370  				Super:           true,
   371  				Inherit:         false,
   372  				CreateRole:      false,
   373  				CreateDB:        false,
   374  				CanLogin:        false,
   375  				Replication:     false,
   376  				ConnectionLimit: -1,
   377  				Password:        "",
   378  				ValidUntil:      "infinity",
   379  				ResQueue:        "pg_default",
   380  				ResGroup:        "",
   381  				Createrexthttp:  false,
   382  				Createrextgpfd:  false,
   383  				Createwextgpfd:  false,
   384  				Createrexthdfs:  false,
   385  				Createwexthdfs:  false,
   386  				TimeConstraints: nil,
   387  			}
   388  
   389  			expectedRoleNegInf := backup.Role{
   390  				Oid:             0,
   391  				Name:            "role1neginf",
   392  				Super:           true,
   393  				Inherit:         false,
   394  				CreateRole:      false,
   395  				CreateDB:        false,
   396  				CanLogin:        false,
   397  				Replication:     false,
   398  				ConnectionLimit: -1,
   399  				Password:        "",
   400  				ValidUntil:      "-infinity",
   401  				ResQueue:        "pg_default",
   402  				ResGroup:        "",
   403  				Createrexthttp:  false,
   404  				Createrextgpfd:  false,
   405  				Createwextgpfd:  false,
   406  				Createrexthdfs:  false,
   407  				Createwexthdfs:  false,
   408  				TimeConstraints: nil,
   409  			}
   410  			var foundRoles int
   411  			for _, role := range results {
   412  				if role.Name == "role1inf" {
   413  					structmatcher.ExpectStructsToMatchExcluding(&expectedRoleInf, role, "Oid", "ResGroup")
   414  					foundRoles++
   415  				} else if role.Name == "role1neginf" {
   416  					structmatcher.ExpectStructsToMatchExcluding(&expectedRoleNegInf, role, "Oid", "ResGroup")
   417  					foundRoles++
   418  				}
   419  			}
   420  			if foundRoles != 2 {
   421  				Fail("Role 'role1inf' or 'role1neginf' was not found")
   422  			}
   423  		})
   424  		It("handles implicit cast of timestamp to text", func() {
   425  			testhelper.AssertQueryRuns(connectionPool, "CREATE ROLE role1 SUPERUSER NOINHERIT")
   426  
   427  			// Function and cast already exist on 4x
   428  			if true {
   429  				testhelper.AssertQueryRuns(connectionPool, "CREATE OR REPLACE FUNCTION pg_catalog.text(timestamp without time zone) RETURNS text STRICT IMMUTABLE LANGUAGE SQL AS 'SELECT textin(timestamp_out($1));';")
   430  				testhelper.AssertQueryRuns(connectionPool, "CREATE CAST (timestamp without time zone AS text) WITH FUNCTION pg_catalog.text(timestamp without time zone) AS IMPLICIT;")
   431  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION pg_catalog.text(timestamp without time zone) CASCADE;")
   432  			}
   433  
   434  			defer testhelper.AssertQueryRuns(connectionPool, "DROP ROLE role1")
   435  
   436  			results := backup.GetRoles(connectionPool)
   437  
   438  			roleOid := testutils.OidFromObjectName(connectionPool, "", "role1", backup.TYPE_ROLE)
   439  			expectedRole := backup.Role{
   440  				Oid:             roleOid,
   441  				Name:            "role1",
   442  				Super:           true,
   443  				Inherit:         false,
   444  				CreateRole:      false,
   445  				CreateDB:        false,
   446  				CanLogin:        false,
   447  				Replication:     false,
   448  				ConnectionLimit: -1,
   449  				Password:        "",
   450  				ValidUntil:      "",
   451  				ResQueue:        "pg_default",
   452  				ResGroup:        "admin_group",
   453  				Createrexthttp:  false,
   454  				Createrextgpfd:  false,
   455  				Createwextgpfd:  false,
   456  				Createrexthdfs:  false,
   457  				Createwexthdfs:  false,
   458  				TimeConstraints: nil,
   459  			}
   460  			if false {
   461  				expectedRole.ResGroup = ""
   462  			}
   463  			for _, role := range results {
   464  				if role.Name == "role1" {
   465  					structmatcher.ExpectStructsToMatch(&expectedRole, role)
   466  					return
   467  				}
   468  			}
   469  			Fail("Role 'role1' was not found")
   470  
   471  		})
   472  	})
   473  	Describe("GetRoleMembers", func() {
   474  		BeforeEach(func() {
   475  			testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE usergroup`)
   476  			testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE testuser`)
   477  		})
   478  		AfterEach(func() {
   479  			defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE usergroup`)
   480  			defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE testuser`)
   481  		})
   482  		It("returns a role without ADMIN OPTION", func() {
   483  			testhelper.AssertQueryRuns(connectionPool, "GRANT usergroup TO testuser")
   484  			expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: "testrole", IsAdmin: false}
   485  
   486  			roleMembers := backup.GetRoleMembers(connectionPool)
   487  
   488  			for _, roleMember := range roleMembers {
   489  				if roleMember.Role == "usergroup" {
   490  					structmatcher.ExpectStructsToMatch(&expectedRoleMember, &roleMember)
   491  					return
   492  				}
   493  			}
   494  			Fail("Role 'testuser' is not a member of role 'usergroup'")
   495  		})
   496  		It("returns a role WITH ADMIN OPTION", func() {
   497  			testhelper.AssertQueryRuns(connectionPool, "GRANT usergroup TO testuser WITH ADMIN OPTION GRANTED BY testrole")
   498  			expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: "testrole", IsAdmin: true}
   499  
   500  			roleMembers := backup.GetRoleMembers(connectionPool)
   501  
   502  			for _, roleMember := range roleMembers {
   503  				if roleMember.Role == "usergroup" {
   504  					structmatcher.ExpectStructsToMatch(&expectedRoleMember, &roleMember)
   505  					return
   506  				}
   507  			}
   508  			Fail("Role 'testuser' is not a member of role 'usergroup'")
   509  		})
   510  		It("returns properly quoted roles in GRANT statement", func() {
   511  			testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE "1testrole" SUPERUSER`)
   512  			defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE "1testrole"`)
   513  			testhelper.AssertQueryRuns(connectionPool, `SET ROLE "1testrole"`)
   514  			defer testhelper.AssertQueryRuns(connectionPool, `SET ROLE testrole`)
   515  			testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE "1usergroup"`)
   516  			defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE "1usergroup"`)
   517  			testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE "1testuser"`)
   518  			defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE "1testuser"`)
   519  			testhelper.AssertQueryRuns(connectionPool, `GRANT "1usergroup" TO "1testuser"`)
   520  			expectedRoleMember := backup.RoleMember{Role: `"1usergroup"`, Member: `"1testuser"`, Grantor: `"1testrole"`, IsAdmin: false}
   521  
   522  			roleMembers := backup.GetRoleMembers(connectionPool)
   523  
   524  			for _, roleMember := range roleMembers {
   525  				if roleMember.Role == `"1usergroup"` {
   526  					structmatcher.ExpectStructsToMatch(&expectedRoleMember, &roleMember)
   527  					return
   528  				}
   529  			}
   530  			Fail(`Role "1testuser" is not a member of role "1usergroup"`)
   531  		})
   532  		It("handles dropped granter", func() {
   533  			testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE testdropgranter_role`)
   534  			defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE testdropgranter_role`)
   535  			testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE testdropgranter_member`)
   536  			defer testhelper.AssertQueryRuns(connectionPool, `DROP ROLE testdropgranter_member`)
   537  			testhelper.AssertQueryRuns(connectionPool, `CREATE ROLE testdropgranter_granter`)
   538  			testhelper.AssertQueryRuns(connectionPool, `GRANT testdropgranter_role TO testdropgranter_member GRANTED BY testdropgranter_granter`)
   539  			testhelper.AssertQueryRuns(connectionPool, `DROP ROLE testdropgranter_granter`)
   540  			expectedRoleMember := backup.RoleMember{Role: `testdropgranter_role`, Member: `testdropgranter_member`, Grantor: ``, IsAdmin: false}
   541  
   542  			roleMember := backup.GetRoleMembers(connectionPool)
   543  			Expect(len(roleMember)).To(Equal(1))
   544  			structmatcher.ExpectStructsToMatch(&expectedRoleMember, &roleMember[0])
   545  		})
   546  		It("handles implicit cast of oid to text", func() {
   547  			// Function and cast already exist on 4x
   548  			if true {
   549  				testhelper.AssertQueryRuns(connectionPool, "CREATE OR REPLACE FUNCTION pg_catalog.text(oid) RETURNS text STRICT IMMUTABLE LANGUAGE SQL AS 'SELECT textin(oidout($1));';")
   550  				testhelper.AssertQueryRuns(connectionPool, "CREATE CAST (oid AS text) WITH FUNCTION pg_catalog.text(oid) AS IMPLICIT;")
   551  				defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION pg_catalog.text(oid) CASCADE;")
   552  			}
   553  			testhelper.AssertQueryRuns(connectionPool, "GRANT usergroup TO testuser")
   554  			expectedRoleMember := backup.RoleMember{Role: "usergroup", Member: "testuser", Grantor: "testrole", IsAdmin: false}
   555  
   556  			roleMembers := backup.GetRoleMembers(connectionPool)
   557  
   558  			for _, roleMember := range roleMembers {
   559  				if roleMember.Role == "usergroup" {
   560  					structmatcher.ExpectStructsToMatch(&expectedRoleMember, &roleMember)
   561  					return
   562  				}
   563  			}
   564  			Fail("Role 'testuser' is not a member of role 'usergroup'")
   565  		})
   566  	})
   567  	Describe("GetRoleGUCs", func() {
   568  		It("returns a slice of values for user level GUCs", func() {
   569  			testhelper.AssertQueryRuns(connectionPool, "CREATE ROLE role1 SUPERUSER NOINHERIT")
   570  			defer testhelper.AssertQueryRuns(connectionPool, "DROP ROLE role1")
   571  			testhelper.AssertQueryRuns(connectionPool, "ALTER ROLE role1 SET search_path TO public")
   572  			testhelper.AssertQueryRuns(connectionPool, "ALTER ROLE role1 SET client_min_messages TO 'info'")
   573  
   574  			defaultStorageOptionsString := "appendonly=true, compresslevel=6, orientation=row, compresstype=none"
   575  			if true {
   576  				defaultStorageOptionsString = "compresslevel=6, compresstype=none"
   577  			}
   578  			testhelper.AssertQueryRuns(connectionPool, fmt.Sprintf("ALTER ROLE role1 SET gp_default_storage_options TO '%s'", defaultStorageOptionsString))
   579  
   580  			results := backup.GetRoleGUCs(connectionPool)
   581  			roleConfig := results["role1"]
   582  
   583  			Expect(roleConfig).To(HaveLen(3))
   584  			expectedRoleConfig := []backup.RoleGUC{
   585  				{RoleName: "role1", Config: `SET client_min_messages TO 'info'`},
   586  				{RoleName: "role1", Config: fmt.Sprintf(`SET gp_default_storage_options TO '%s'`, defaultStorageOptionsString)},
   587  				{RoleName: "role1", Config: `SET search_path TO public`}}
   588  
   589  			Expect(roleConfig).To(ConsistOf(expectedRoleConfig))
   590  		})
   591  		It("returns a slice of values for db specific user level GUCs", func() {
   592  			testutils.SkipIfBefore6(connectionPool)
   593  
   594  			testhelper.AssertQueryRuns(connectionPool, "CREATE ROLE role1 SUPERUSER NOINHERIT")
   595  			defer testhelper.AssertQueryRuns(connectionPool, "DROP ROLE role1")
   596  			testhelper.AssertQueryRuns(connectionPool, "ALTER ROLE role1 IN DATABASE testdb SET search_path TO public")
   597  			testhelper.AssertQueryRuns(connectionPool, "ALTER ROLE role1 IN DATABASE testdb SET client_min_messages TO 'info'")
   598  
   599  			results := backup.GetRoleGUCs(connectionPool)
   600  			roleConfig := results["role1"]
   601  
   602  			Expect(roleConfig).To(HaveLen(2))
   603  			expectedRoleConfig := []backup.RoleGUC{
   604  				{RoleName: "role1", DbName: "testdb", Config: `SET client_min_messages TO 'info'`},
   605  				{RoleName: "role1", DbName: "testdb", Config: `SET search_path TO public`}}
   606  
   607  			Expect(roleConfig).To(ConsistOf(expectedRoleConfig))
   608  		})
   609  	})
   610  	Describe("GetTablespaces", func() {
   611  		It("returns a tablespace", func() {
   612  			var expectedTablespace backup.Tablespace
   613  			if false {
   614  				testhelper.AssertQueryRuns(connectionPool, "CREATE TABLESPACE test_tablespace FILESPACE test_dir")
   615  				expectedTablespace = backup.Tablespace{Oid: 0, Tablespace: "test_tablespace", FileLocation: "test_dir"}
   616  			} else {
   617  				testhelper.AssertQueryRuns(connectionPool, "CREATE TABLESPACE test_tablespace LOCATION '/tmp/test_dir'")
   618  				expectedTablespace = backup.Tablespace{Oid: 0, Tablespace: "test_tablespace", FileLocation: "'/tmp/test_dir'", SegmentLocations: []string{}}
   619  			}
   620  			defer testhelper.AssertQueryRuns(connectionPool, "DROP TABLESPACE test_tablespace")
   621  
   622  			resultTablespaces := backup.GetTablespaces(connectionPool)
   623  
   624  			for _, tablespace := range resultTablespaces {
   625  				if tablespace.Tablespace == "test_tablespace" {
   626  					structmatcher.ExpectStructsToMatchExcluding(&expectedTablespace, &tablespace, "Oid")
   627  					return
   628  				}
   629  			}
   630  			Fail("Tablespace 'test_tablespace' was not created")
   631  		})
   632  		It("returns a tablespace with segment locations and options", func() {
   633  			testutils.SkipIfBefore6(connectionPool)
   634  
   635  			testhelper.AssertQueryRuns(connectionPool, "CREATE TABLESPACE test_tablespace LOCATION '/tmp/test_dir' WITH (content0='/tmp/test_dir1', seq_page_cost=123)")
   636  			expectedTablespace := backup.Tablespace{
   637  				Oid: 0, Tablespace: "test_tablespace", FileLocation: "'/tmp/test_dir'",
   638  				SegmentLocations: []string{"content0='/tmp/test_dir1'"},
   639  				Options:          "seq_page_cost=123",
   640  			}
   641  
   642  			defer testhelper.AssertQueryRuns(connectionPool, "DROP TABLESPACE test_tablespace")
   643  
   644  			resultTablespaces := backup.GetTablespaces(connectionPool)
   645  
   646  			for _, tablespace := range resultTablespaces {
   647  				if tablespace.Tablespace == "test_tablespace" {
   648  					structmatcher.ExpectStructsToMatchExcluding(&expectedTablespace, &tablespace, "Oid")
   649  					return
   650  				}
   651  			}
   652  			Fail("Tablespace 'test_tablespace' was not created")
   653  		})
   654  		It("handles implicit cast of oid to text", func() {
   655  			testutils.SkipIfBefore6(connectionPool)
   656  
   657  			testhelper.AssertQueryRuns(connectionPool, "CREATE TABLESPACE test_tablespace LOCATION '/tmp/test_dir' WITH (content0='/tmp/test_dir1', seq_page_cost=123)")
   658  			testhelper.AssertQueryRuns(connectionPool, "CREATE OR REPLACE FUNCTION pg_catalog.text(oid) RETURNS text STRICT IMMUTABLE LANGUAGE SQL AS 'SELECT textin(oidout($1));';")
   659  			testhelper.AssertQueryRuns(connectionPool, "CREATE CAST (oid AS text) WITH FUNCTION pg_catalog.text(oid) AS IMPLICIT;")
   660  			expectedTablespace := backup.Tablespace{
   661  				Oid: 0, Tablespace: "test_tablespace", FileLocation: "'/tmp/test_dir'",
   662  				SegmentLocations: []string{"content0='/tmp/test_dir1'"},
   663  				Options:          "seq_page_cost=123",
   664  			}
   665  
   666  			defer testhelper.AssertQueryRuns(connectionPool, "DROP TABLESPACE test_tablespace")
   667  			defer testhelper.AssertQueryRuns(connectionPool, "DROP FUNCTION pg_catalog.text(oid) CASCADE;")
   668  
   669  			resultTablespaces := backup.GetTablespaces(connectionPool)
   670  
   671  			for _, tablespace := range resultTablespaces {
   672  				if tablespace.Tablespace == "test_tablespace" {
   673  					structmatcher.ExpectStructsToMatchExcluding(&expectedTablespace, &tablespace, "Oid")
   674  					return
   675  				}
   676  			}
   677  			Fail("Tablespace 'test_tablespace' was not created")
   678  		})
   679  	})
   680  })