github.com/tuhaihe/gpbackup@v1.0.3/end_to_end/incremental_test.go (about) 1 package end_to_end_test 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "strconv" 8 9 "github.com/tuhaihe/gp-common-go-libs/dbconn" 10 "github.com/tuhaihe/gp-common-go-libs/testhelper" 11 12 . "github.com/onsi/ginkgo/v2" 13 . "github.com/onsi/gomega" 14 ) 15 16 var _ = Describe("End to End incremental tests", func() { 17 BeforeEach(func() { 18 end_to_end_setup() 19 }) 20 AfterEach(func() { 21 end_to_end_teardown() 22 }) 23 24 Describe("Incremental backup", func() { 25 BeforeEach(func() { 26 skipIfOldBackupVersionBefore("1.7.0") 27 }) 28 It("restores from an incremental backup specified with a timestamp", func() { 29 fullBackupTimestamp := gpbackup(gpbackupPath, backupHelperPath, 30 "--leaf-partition-data") 31 32 testhelper.AssertQueryRuns(backupConn, 33 "INSERT into schema2.ao1 values(1001)") 34 defer testhelper.AssertQueryRuns(backupConn, 35 "DELETE from schema2.ao1 where i=1001") 36 incremental1Timestamp := gpbackup(gpbackupPath, backupHelperPath, 37 "--incremental", 38 "--leaf-partition-data", 39 "--from-timestamp", fullBackupTimestamp) 40 41 testhelper.AssertQueryRuns(backupConn, 42 "INSERT into schema2.ao1 values(1002)") 43 defer testhelper.AssertQueryRuns(backupConn, 44 "DELETE from schema2.ao1 where i=1002") 45 incremental2Timestamp := gpbackup(gpbackupPath, backupHelperPath, 46 "--incremental", 47 "--leaf-partition-data", 48 "--from-timestamp", incremental1Timestamp) 49 50 gprestore(gprestorePath, restoreHelperPath, incremental2Timestamp, 51 "--redirect-db", "restoredb") 52 53 assertRelationsCreated(restoreConn, TOTAL_RELATIONS) 54 assertDataRestored(restoreConn, publicSchemaTupleCounts) 55 schema2TupleCounts["schema2.ao1"] = 1002 56 assertDataRestored(restoreConn, schema2TupleCounts) 57 }) 58 It("restores from an incremental backup with AO Table consisting of multiple segment files", func() { 59 // Versions before 1.13.0 incorrectly handle AO table inserts involving multiple seg files 60 skipIfOldBackupVersionBefore("1.13.0") 61 62 testhelper.AssertQueryRuns(backupConn, 63 "CREATE TABLE foobar WITH (appendonly=true) AS SELECT i FROM generate_series(1,5) i") 64 defer testhelper.AssertQueryRuns(backupConn, 65 "DROP TABLE foobar") 66 testhelper.AssertQueryRuns(backupConn, "VACUUM foobar") 67 entriesInTable := dbconn.MustSelectString(backupConn, 68 "SELECT count(*) FROM foobar") 69 Expect(entriesInTable).To(Equal(strconv.Itoa(5))) 70 71 fullBackupTimestamp := gpbackup(gpbackupPath, backupHelperPath, 72 "--leaf-partition-data") 73 74 testhelper.AssertQueryRuns(backupConn, 75 "INSERT INTO foobar VALUES (1)") 76 77 // Ensure two distinct aoseg entries contain 'foobar' data 78 var numRows string 79 // For GPDB 7+, the gp_toolkit function returns the aoseg entries from the segments 80 numRows = dbconn.MustSelectString(backupConn, 81 "SELECT count(distinct(segno)) FROM gp_toolkit.__gp_aoseg('foobar'::regclass)") 82 Expect(numRows).To(Equal(strconv.Itoa(2))) 83 84 entriesInTable = dbconn.MustSelectString(backupConn, 85 "SELECT count(*) FROM foobar") 86 Expect(entriesInTable).To(Equal(strconv.Itoa(6))) 87 88 incremental1Timestamp := gpbackup(gpbackupPath, backupHelperPath, 89 "--incremental", 90 "--leaf-partition-data", 91 "--from-timestamp", fullBackupTimestamp) 92 93 gprestore(gprestorePath, restoreHelperPath, incremental1Timestamp, 94 "--redirect-db", "restoredb") 95 96 // The insertion should have been recorded in the incremental backup 97 entriesInTable = dbconn.MustSelectString(restoreConn, 98 "SELECT count(*) FROM foobar") 99 Expect(entriesInTable).To(Equal(strconv.Itoa(6))) 100 }) 101 It("can restore from an old backup with an incremental taken from new binaries with --include-table", func() { 102 if !useOldBackupVersion { 103 Skip("This test is only needed for old backup versions") 104 } 105 _ = gpbackup(gpbackupPath, backupHelperPath, 106 "--leaf-partition-data", 107 "--include-table=public.sales") 108 testhelper.AssertQueryRuns(backupConn, 109 "INSERT into sales values(1, '2017-01-01', 99.99)") 110 defer testhelper.AssertQueryRuns(backupConn, 111 "DELETE from sales where amt=99.99") 112 _ = gpbackup(gpbackupPath, backupHelperPath, 113 "--incremental", 114 "--leaf-partition-data", 115 "--include-table=public.sales") 116 117 gpbackupPathOld, backupHelperPathOld := gpbackupPath, backupHelperPath 118 gpbackupPath, backupHelperPath, _ = buildAndInstallBinaries() 119 120 testhelper.AssertQueryRuns(backupConn, 121 "INSERT into sales values(2, '2017-02-01', 88.88)") 122 defer testhelper.AssertQueryRuns(backupConn, 123 "DELETE from sales where amt=88.88") 124 incremental2Timestamp := gpbackup(gpbackupPath, backupHelperPath, 125 "--incremental", 126 "--leaf-partition-data", 127 "--include-table=public.sales") 128 gpbackupPath, backupHelperPath = gpbackupPathOld, backupHelperPathOld 129 130 gprestore(gprestorePath, restoreHelperPath, incremental2Timestamp, 131 "--redirect-db", "restoredb") 132 133 localTupleCounts := map[string]int{ 134 "public.sales": 15, 135 } 136 assertRelationsCreated(restoreConn, 13) 137 assertDataRestored(restoreConn, localTupleCounts) 138 }) 139 Context("Without a timestamp", func() { 140 It("restores from a incremental backup specified with a backup directory", func() { 141 _ = gpbackup(gpbackupPath, backupHelperPath, 142 "--leaf-partition-data", 143 "--backup-dir", backupDir) 144 145 testhelper.AssertQueryRuns(backupConn, 146 "INSERT into schema2.ao1 values(1001)") 147 defer testhelper.AssertQueryRuns(backupConn, 148 "DELETE from schema2.ao1 where i=1001") 149 _ = gpbackup(gpbackupPath, backupHelperPath, 150 "--incremental", 151 "--leaf-partition-data", 152 "--backup-dir", backupDir) 153 154 testhelper.AssertQueryRuns(backupConn, 155 "INSERT into schema2.ao1 values(1002)") 156 defer testhelper.AssertQueryRuns(backupConn, 157 "DELETE from schema2.ao1 where i=1002") 158 incremental2Timestamp := gpbackup(gpbackupPath, backupHelperPath, 159 "--incremental", 160 "--leaf-partition-data", 161 "--backup-dir", backupDir) 162 163 gprestore(gprestorePath, restoreHelperPath, incremental2Timestamp, 164 "--redirect-db", "restoredb", 165 "--backup-dir", backupDir) 166 167 assertRelationsCreated(restoreConn, TOTAL_RELATIONS) 168 assertDataRestored(restoreConn, publicSchemaTupleCounts) 169 schema2TupleCounts["schema2.ao1"] = 1002 170 assertDataRestored(restoreConn, schema2TupleCounts) 171 172 _ = os.Remove(backupDir) 173 }) 174 It("restores from --include filtered incremental backup with partition tables", func() { 175 _ = gpbackup(gpbackupPath, backupHelperPath, 176 "--leaf-partition-data", 177 "--include-table", "public.sales") 178 179 testhelper.AssertQueryRuns(backupConn, 180 "INSERT into sales VALUES(19, '2017-02-15'::date, 100)") 181 defer testhelper.AssertQueryRuns(backupConn, 182 "DELETE from sales where id=19") 183 _ = gpbackup(gpbackupPath, backupHelperPath, 184 "--incremental", 185 "--leaf-partition-data", 186 "--include-table", "public.sales") 187 188 testhelper.AssertQueryRuns(backupConn, 189 "INSERT into sales VALUES(20, '2017-03-15'::date, 100)") 190 defer testhelper.AssertQueryRuns(backupConn, 191 "DELETE from sales where id=20") 192 incremental2Timestamp := gpbackup(gpbackupPath, backupHelperPath, 193 "--incremental", 194 "--leaf-partition-data", 195 "--include-table", "public.sales") 196 gprestore(gprestorePath, restoreHelperPath, incremental2Timestamp, 197 "--redirect-db", "restoredb") 198 199 assertDataRestored(restoreConn, map[string]int{ 200 "public.sales": 15, 201 "public.sales_1_prt_feb17": 2, 202 "public.sales_1_prt_mar17": 2, 203 }) 204 }) 205 It("restores from --exclude filtered incremental backup with partition tables", func() { 206 skipIfOldBackupVersionBefore("1.18.0") 207 publicSchemaTupleCountsWithExclude := map[string]int{ 208 "public.foo": 40000, // holds is excluded and doesn't exist 209 "public.sales": 12, // 13 original - 1 for excluded partition 210 } 211 schema2TupleCountsWithExclude := map[string]int{ 212 "schema2.returns": 6, 213 "schema2.foo2": 0, 214 "schema2.foo3": 100, 215 "schema2.ao2": 1001, // +1 for new row, ao1 is excluded and doesn't exist 216 } 217 218 _ = gpbackup(gpbackupPath, backupHelperPath, 219 "--leaf-partition-data", 220 "--exclude-table", "public.holds", 221 "--exclude-table", "public.sales_1_prt_mar17", 222 "--exclude-table", "schema2.ao1") 223 224 testhelper.AssertQueryRuns(backupConn, 225 "INSERT into sales VALUES(20, '2017-03-15'::date, 100)") 226 defer testhelper.AssertQueryRuns(backupConn, 227 "DELETE from sales where id=20") 228 testhelper.AssertQueryRuns(backupConn, 229 "INSERT into schema2.ao1 values(1001)") 230 defer testhelper.AssertQueryRuns(backupConn, 231 "DELETE from schema2.ao1 where i=1001") 232 testhelper.AssertQueryRuns(backupConn, 233 "INSERT into schema2.ao2 values(1002)") 234 defer testhelper.AssertQueryRuns(backupConn, 235 "DELETE from schema2.ao2 where i=1002") 236 237 incremental1Timestamp := gpbackup(gpbackupPath, backupHelperPath, 238 "--incremental", 239 "--leaf-partition-data", 240 "--exclude-table", "public.holds", 241 "--exclude-table", "public.sales_1_prt_mar17", 242 "--exclude-table", "schema2.ao1") 243 244 gprestore(gprestorePath, restoreHelperPath, incremental1Timestamp, 245 "--redirect-db", "restoredb") 246 247 if false { 248 // -2 for public.holds and schema2.ao1, excluded partition will be included anyway but it's data - will not 249 assertRelationsCreated(restoreConn, TOTAL_RELATIONS-2) 250 } else { 251 // In GPDB 7+, the sales_1_prt_mar17 partition will not be created. 252 // TODO: Should the leaf partition actually be created when it has 253 // been excluded with the new GPDB partitioning logic? 254 assertRelationsCreated(restoreConn, TOTAL_RELATIONS-3) 255 } 256 assertDataRestored(restoreConn, publicSchemaTupleCountsWithExclude) 257 assertDataRestored(restoreConn, schema2TupleCountsWithExclude) 258 }) 259 It("restores from full incremental backup with partition tables with restore table filtering", func() { 260 skipIfOldBackupVersionBefore("1.7.2") 261 testhelper.AssertQueryRuns(backupConn, 262 "INSERT into sales VALUES(19, '2017-02-15'::date, 100)") 263 defer testhelper.AssertQueryRuns(backupConn, 264 "DELETE from sales where id=19") 265 _ = gpbackup(gpbackupPath, backupHelperPath, 266 "--leaf-partition-data") 267 268 incremental1Timestamp := gpbackup(gpbackupPath, backupHelperPath, 269 "--incremental", "--leaf-partition-data") 270 gprestore(gprestorePath, restoreHelperPath, incremental1Timestamp, 271 "--redirect-db", "restoredb", 272 "--include-table", "public.sales_1_prt_feb17") 273 274 assertDataRestored(restoreConn, map[string]int{ 275 "public.sales": 2, 276 "public.sales_1_prt_feb17": 2, 277 }) 278 }) 279 Context("old binaries", func() { 280 It("can restore from a backup with an incremental taken from new binaries", func() { 281 if !useOldBackupVersion { 282 Skip("This test is only needed for old backup versions") 283 } 284 _ = gpbackup(gpbackupPath, backupHelperPath, 285 "--leaf-partition-data") 286 287 testhelper.AssertQueryRuns(backupConn, 288 "INSERT into schema2.ao1 values(1001)") 289 defer testhelper.AssertQueryRuns(backupConn, 290 "DELETE from schema2.ao1 where i=1001") 291 _ = gpbackup(gpbackupPath, backupHelperPath, 292 "--incremental", 293 "--leaf-partition-data") 294 295 gpbackupPathOld, backupHelperPathOld := gpbackupPath, backupHelperPath 296 gpbackupPath, backupHelperPath, _ = buildAndInstallBinaries() 297 298 testhelper.AssertQueryRuns(backupConn, 299 "INSERT into schema2.ao1 values(1002)") 300 defer testhelper.AssertQueryRuns(backupConn, 301 "DELETE from schema2.ao1 where i=1002") 302 incremental2Timestamp := gpbackup(gpbackupPath, backupHelperPath, 303 "--incremental", 304 "--leaf-partition-data") 305 gpbackupPath, backupHelperPath = gpbackupPathOld, backupHelperPathOld 306 gprestore(gprestorePath, restoreHelperPath, incremental2Timestamp, 307 "--redirect-db", "restoredb") 308 309 assertRelationsCreated(restoreConn, TOTAL_RELATIONS) 310 assertDataRestored(restoreConn, publicSchemaTupleCounts) 311 schema2TupleCounts["schema2.ao1"] = 1002 312 assertDataRestored(restoreConn, schema2TupleCounts) 313 }) 314 }) 315 }) 316 Context("With a plugin", func() { 317 BeforeEach(func() { 318 // FIXME: we are temporarily disabling these tests because we will be altering our backwards compatibility logic. 319 if useOldBackupVersion { 320 Skip("This test is only needed for the most recent backup versions") 321 } 322 // pluginExecutablePath := fmt.Sprintf("%s/src/github.com/greenplum-db/gpbackup/plugins/example_plugin.bash", os.Getenv("GOPATH")) 323 // copyPluginToAllHosts(backupConn, pluginExecutablePath) 324 }) 325 It("Restores from an incremental backup based on a from-timestamp incremental", func() { 326 Skip("Cloudberry skip") 327 fullBackupTimestamp := gpbackup(gpbackupPath, backupHelperPath, 328 "--leaf-partition-data", 329 "--single-data-file", 330 "--plugin-config", pluginConfigPath) 331 forceMetadataFileDownloadFromPlugin(backupConn, fullBackupTimestamp) 332 testhelper.AssertQueryRuns(backupConn, 333 "INSERT into schema2.ao1 values(1001)") 334 defer testhelper.AssertQueryRuns(backupConn, 335 "DELETE from schema2.ao1 where i=1001") 336 incremental1Timestamp := gpbackup(gpbackupPath, backupHelperPath, 337 "--incremental", 338 "--leaf-partition-data", 339 "--single-data-file", 340 "--from-timestamp", fullBackupTimestamp, 341 "--plugin-config", pluginConfigPath) 342 forceMetadataFileDownloadFromPlugin(backupConn, incremental1Timestamp) 343 344 testhelper.AssertQueryRuns(backupConn, 345 "INSERT into schema2.ao1 values(1002)") 346 defer testhelper.AssertQueryRuns(backupConn, 347 "DELETE from schema2.ao1 where i=1002") 348 incremental2Timestamp := gpbackup(gpbackupPath, backupHelperPath, 349 "--incremental", 350 "--leaf-partition-data", 351 "--single-data-file", 352 "--plugin-config", pluginConfigPath) 353 forceMetadataFileDownloadFromPlugin(backupConn, incremental2Timestamp) 354 355 gprestore(gprestorePath, restoreHelperPath, incremental2Timestamp, 356 "--redirect-db", "restoredb", 357 "--plugin-config", pluginConfigPath) 358 359 assertRelationsCreated(restoreConn, TOTAL_RELATIONS) 360 assertDataRestored(restoreConn, publicSchemaTupleCounts) 361 schema2TupleCounts["schema2.ao1"] = 1002 362 assertDataRestored(restoreConn, schema2TupleCounts) 363 assertArtifactsCleaned(restoreConn, fullBackupTimestamp) 364 assertArtifactsCleaned(restoreConn, incremental1Timestamp) 365 assertArtifactsCleaned(restoreConn, incremental2Timestamp) 366 }) 367 It("Restores from an incremental backup based on a from-timestamp incremental with --copy-queue-size", func() { 368 Skip("Cloudberry skip") 369 fullBackupTimestamp := gpbackup(gpbackupPath, backupHelperPath, 370 "--leaf-partition-data", 371 "--single-data-file", 372 "--copy-queue-size", "4", 373 "--plugin-config", pluginConfigPath) 374 forceMetadataFileDownloadFromPlugin(backupConn, fullBackupTimestamp) 375 testhelper.AssertQueryRuns(backupConn, 376 "INSERT into schema2.ao1 values(1001)") 377 defer testhelper.AssertQueryRuns(backupConn, 378 "DELETE from schema2.ao1 where i=1001") 379 incremental1Timestamp := gpbackup(gpbackupPath, backupHelperPath, 380 "--incremental", 381 "--leaf-partition-data", 382 "--single-data-file", 383 "--copy-queue-size", "4", 384 "--from-timestamp", fullBackupTimestamp, 385 "--plugin-config", pluginConfigPath) 386 forceMetadataFileDownloadFromPlugin(backupConn, incremental1Timestamp) 387 388 testhelper.AssertQueryRuns(backupConn, 389 "INSERT into schema2.ao1 values(1002)") 390 defer testhelper.AssertQueryRuns(backupConn, 391 "DELETE from schema2.ao1 where i=1002") 392 incremental2Timestamp := gpbackup(gpbackupPath, backupHelperPath, 393 "--incremental", 394 "--leaf-partition-data", 395 "--single-data-file", 396 "--copy-queue-size", "4", 397 "--plugin-config", pluginConfigPath) 398 forceMetadataFileDownloadFromPlugin(backupConn, incremental2Timestamp) 399 400 gprestore(gprestorePath, restoreHelperPath, incremental2Timestamp, 401 "--redirect-db", "restoredb", 402 "--copy-queue-size", "4", 403 "--plugin-config", pluginConfigPath) 404 405 assertRelationsCreated(restoreConn, TOTAL_RELATIONS) 406 assertDataRestored(restoreConn, publicSchemaTupleCounts) 407 schema2TupleCounts["schema2.ao1"] = 1002 408 assertDataRestored(restoreConn, schema2TupleCounts) 409 assertArtifactsCleaned(restoreConn, fullBackupTimestamp) 410 assertArtifactsCleaned(restoreConn, incremental1Timestamp) 411 assertArtifactsCleaned(restoreConn, incremental2Timestamp) 412 }) 413 It("Runs backup and restore if plugin location changed", func() { 414 Skip("Cloudberry skip src/github.com/tuhaihe/gpbackup/plugins/example_plugin.bash"); 415 pluginExecutablePath := fmt.Sprintf("%s/src/github.com/tuhaihe/gpbackup/plugins/example_plugin.bash", os.Getenv("GOPATH")) 416 fullBackupTimestamp := gpbackup(gpbackupPath, backupHelperPath, 417 "--leaf-partition-data", 418 "--plugin-config", pluginConfigPath) 419 420 otherPluginExecutablePath := fmt.Sprintf("%s/other_plugin_location/example_plugin.bash", backupDir) 421 command := exec.Command("bash", "-c", fmt.Sprintf("mkdir %s/other_plugin_location && cp %s %s/other_plugin_location", backupDir, pluginExecutablePath, backupDir)) 422 mustRunCommand(command) 423 newCongig := fmt.Sprintf(`EOF1 424 executablepath: %s/other_plugin_location/example_plugin.bash 425 options: 426 password: unknown 427 EOF1`, backupDir) 428 otherPluginConfig := fmt.Sprintf("%s/other_plugin_location/example_plugin_config.yml", backupDir) 429 command = exec.Command("bash", "-c", fmt.Sprintf("cat > %s << %s", otherPluginConfig, newCongig)) 430 mustRunCommand(command) 431 432 copyPluginToAllHosts(backupConn, otherPluginExecutablePath) 433 434 incrementalBackupTimestamp := gpbackup(gpbackupPath, backupHelperPath, 435 "--leaf-partition-data", 436 "--incremental", 437 "--plugin-config", otherPluginConfig) 438 439 Expect(incrementalBackupTimestamp).NotTo(BeNil()) 440 441 gprestore(gprestorePath, restoreHelperPath, incrementalBackupTimestamp, 442 "--redirect-db", "restoredb", 443 "--plugin-config", otherPluginConfig) 444 445 assertRelationsCreated(restoreConn, TOTAL_RELATIONS) 446 assertDataRestored(restoreConn, publicSchemaTupleCounts) 447 assertArtifactsCleaned(restoreConn, fullBackupTimestamp) 448 assertArtifactsCleaned(restoreConn, incrementalBackupTimestamp) 449 }) 450 }) 451 }) 452 Describe("Incremental restore", func() { 453 var oldSchemaTupleCounts, newSchemaTupleCounts map[string]int 454 BeforeEach(func() { 455 skipIfOldBackupVersionBefore("1.16.0") 456 }) 457 Context("Simple incremental restore", func() { 458 It("Existing tables should be excluded from metadata restore", func() { 459 // Create a heap, ao, co, and external table and create a backup 460 testhelper.AssertQueryRuns(backupConn, 461 "DROP SCHEMA IF EXISTS testschema CASCADE; CREATE SCHEMA testschema;") 462 testhelper.AssertQueryRuns(backupConn, 463 "CREATE TABLE testschema.heap_table (a int);") 464 testhelper.AssertQueryRuns(backupConn, 465 "CREATE TABLE testschema.ao_table (a int) WITH (appendonly=true);") 466 testhelper.AssertQueryRuns(backupConn, 467 "CREATE TABLE testschema.co_table (a int) WITH (appendonly=true, orientation=column);") 468 testhelper.AssertQueryRuns(backupConn, 469 "CREATE EXTERNAL WEB TABLE testschema.external_table (a text) EXECUTE E'echo hi' FORMAT 'csv';") 470 backupTimestamp := gpbackup(gpbackupPath, backupHelperPath, "--leaf-partition-data") 471 472 // Restore the backup to a different database 473 testhelper.AssertQueryRuns(restoreConn, 474 "DROP SCHEMA IF EXISTS testschema CASCADE;") 475 gprestore(gprestorePath, restoreHelperPath, backupTimestamp, "--redirect-db", "restoredb") 476 477 // Trigger an incremental backup 478 testhelper.AssertQueryRuns(backupConn, 479 "INSERT INTO testschema.ao_table VALUES (1);") 480 incrementalBackupTimestamp := gpbackup(gpbackupPath, backupHelperPath, "--leaf-partition-data", "--incremental") 481 482 // Restore the incremental backup. We should see gprestore 483 // not error out due to already existing tables. 484 gprestore(gprestorePath, restoreHelperPath, incrementalBackupTimestamp, "--redirect-db", "restoredb", "--incremental", "--data-only") 485 486 // Cleanup 487 testhelper.AssertQueryRuns(backupConn, 488 "DROP SCHEMA IF EXISTS testschema CASCADE;") 489 testhelper.AssertQueryRuns(restoreConn, 490 "DROP SCHEMA IF EXISTS testschema CASCADE;") 491 }) 492 It("Does not try to restore postdata", func() { 493 testhelper.AssertQueryRuns(backupConn, 494 "CREATE TABLE zoo (a int) WITH (appendonly=true);") 495 testhelper.AssertQueryRuns(backupConn, 496 "CREATE INDEX fooidx ON zoo USING btree(a);") 497 backupTimestamp := gpbackup(gpbackupPath, backupHelperPath, "--leaf-partition-data") 498 testhelper.AssertQueryRuns(backupConn, 499 "INSERT INTO zoo VALUES (1);") 500 incrementalBackupTimestamp := gpbackup(gpbackupPath, backupHelperPath, "--leaf-partition-data", "--incremental") 501 gprestore(gprestorePath, restoreHelperPath, backupTimestamp, "--redirect-db", "restoredb") 502 gprestore(gprestorePath, restoreHelperPath, incrementalBackupTimestamp, "--redirect-db", "restoredb", "--incremental", "--data-only") 503 504 // Cleanup 505 testhelper.AssertQueryRuns(backupConn, 506 "DROP TABLE IF EXISTS zoo;") 507 testhelper.AssertQueryRuns(restoreConn, 508 "DROP TABLE IF EXISTS zoo;") 509 }) 510 It("Does not incremental restore without --data-only", func() { 511 args := []string{ 512 "--timestamp", "23432432", 513 "--incremental", 514 "--redirect-db", "restoredb"} 515 cmd := exec.Command(gprestorePath, args...) 516 output, err := cmd.CombinedOutput() 517 Expect(err).To(HaveOccurred()) 518 Expect(string(output)).To(ContainSubstring("Cannot use --incremental without --data-only")) 519 }) 520 It("Does not incremental restore with --metadata-only", func() { 521 args := []string{ 522 "--timestamp", "23432432", 523 "--incremental", "--metadata-only", 524 "--redirect-db", "restoredb"} 525 cmd := exec.Command(gprestorePath, args...) 526 output, err := cmd.CombinedOutput() 527 Expect(err).To(HaveOccurred()) 528 Expect(string(output)).To(ContainSubstring( 529 "The following flags may not be specified together: truncate-table, metadata-only, incremental")) 530 }) 531 }) 532 Context("No DDL no partitioning", func() { 533 BeforeEach(func() { 534 testhelper.AssertQueryRuns(backupConn, 535 "DROP SCHEMA IF EXISTS new_schema CASCADE; DROP SCHEMA IF EXISTS old_schema CASCADE; CREATE SCHEMA old_schema;") 536 testhelper.AssertQueryRuns(backupConn, 537 "CREATE TABLE old_schema.old_table0 (mydata int) WITH (appendonly=true) DISTRIBUTED BY (mydata);") 538 testhelper.AssertQueryRuns(backupConn, 539 "CREATE TABLE old_schema.old_table1 (mydata int) WITH (appendonly=true) DISTRIBUTED BY (mydata);") 540 testhelper.AssertQueryRuns(backupConn, 541 "CREATE TABLE old_schema.old_table2 (mydata int) WITH (appendonly=true) DISTRIBUTED BY (mydata);") 542 testhelper.AssertQueryRuns(backupConn, 543 "INSERT INTO old_schema.old_table0 SELECT generate_series(1, 5);") 544 testhelper.AssertQueryRuns(backupConn, 545 "INSERT INTO old_schema.old_table1 SELECT generate_series(1, 10);") 546 testhelper.AssertQueryRuns(backupConn, 547 "INSERT INTO old_schema.old_table2 SELECT generate_series(1, 15);") 548 549 oldSchemaTupleCounts = map[string]int{ 550 "old_schema.old_table0": 5, 551 "old_schema.old_table1": 10, 552 "old_schema.old_table2": 15, 553 } 554 newSchemaTupleCounts = map[string]int{} 555 baseTimestamp := gpbackup(gpbackupPath, backupHelperPath, 556 "--leaf-partition-data") 557 558 testhelper.AssertQueryRuns(restoreConn, 559 "DROP SCHEMA IF EXISTS new_schema CASCADE; DROP SCHEMA IF EXISTS old_schema CASCADE;") 560 gprestore(gprestorePath, restoreHelperPath, baseTimestamp, 561 "--redirect-db", "restoredb") 562 }) 563 AfterEach(func() { 564 testhelper.AssertQueryRuns(backupConn, 565 "DROP SCHEMA IF EXISTS new_schema CASCADE; DROP SCHEMA IF EXISTS old_schema CASCADE;") 566 testhelper.AssertQueryRuns(restoreConn, 567 "DROP SCHEMA IF EXISTS new_schema CASCADE; DROP SCHEMA IF EXISTS old_schema CASCADE;") 568 }) 569 Context("Include/Exclude schemas and tables", func() { 570 var timestamp string 571 BeforeEach(func() { 572 testhelper.AssertQueryRuns(backupConn, 573 "CREATE SCHEMA new_schema;") 574 testhelper.AssertQueryRuns(backupConn, 575 "CREATE TABLE new_schema.new_table1 (mydata int) WITH (appendonly=true) DISTRIBUTED BY (mydata);") 576 testhelper.AssertQueryRuns(backupConn, 577 "CREATE TABLE new_schema.new_table2 (mydata int) WITH (appendonly=true) DISTRIBUTED BY (mydata);") 578 testhelper.AssertQueryRuns(backupConn, 579 "INSERT INTO new_schema.new_table1 SELECT generate_series(1, 30);") 580 testhelper.AssertQueryRuns(backupConn, 581 "INSERT INTO new_schema.new_table2 SELECT generate_series(1, 35);") 582 testhelper.AssertQueryRuns(backupConn, 583 "INSERT INTO old_schema.old_table1 SELECT generate_series(11, 20);") 584 timestamp = gpbackup(gpbackupPath, backupHelperPath, 585 "--leaf-partition-data", "--incremental") 586 }) 587 AfterEach(func() { 588 testhelper.AssertQueryRuns(backupConn, 589 "DROP SCHEMA IF EXISTS new_schema CASCADE;") 590 testhelper.AssertQueryRuns(restoreConn, 591 "DROP SCHEMA IF EXISTS new_schema CASCADE;") 592 testhelper.AssertQueryRuns(backupConn, 593 "DELETE FROM old_schema.old_table1 where mydata>10;") 594 oldSchemaTupleCounts = map[string]int{} 595 newSchemaTupleCounts = map[string]int{} 596 assertArtifactsCleaned(restoreConn, timestamp) 597 }) 598 It("Restores only tables included by use if user input is provided", func() { 599 gprestore(gprestorePath, restoreHelperPath, timestamp, 600 "--incremental", "--data-only", 601 "--include-table", "old_schema.old_table1", 602 "--redirect-db", "restoredb") 603 oldSchemaTupleCounts["old_schema.old_table1"] = 20 604 // new_schema should not be present 605 assertSchemasExist(restoreConn, 4) 606 assertRelationsExistForIncremental(restoreConn, 3) 607 assertDataRestored(restoreConn, oldSchemaTupleCounts) 608 assertDataRestored(restoreConn, newSchemaTupleCounts) 609 }) 610 It("Does not restore tables excluded by user if user input is provided", func() { 611 gprestore(gprestorePath, restoreHelperPath, timestamp, 612 "--incremental", "--data-only", 613 "--exclude-table", "new_schema.new_table1", 614 "--exclude-table", "new_schema.new_table2", 615 "--redirect-db", "restoredb") 616 oldSchemaTupleCounts["old_schema.old_table1"] = 20 617 // new_schema should not be present 618 assertSchemasExist(restoreConn, 4) 619 assertRelationsExistForIncremental(restoreConn, 3) 620 assertDataRestored(restoreConn, oldSchemaTupleCounts) 621 assertDataRestored(restoreConn, newSchemaTupleCounts) 622 }) 623 It("Restores only schemas included by user if user input is provided", func() { 624 gprestore(gprestorePath, restoreHelperPath, timestamp, 625 "--incremental", "--data-only", 626 "--include-schema", "old_schema", 627 "--redirect-db", "restoredb") 628 oldSchemaTupleCounts["old_schema.old_table1"] = 20 629 assertSchemasExist(restoreConn, 4) 630 assertRelationsExistForIncremental(restoreConn, 3) 631 assertDataRestored(restoreConn, oldSchemaTupleCounts) 632 assertDataRestored(restoreConn, newSchemaTupleCounts) 633 }) 634 It("Does not restore schemas excluded by user if user input is provided", func() { 635 gprestore(gprestorePath, restoreHelperPath, timestamp, 636 "--incremental", "--data-only", 637 "--exclude-schema", "new_schema", 638 "--redirect-db", "restoredb") 639 oldSchemaTupleCounts["old_schema.old_table1"] = 20 640 assertSchemasExist(restoreConn, 4) 641 assertRelationsExistForIncremental(restoreConn, 3) 642 assertDataRestored(restoreConn, oldSchemaTupleCounts) 643 assertDataRestored(restoreConn, newSchemaTupleCounts) 644 }) 645 }) 646 Context("New tables and schemas", func() { 647 var timestamp string 648 BeforeEach(func() { 649 testhelper.AssertQueryRuns(backupConn, 650 "CREATE SCHEMA new_schema;") 651 testhelper.AssertQueryRuns(backupConn, 652 "CREATE TABLE new_schema.new_table1 (mydata int) WITH (appendonly=true) DISTRIBUTED BY (mydata);") 653 testhelper.AssertQueryRuns(backupConn, 654 "INSERT INTO new_schema.new_table1 SELECT generate_series(1, 30);") 655 testhelper.AssertQueryRuns(backupConn, 656 "CREATE TABLE old_schema.new_table1 (mydata int) WITH (appendonly=true) DISTRIBUTED BY (mydata);") 657 testhelper.AssertQueryRuns(backupConn, 658 "INSERT INTO old_schema.new_table1 SELECT generate_series(1, 20);") 659 testhelper.AssertQueryRuns(backupConn, 660 "INSERT INTO new_schema.new_table1 SELECT generate_series(1, 25);") 661 testhelper.AssertQueryRuns(backupConn, 662 "INSERT INTO old_schema.old_table1 SELECT generate_series(11, 20);") 663 timestamp = gpbackup(gpbackupPath, backupHelperPath, 664 "--leaf-partition-data", "--incremental") 665 }) 666 AfterEach(func() { 667 testhelper.AssertQueryRuns(backupConn, 668 "DROP TABLE IF EXISTS old_schema.new_table1 CASCADE;") 669 testhelper.AssertQueryRuns(backupConn, 670 "DROP TABLE IF EXISTS old_schema.new_table2 CASCADE;") 671 testhelper.AssertQueryRuns(restoreConn, 672 "DROP TABLE IF EXISTS old_schema.new_table1 CASCADE;") 673 testhelper.AssertQueryRuns(restoreConn, 674 "DROP TABLE IF EXISTS old_schema.new_table2 CASCADE;") 675 testhelper.AssertQueryRuns(backupConn, 676 "DELETE FROM old_schema.old_table1 where mydata>10;") 677 testhelper.AssertQueryRuns(backupConn, 678 "DROP SCHEMA IF EXISTS new_schema CASCADE;") 679 testhelper.AssertQueryRuns(restoreConn, 680 "DROP SCHEMA IF EXISTS new_schema CASCADE;") 681 oldSchemaTupleCounts = map[string]int{} 682 newSchemaTupleCounts = map[string]int{} 683 assertArtifactsCleaned(restoreConn, timestamp) 684 }) 685 It("Does not restore old/new tables and exits gracefully", func() { 686 args := []string{ 687 "--timestamp", timestamp, 688 "--incremental", "--data-only", 689 "--redirect-db", "restoredb"} 690 cmd := exec.Command(gprestorePath, args...) 691 output, err := cmd.CombinedOutput() 692 Expect(err).To(HaveOccurred()) 693 Expect(string(output)).To(ContainSubstring( 694 "objects are missing from the target database: " + 695 "[new_schema new_schema.new_table1 old_schema.new_table1]")) 696 assertRelationsExistForIncremental(restoreConn, 3) 697 assertDataRestored(restoreConn, oldSchemaTupleCounts) 698 assertDataRestored(restoreConn, newSchemaTupleCounts) 699 }) 700 It("Only restores existing tables if --on-error-continue is specified", func() { 701 args := []string{ 702 "--timestamp", timestamp, 703 "--incremental", "--data-only", 704 "--on-error-continue", 705 "--redirect-db", "restoredb"} 706 cmd := exec.Command(gprestorePath, args...) 707 _, err := cmd.CombinedOutput() 708 Expect(err).To(HaveOccurred()) 709 oldSchemaTupleCounts["old_schema.old_table1"] = 20 710 assertRelationsExistForIncremental(restoreConn, 3) 711 assertDataRestored(restoreConn, oldSchemaTupleCounts) 712 assertDataRestored(restoreConn, newSchemaTupleCounts) 713 }) 714 }) 715 Context("Existing tables in existing schemas were updated", func() { 716 var timestamp string 717 BeforeEach(func() { 718 testhelper.AssertQueryRuns(backupConn, 719 "INSERT INTO old_schema.old_table1 SELECT generate_series(11, 20);") 720 testhelper.AssertQueryRuns(backupConn, 721 "INSERT INTO old_schema.old_table2 SELECT generate_series(16, 30);") 722 timestamp = gpbackup(gpbackupPath, backupHelperPath, 723 "--leaf-partition-data", "--incremental") 724 }) 725 AfterEach(func() { 726 testhelper.AssertQueryRuns(backupConn, 727 "DELETE FROM old_schema.old_table1 where mydata>10;") 728 testhelper.AssertQueryRuns(backupConn, 729 "DELETE FROM old_schema.old_table2 where mydata>15;") 730 oldSchemaTupleCounts["old_schema.old_table1"] = 10 731 oldSchemaTupleCounts["old_schema.old_table2"] = 15 732 testhelper.AssertQueryRuns(restoreConn, 733 "DELETE FROM old_schema.old_table1 where mydata>10;") 734 testhelper.AssertQueryRuns(restoreConn, 735 "DELETE FROM old_schema.old_table2 where mydata>15;") 736 assertArtifactsCleaned(restoreConn, timestamp) 737 }) 738 It("Updates data in existing tables", func() { 739 gprestore(gprestorePath, restoreHelperPath, timestamp, 740 "--incremental", "--data-only", 741 "--redirect-db", "restoredb") 742 oldSchemaTupleCounts["old_schema.old_table1"] = 20 743 oldSchemaTupleCounts["old_schema.old_table2"] = 30 744 assertSchemasExist(restoreConn, 4) 745 assertRelationsExistForIncremental(restoreConn, 3) 746 assertDataRestored(restoreConn, oldSchemaTupleCounts) 747 assertDataRestored(restoreConn, newSchemaTupleCounts) 748 }) 749 It("Updates only tables included by user if user input is provided", func() { 750 gprestore(gprestorePath, restoreHelperPath, timestamp, 751 "--incremental", "--data-only", 752 "--include-table", "old_schema.old_table1", 753 "--redirect-db", "restoredb") 754 oldSchemaTupleCounts["old_schema.old_table1"] = 20 755 assertSchemasExist(restoreConn, 4) 756 assertRelationsExistForIncremental(restoreConn, 3) 757 assertDataRestored(restoreConn, oldSchemaTupleCounts) 758 assertDataRestored(restoreConn, newSchemaTupleCounts) 759 }) 760 It("Does not update tables excluded by user if user input is provided", func() { 761 gprestore(gprestorePath, restoreHelperPath, timestamp, 762 "--incremental", "--data-only", 763 "--exclude-table", "old_schema.old_table1", 764 "--redirect-db", "restoredb") 765 oldSchemaTupleCounts["old_schema.old_table2"] = 30 766 assertSchemasExist(restoreConn, 4) 767 assertRelationsExistForIncremental(restoreConn, 3) 768 assertDataRestored(restoreConn, oldSchemaTupleCounts) 769 assertDataRestored(restoreConn, newSchemaTupleCounts) 770 }) 771 It("Does not update anything if user excluded all tables", func() { 772 gprestore(gprestorePath, restoreHelperPath, timestamp, 773 "--incremental", "--data-only", 774 "--exclude-table", "old_schema.old_table1", 775 "--exclude-table", "old_schema.old_table2", 776 "--redirect-db", "restoredb") 777 assertSchemasExist(restoreConn, 4) 778 assertRelationsExistForIncremental(restoreConn, 3) 779 assertDataRestored(restoreConn, oldSchemaTupleCounts) 780 assertDataRestored(restoreConn, newSchemaTupleCounts) 781 }) 782 It("Does not update tables if user input is provided and schema is not included", func() { 783 gprestore(gprestorePath, restoreHelperPath, timestamp, 784 "--incremental", "--data-only", 785 "--include-schema", "public", 786 "--redirect-db", "restoredb") 787 assertSchemasExist(restoreConn, 4) 788 assertRelationsExistForIncremental(restoreConn, 3) 789 assertDataRestored(restoreConn, oldSchemaTupleCounts) 790 assertDataRestored(restoreConn, newSchemaTupleCounts) 791 }) 792 It("Does not restore tables if user input is provide and schema is excluded by user", func() { 793 gprestore(gprestorePath, restoreHelperPath, timestamp, 794 "--incremental", "--data-only", 795 "--exclude-schema", "old_schema", 796 "--redirect-db", "restoredb") 797 assertSchemasExist(restoreConn, 4) 798 assertRelationsExistForIncremental(restoreConn, 3) 799 assertDataRestored(restoreConn, oldSchemaTupleCounts) 800 assertDataRestored(restoreConn, newSchemaTupleCounts) 801 }) 802 }) 803 }) 804 }) 805 })