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