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  }