github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/sql/sem/tree/backup.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package tree 12 13 import ( 14 "github.com/cockroachdb/errors" 15 "github.com/google/go-cmp/cmp" 16 ) 17 18 // DescriptorCoverage specifies the subset of descriptors that are requested during a backup 19 // or a restore. 20 type DescriptorCoverage int32 21 22 const ( 23 // RequestedDescriptors table coverage means that the backup is not 24 // guaranteed to have all of the cluster data. This can be accomplished by 25 // backing up a specific subset of tables/databases. Note that even if all 26 // of the tables and databases have been included in the backup manually, a 27 // backup is not said to have complete table coverage unless it was created 28 // by a `BACKUP TO` command. 29 RequestedDescriptors DescriptorCoverage = iota 30 31 // AllDescriptors table coverage means that backup is guaranteed to have all the 32 // relevant data in the cluster. These can only be created by running a 33 // full cluster backup with `BACKUP TO`. 34 AllDescriptors 35 36 // SystemUsers coverage indicates that only the system.users 37 // table will be restored from the backup. 38 SystemUsers 39 ) 40 41 // BackupOptions describes options for the BACKUP execution. 42 type BackupOptions struct { 43 CaptureRevisionHistory Expr 44 IncludeAllSecondaryTenants Expr 45 EncryptionPassphrase Expr 46 Detached *DBool 47 EncryptionKMSURI StringOrPlaceholderOptList 48 IncrementalStorage StringOrPlaceholderOptList 49 ExecutionLocality Expr 50 UpdatesClusterMonitoringMetrics Expr 51 } 52 53 var _ NodeFormatter = &BackupOptions{} 54 55 // Backup represents a BACKUP statement. 56 type Backup struct { 57 Targets *BackupTargetList 58 59 // To is set to the root directory of the backup (called the <destination> in 60 // the docs). 61 To StringOrPlaceholderOptList 62 63 // IncrementalFrom is only set for the old 'BACKUP .... TO ...' syntax. 64 IncrementalFrom Exprs 65 66 AsOf AsOfClause 67 Options BackupOptions 68 69 // Nested is set to true when the user creates a backup with 70 //`BACKUP ... INTO... ` syntax. 71 Nested bool 72 73 // AppendToLatest is set to true if the user creates a backup with 74 //`BACKUP...INTO LATEST...` 75 AppendToLatest bool 76 77 // Subdir may be set by the parser when the SQL query is of the form `BACKUP 78 // INTO 'subdir' IN...`. Alternatively, if Nested is true but a subdir was not 79 // explicitly specified by the user, then this will be set during BACKUP 80 // planning once the destination has been resolved. 81 Subdir Expr 82 } 83 84 var _ Statement = &Backup{} 85 86 // Format implements the NodeFormatter interface. 87 func (node *Backup) Format(ctx *FmtCtx) { 88 ctx.WriteString("BACKUP ") 89 if node.Targets != nil { 90 ctx.FormatNode(node.Targets) 91 ctx.WriteString(" ") 92 } 93 if node.Nested { 94 ctx.WriteString("INTO ") 95 if node.Subdir != nil { 96 ctx.FormatNode(node.Subdir) 97 ctx.WriteString(" IN ") 98 } else if node.AppendToLatest { 99 ctx.WriteString("LATEST IN ") 100 } 101 } else { 102 ctx.WriteString("TO ") 103 } 104 ctx.FormatNode(&node.To) 105 if node.AsOf.Expr != nil { 106 ctx.WriteString(" ") 107 ctx.FormatNode(&node.AsOf) 108 } 109 if node.IncrementalFrom != nil { 110 ctx.WriteString(" INCREMENTAL FROM ") 111 ctx.FormatNode(&node.IncrementalFrom) 112 } 113 114 if !node.Options.IsDefault() { 115 ctx.WriteString(" WITH OPTIONS (") 116 ctx.FormatNode(&node.Options) 117 ctx.WriteString(")") 118 } 119 } 120 121 // Coverage return the coverage (all vs requested). 122 func (node Backup) Coverage() DescriptorCoverage { 123 if node.Targets == nil { 124 return AllDescriptors 125 } 126 return RequestedDescriptors 127 } 128 129 // RestoreOptions describes options for the RESTORE execution. 130 type RestoreOptions struct { 131 EncryptionPassphrase Expr 132 DecryptionKMSURI StringOrPlaceholderOptList 133 IntoDB Expr 134 SkipMissingFKs bool 135 SkipMissingSequences bool 136 SkipMissingSequenceOwners bool 137 SkipMissingViews bool 138 SkipMissingUDFs bool 139 Detached bool 140 SkipLocalitiesCheck bool 141 DebugPauseOn Expr 142 NewDBName Expr 143 IncludeAllSecondaryTenants Expr 144 IncrementalStorage StringOrPlaceholderOptList 145 AsTenant Expr 146 ForceTenantID Expr 147 SchemaOnly bool 148 VerifyData bool 149 UnsafeRestoreIncompatibleVersion bool 150 ExecutionLocality Expr 151 ExperimentalOnline bool 152 RemoveRegions bool 153 } 154 155 var _ NodeFormatter = &RestoreOptions{} 156 157 // Restore represents a RESTORE statement. 158 type Restore struct { 159 Targets BackupTargetList 160 DescriptorCoverage DescriptorCoverage 161 162 // From contains the URIs for the backup(s) we seek to restore. 163 // - len(From)>1 implies the user explicitly passed incremental backup paths, 164 // which is only allowed using the old syntax, `RESTORE <targets> FROM <destination>. 165 // In this case, From[0] contains the URI(s) for the full backup. 166 // - len(From)==1 implies we'll have to look for incremental backups in planning 167 // - len(From[0]) > 1 implies the backups are locality aware 168 // - From[i][0] must be the default locality. 169 From []StringOrPlaceholderOptList 170 AsOf AsOfClause 171 Options RestoreOptions 172 173 // Subdir may be set by the parser when the SQL query is of the form `RESTORE 174 // ... FROM 'from' IN 'subdir'...`. Alternatively, restore_planning.go will set 175 // it for the query `RESTORE ... FROM 'from' IN LATEST...` 176 Subdir Expr 177 } 178 179 var _ Statement = &Restore{} 180 181 // Format implements the NodeFormatter interface. 182 func (node *Restore) Format(ctx *FmtCtx) { 183 ctx.WriteString("RESTORE ") 184 if node.DescriptorCoverage == RequestedDescriptors { 185 ctx.FormatNode(&node.Targets) 186 ctx.WriteString(" ") 187 } 188 ctx.WriteString("FROM ") 189 if node.Subdir != nil { 190 ctx.FormatNode(node.Subdir) 191 ctx.WriteString(" IN ") 192 } 193 for i := range node.From { 194 if i > 0 { 195 ctx.WriteString(", ") 196 } 197 ctx.FormatNode(&node.From[i]) 198 } 199 if node.AsOf.Expr != nil { 200 ctx.WriteString(" ") 201 ctx.FormatNode(&node.AsOf) 202 } 203 if !node.Options.IsDefault() { 204 if ctx.HasFlags(FmtHideConstants) { 205 ctx.WriteString(" WITH OPTIONS (") 206 ctx.FormatNode(&node.Options) 207 ctx.WriteString(")") 208 } else { 209 ctx.WriteString(" WITH OPTIONS (") 210 ctx.FormatNode(&node.Options) 211 ctx.WriteString(")") 212 } 213 } 214 } 215 216 // KVOption is a key-value option. 217 type KVOption struct { 218 Key Name 219 Value Expr 220 } 221 222 // KVOptions is a list of KVOptions. 223 type KVOptions []KVOption 224 225 // HasKey searches the set of options to discover if it has key. 226 func (o *KVOptions) HasKey(key Name) bool { 227 for _, kv := range *o { 228 if kv.Key == key { 229 return true 230 } 231 } 232 return false 233 } 234 235 // Format implements the NodeFormatter interface. 236 func (o *KVOptions) Format(ctx *FmtCtx) { 237 for i := range *o { 238 n := &(*o)[i] 239 if i > 0 { 240 ctx.WriteString(", ") 241 } 242 // KVOption Key values never contain PII and should be distinguished 243 // for feature tracking purposes. 244 ctx.WithFlags(ctx.flags&^FmtMarkRedactionNode, func() { 245 ctx.FormatNode(&n.Key) 246 }) 247 if n.Value != nil { 248 ctx.WriteString(` = `) 249 ctx.FormatNode(n.Value) 250 } 251 } 252 } 253 254 // StringOrPlaceholderOptList is a list of strings or placeholders. 255 type StringOrPlaceholderOptList []Expr 256 257 // Format implements the NodeFormatter interface. 258 func (node *StringOrPlaceholderOptList) Format(ctx *FmtCtx) { 259 if len(*node) > 1 { 260 ctx.WriteString("(") 261 } 262 ctx.FormatNode((*Exprs)(node)) 263 if len(*node) > 1 { 264 ctx.WriteString(")") 265 } 266 } 267 268 // Format implements the NodeFormatter interface 269 func (o *BackupOptions) Format(ctx *FmtCtx) { 270 var addSep bool 271 maybeAddSep := func() { 272 if addSep { 273 ctx.WriteString(", ") 274 } 275 addSep = true 276 } 277 if o.CaptureRevisionHistory != nil { 278 ctx.WriteString("revision_history = ") 279 ctx.FormatNode(o.CaptureRevisionHistory) 280 addSep = true 281 } 282 283 if o.EncryptionPassphrase != nil { 284 maybeAddSep() 285 ctx.WriteString("encryption_passphrase = ") 286 if ctx.flags.HasFlags(FmtShowPasswords) { 287 ctx.FormatNode(o.EncryptionPassphrase) 288 } else { 289 ctx.WriteString(PasswordSubstitution) 290 } 291 } 292 293 if o.Detached != nil { 294 maybeAddSep() 295 ctx.WriteString("detached") 296 if o.Detached != DBoolTrue { 297 ctx.WriteString(" = FALSE") 298 } 299 } 300 301 if o.EncryptionKMSURI != nil { 302 maybeAddSep() 303 ctx.WriteString("kms = ") 304 ctx.FormatNode(&o.EncryptionKMSURI) 305 } 306 307 if o.IncrementalStorage != nil { 308 maybeAddSep() 309 ctx.WriteString("incremental_location = ") 310 ctx.FormatNode(&o.IncrementalStorage) 311 } 312 313 if o.ExecutionLocality != nil { 314 maybeAddSep() 315 ctx.WriteString("execution locality = ") 316 ctx.FormatNode(o.ExecutionLocality) 317 } 318 319 if o.IncludeAllSecondaryTenants != nil { 320 maybeAddSep() 321 ctx.WriteString("include_all_virtual_clusters = ") 322 ctx.FormatNode(o.IncludeAllSecondaryTenants) 323 } 324 325 if o.UpdatesClusterMonitoringMetrics != nil { 326 maybeAddSep() 327 ctx.WriteString("updates_cluster_monitoring_metrics = ") 328 ctx.FormatNode(o.UpdatesClusterMonitoringMetrics) 329 } 330 } 331 332 // CombineWith merges other backup options into this backup options struct. 333 // An error is returned if the same option merged multiple times. 334 func (o *BackupOptions) CombineWith(other *BackupOptions) error { 335 if o.CaptureRevisionHistory != nil { 336 if other.CaptureRevisionHistory != nil { 337 return errors.New("revision_history option specified multiple times") 338 } 339 } else { 340 o.CaptureRevisionHistory = other.CaptureRevisionHistory 341 } 342 343 if o.EncryptionPassphrase == nil { 344 o.EncryptionPassphrase = other.EncryptionPassphrase 345 } else if other.EncryptionPassphrase != nil { 346 return errors.New("encryption_passphrase specified multiple times") 347 } 348 349 if o.Detached != nil { 350 if other.Detached != nil { 351 return errors.New("detached option specified multiple times") 352 } 353 } else { 354 o.Detached = other.Detached 355 } 356 357 if o.EncryptionKMSURI == nil { 358 o.EncryptionKMSURI = other.EncryptionKMSURI 359 } else if other.EncryptionKMSURI != nil { 360 return errors.New("kms specified multiple times") 361 } 362 363 if o.IncrementalStorage == nil { 364 o.IncrementalStorage = other.IncrementalStorage 365 } else if other.IncrementalStorage != nil { 366 return errors.New("incremental_location option specified multiple times") 367 } 368 369 if o.ExecutionLocality == nil { 370 o.ExecutionLocality = other.ExecutionLocality 371 } else if other.ExecutionLocality != nil { 372 return errors.New("execution locality option specified multiple times") 373 } 374 375 if o.IncludeAllSecondaryTenants != nil { 376 if other.IncludeAllSecondaryTenants != nil { 377 return errors.New("include_all_virtual_clusters specified multiple times") 378 } 379 } else { 380 o.IncludeAllSecondaryTenants = other.IncludeAllSecondaryTenants 381 } 382 383 if o.UpdatesClusterMonitoringMetrics != nil { 384 if other.UpdatesClusterMonitoringMetrics != nil { 385 return errors.New("updates_cluster_monitoring_metrics option specified multiple times") 386 } 387 } else { 388 o.UpdatesClusterMonitoringMetrics = other.UpdatesClusterMonitoringMetrics 389 } 390 return nil 391 } 392 393 // IsDefault returns true if this backup options struct has default value. 394 func (o BackupOptions) IsDefault() bool { 395 options := BackupOptions{} 396 return o.CaptureRevisionHistory == options.CaptureRevisionHistory && 397 (o.Detached == nil || o.Detached == DBoolFalse) && 398 cmp.Equal(o.EncryptionKMSURI, options.EncryptionKMSURI) && 399 o.EncryptionPassphrase == options.EncryptionPassphrase && 400 cmp.Equal(o.IncrementalStorage, options.IncrementalStorage) && 401 o.ExecutionLocality == options.ExecutionLocality && 402 o.IncludeAllSecondaryTenants == options.IncludeAllSecondaryTenants && 403 o.UpdatesClusterMonitoringMetrics == options.UpdatesClusterMonitoringMetrics 404 } 405 406 // Format implements the NodeFormatter interface. 407 func (o *RestoreOptions) Format(ctx *FmtCtx) { 408 var addSep bool 409 maybeAddSep := func() { 410 if addSep { 411 ctx.WriteString(", ") 412 } 413 addSep = true 414 } 415 if o.EncryptionPassphrase != nil { 416 addSep = true 417 ctx.WriteString("encryption_passphrase = ") 418 if ctx.flags.HasFlags(FmtShowPasswords) { 419 ctx.FormatNode(o.EncryptionPassphrase) 420 } else { 421 ctx.WriteString(PasswordSubstitution) 422 } 423 } 424 425 if o.DecryptionKMSURI != nil { 426 maybeAddSep() 427 ctx.WriteString("kms = ") 428 ctx.FormatNode(&o.DecryptionKMSURI) 429 } 430 431 if o.IntoDB != nil { 432 maybeAddSep() 433 ctx.WriteString("into_db = ") 434 ctx.FormatNode(o.IntoDB) 435 } 436 437 if o.DebugPauseOn != nil { 438 maybeAddSep() 439 ctx.WriteString("debug_pause_on = ") 440 ctx.FormatNode(o.DebugPauseOn) 441 } 442 443 if o.SkipMissingFKs { 444 maybeAddSep() 445 ctx.WriteString("skip_missing_foreign_keys") 446 } 447 448 if o.SkipMissingSequenceOwners { 449 maybeAddSep() 450 ctx.WriteString("skip_missing_sequence_owners") 451 } 452 453 if o.SkipMissingSequences { 454 maybeAddSep() 455 ctx.WriteString("skip_missing_sequences") 456 } 457 458 if o.SkipMissingViews { 459 maybeAddSep() 460 ctx.WriteString("skip_missing_views") 461 } 462 463 if o.SkipMissingUDFs { 464 maybeAddSep() 465 ctx.WriteString("skip_missing_udfs") 466 } 467 468 if o.Detached { 469 maybeAddSep() 470 ctx.WriteString("detached") 471 } 472 473 if o.SkipLocalitiesCheck { 474 maybeAddSep() 475 ctx.WriteString("skip_localities_check") 476 } 477 478 if o.NewDBName != nil { 479 maybeAddSep() 480 ctx.WriteString("new_db_name = ") 481 ctx.FormatNode(o.NewDBName) 482 } 483 484 if o.IncludeAllSecondaryTenants != nil { 485 maybeAddSep() 486 ctx.WriteString("include_all_virtual_clusters = ") 487 ctx.FormatNode(o.IncludeAllSecondaryTenants) 488 } 489 490 if o.IncrementalStorage != nil { 491 maybeAddSep() 492 ctx.WriteString("incremental_location = ") 493 ctx.FormatNode(&o.IncrementalStorage) 494 } 495 496 if o.AsTenant != nil { 497 maybeAddSep() 498 ctx.WriteString("virtual_cluster_name = ") 499 ctx.FormatNode(o.AsTenant) 500 } 501 502 if o.ForceTenantID != nil { 503 maybeAddSep() 504 ctx.WriteString("virtual_cluster = ") 505 ctx.FormatNode(o.ForceTenantID) 506 } 507 508 if o.SchemaOnly { 509 maybeAddSep() 510 ctx.WriteString("schema_only") 511 } 512 if o.VerifyData { 513 maybeAddSep() 514 ctx.WriteString("verify_backup_table_data") 515 } 516 517 if o.UnsafeRestoreIncompatibleVersion { 518 maybeAddSep() 519 ctx.WriteString("unsafe_restore_incompatible_version") 520 } 521 522 if o.ExecutionLocality != nil { 523 if ctx.HasFlags(FmtHideConstants) { 524 maybeAddSep() 525 ctx.WriteString("execution locality = ") 526 ctx.FormatNode(o.ExecutionLocality) 527 } else { 528 maybeAddSep() 529 ctx.WriteString("execution locality = ") 530 ctx.FormatNode(o.ExecutionLocality) 531 } 532 } 533 534 if o.ExperimentalOnline { 535 maybeAddSep() 536 ctx.WriteString("experimental deferred copy") 537 } 538 539 if o.RemoveRegions { 540 maybeAddSep() 541 ctx.WriteString("remove_regions") 542 } 543 } 544 545 // CombineWith merges other backup options into this backup options struct. 546 // An error is returned if the same option merged multiple times. 547 func (o *RestoreOptions) CombineWith(other *RestoreOptions) error { 548 if o.EncryptionPassphrase == nil { 549 o.EncryptionPassphrase = other.EncryptionPassphrase 550 } else if other.EncryptionPassphrase != nil { 551 return errors.New("encryption_passphrase specified multiple times") 552 } 553 554 if o.DecryptionKMSURI == nil { 555 o.DecryptionKMSURI = other.DecryptionKMSURI 556 } else if other.DecryptionKMSURI != nil { 557 return errors.New("kms specified multiple times") 558 } 559 560 if o.IntoDB == nil { 561 o.IntoDB = other.IntoDB 562 } else if other.IntoDB != nil { 563 return errors.New("into_db specified multiple times") 564 } 565 566 if o.SkipMissingFKs { 567 if other.SkipMissingFKs { 568 return errors.New("skip_missing_foreign_keys specified multiple times") 569 } 570 } else { 571 o.SkipMissingFKs = other.SkipMissingFKs 572 } 573 574 if o.SkipMissingSequences { 575 if other.SkipMissingSequences { 576 return errors.New("skip_missing_sequences specified multiple times") 577 } 578 } else { 579 o.SkipMissingSequences = other.SkipMissingSequences 580 } 581 582 if o.SkipMissingSequenceOwners { 583 if other.SkipMissingSequenceOwners { 584 return errors.New("skip_missing_sequence_owners specified multiple times") 585 } 586 } else { 587 o.SkipMissingSequenceOwners = other.SkipMissingSequenceOwners 588 } 589 590 if o.SkipMissingViews { 591 if other.SkipMissingViews { 592 return errors.New("skip_missing_views specified multiple times") 593 } 594 } else { 595 o.SkipMissingViews = other.SkipMissingViews 596 } 597 598 if o.SkipMissingUDFs { 599 if other.SkipMissingUDFs { 600 return errors.New("skip_missing_udfs specified multiple times") 601 } 602 } else { 603 o.SkipMissingUDFs = other.SkipMissingUDFs 604 } 605 606 if o.Detached { 607 if other.Detached { 608 return errors.New("detached option specified multiple times") 609 } 610 } else { 611 o.Detached = other.Detached 612 } 613 614 if o.SkipLocalitiesCheck { 615 // If RemoveRegions is true, SkipLocalitiesCheck should also be true 616 if other.SkipLocalitiesCheck && !other.RemoveRegions { 617 return errors.New("skip_localities_check specified multiple times") 618 } 619 } else { 620 o.SkipLocalitiesCheck = other.SkipLocalitiesCheck 621 } 622 623 if o.DebugPauseOn == nil { 624 o.DebugPauseOn = other.DebugPauseOn 625 } else if other.DebugPauseOn != nil { 626 return errors.New("debug_pause_on specified multiple times") 627 } 628 629 if o.NewDBName == nil { 630 o.NewDBName = other.NewDBName 631 } else if other.NewDBName != nil { 632 return errors.New("new_db_name specified multiple times") 633 } 634 635 if o.IncrementalStorage == nil { 636 o.IncrementalStorage = other.IncrementalStorage 637 } else if other.IncrementalStorage != nil { 638 return errors.New("incremental_location option specified multiple times") 639 } 640 641 if o.AsTenant == nil { 642 o.AsTenant = other.AsTenant 643 } else if other.AsTenant != nil { 644 return errors.New("tenant_name option specified multiple times") 645 } 646 647 if o.ForceTenantID == nil { 648 o.ForceTenantID = other.ForceTenantID 649 } else if other.ForceTenantID != nil { 650 return errors.New("virtual_cluster option specified multiple times") 651 } 652 653 if o.SchemaOnly { 654 if other.SchemaOnly { 655 return errors.New("schema_only option specified multiple times") 656 } 657 } else { 658 o.SchemaOnly = other.SchemaOnly 659 } 660 if o.VerifyData { 661 if other.VerifyData { 662 return errors.New("verify_backup_table_data option specified multiple times") 663 } 664 } else { 665 o.VerifyData = other.VerifyData 666 } 667 668 if o.IncludeAllSecondaryTenants != nil { 669 if other.IncludeAllSecondaryTenants != nil { 670 return errors.New("include_all_virtual_clusters specified multiple times") 671 } 672 } else { 673 o.IncludeAllSecondaryTenants = other.IncludeAllSecondaryTenants 674 } 675 676 if o.UnsafeRestoreIncompatibleVersion { 677 if other.UnsafeRestoreIncompatibleVersion { 678 return errors.New("unsafe_restore_incompatible_version specified multiple times") 679 } 680 } else { 681 o.UnsafeRestoreIncompatibleVersion = other.UnsafeRestoreIncompatibleVersion 682 } 683 684 if o.ExecutionLocality == nil { 685 o.ExecutionLocality = other.ExecutionLocality 686 } else if other.ExecutionLocality != nil { 687 return errors.New("execution locality option specified multiple times") 688 } 689 690 if o.ExperimentalOnline { 691 if other.ExperimentalOnline { 692 return errors.New("experimental deferred copy specified multiple times") 693 } 694 } else { 695 o.ExperimentalOnline = other.ExperimentalOnline 696 } 697 698 if o.RemoveRegions { 699 if other.RemoveRegions { 700 return errors.New("remove_regions specified multiple times") 701 } 702 } else { 703 o.RemoveRegions = other.RemoveRegions 704 } 705 706 return nil 707 } 708 709 // IsDefault returns true if this backup options struct has default value. 710 func (o RestoreOptions) IsDefault() bool { 711 options := RestoreOptions{} 712 return o.SkipMissingFKs == options.SkipMissingFKs && 713 o.SkipMissingSequences == options.SkipMissingSequences && 714 o.SkipMissingSequenceOwners == options.SkipMissingSequenceOwners && 715 o.SkipMissingViews == options.SkipMissingViews && 716 o.SkipMissingUDFs == options.SkipMissingUDFs && 717 cmp.Equal(o.DecryptionKMSURI, options.DecryptionKMSURI) && 718 o.EncryptionPassphrase == options.EncryptionPassphrase && 719 o.IntoDB == options.IntoDB && 720 o.Detached == options.Detached && 721 o.SkipLocalitiesCheck == options.SkipLocalitiesCheck && 722 o.DebugPauseOn == options.DebugPauseOn && 723 o.NewDBName == options.NewDBName && 724 cmp.Equal(o.IncrementalStorage, options.IncrementalStorage) && 725 o.AsTenant == options.AsTenant && 726 o.ForceTenantID == options.ForceTenantID && 727 o.SchemaOnly == options.SchemaOnly && 728 o.VerifyData == options.VerifyData && 729 o.IncludeAllSecondaryTenants == options.IncludeAllSecondaryTenants && 730 o.UnsafeRestoreIncompatibleVersion == options.UnsafeRestoreIncompatibleVersion && 731 o.ExecutionLocality == options.ExecutionLocality && 732 o.ExperimentalOnline == options.ExperimentalOnline && 733 o.RemoveRegions == options.RemoveRegions 734 } 735 736 // BackupTargetList represents a list of targets. 737 // Only one field may be non-nil. 738 type BackupTargetList struct { 739 Databases NameList 740 Schemas ObjectNamePrefixList 741 Tables TableAttrs 742 TenantID TenantID 743 } 744 745 // Format implements the NodeFormatter interface. 746 func (tl *BackupTargetList) Format(ctx *FmtCtx) { 747 if tl.Databases != nil { 748 ctx.WriteString("DATABASE ") 749 ctx.FormatNode(&tl.Databases) 750 } else if tl.Schemas != nil { 751 ctx.WriteString("SCHEMA ") 752 ctx.FormatNode(&tl.Schemas) 753 } else if tl.TenantID.Specified { 754 ctx.WriteString("VIRTUAL CLUSTER ") 755 ctx.FormatNode(&tl.TenantID) 756 } else { 757 if tl.Tables.SequenceOnly { 758 ctx.WriteString("SEQUENCE ") 759 } else { 760 ctx.WriteString("TABLE ") 761 } 762 ctx.FormatNode(&tl.Tables.TablePatterns) 763 } 764 }