diff --git a/pkg/ccl/backupccl/BUILD.bazel b/pkg/ccl/backupccl/BUILD.bazel index 2b13ee9e4fad..6b75f0854128 100644 --- a/pkg/ccl/backupccl/BUILD.bazel +++ b/pkg/ccl/backupccl/BUILD.bazel @@ -147,6 +147,7 @@ go_test( name = "backupccl_test", size = "enormous", srcs = [ + "alter_backup_schedule_test.go", "alter_backup_test.go", "backup_cloud_test.go", "backup_intents_test.go", diff --git a/pkg/ccl/backupccl/alter_backup_schedule.go b/pkg/ccl/backupccl/alter_backup_schedule.go index c1b38b0c591a..6c421b22eb29 100644 --- a/pkg/ccl/backupccl/alter_backup_schedule.go +++ b/pkg/ccl/backupccl/alter_backup_schedule.go @@ -164,7 +164,11 @@ func doAlterBackupSchedules( return err } - // TODO(benbardin): Verify backup statement. + // Run full backup in dry-run mode. This will do all of the sanity checks + // and validation we need to make in order to ensure the schedule is sane. + if _, err = dryRunBackup(ctx, p, s.fullStmt); err != nil { + return errors.Wrap(err, "failed to dry run backup") + } s.fullArgs.BackupStatement = tree.AsStringWithFlags(s.fullStmt, tree.FmtParsable|tree.FmtShowPasswords) fullAny, err := pbtypes.MarshalAny(s.fullArgs) @@ -190,8 +194,36 @@ func doAlterBackupSchedules( if err := s.incJob.Update(ctx, p.ExecCfg().InternalExecutor, p.Txn()); err != nil { return err } + + if err := emitAlteredSchedule(s.incJob, s.incStmt, resultsCh); err != nil { + return err + } + } + + // Emit the full backup schedule after the incremental. + // This matches behavior in CREATE SCHEDULE FOR BACKUP. + return emitAlteredSchedule(s.fullJob, s.fullStmt, resultsCh) +} + +func emitAlteredSchedule( + job *jobs.ScheduledJob, stmt *tree.Backup, resultsCh chan<- tree.Datums, +) error { + to := make([]string, len(stmt.To)) + for i, dest := range stmt.To { + to[i] = tree.AsStringWithFlags(dest, tree.FmtBareStrings) + } + kmsURIs := make([]string, len(stmt.Options.EncryptionKMSURI)) + for i, kmsURI := range stmt.Options.EncryptionKMSURI { + kmsURIs[i] = tree.AsStringWithFlags(kmsURI, tree.FmtBareStrings) + } + incDests := make([]string, len(stmt.Options.IncrementalStorage)) + for i, incDest := range stmt.Options.IncrementalStorage { + incDests[i] = tree.AsStringWithFlags(incDest, tree.FmtBareStrings) + } + if err := emitSchedule(job, stmt, to, nil, /* incrementalFrom */ + kmsURIs, incDests, resultsCh); err != nil { + return err } - // TODO(benbardin): Emit schedules. return nil } diff --git a/pkg/ccl/backupccl/alter_backup_schedule_test.go b/pkg/ccl/backupccl/alter_backup_schedule_test.go new file mode 100644 index 000000000000..c985f855140f --- /dev/null +++ b/pkg/ccl/backupccl/alter_backup_schedule_test.go @@ -0,0 +1,144 @@ +// Copyright 2020 The Cockroach Authors. +// +// Licensed as a CockroachDB Enterprise file under the Cockroach Community +// License (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt + +package backupccl + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/jobs/jobstest" + "github.com/cockroachdb/cockroach/pkg/scheduledjobs" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/testutils" + "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/stretchr/testify/require" +) + +// alterSchedulesTestHelper starts a server, and arranges for job scheduling daemon to +// use jobstest.JobSchedulerTestEnv. +// This helper also arranges for the manual override of scheduling logic +// via executeSchedules callback. +type execAlterSchedulesFn = func(ctx context.Context, maxSchedules int64) error +type alterSchedulesTestHelper struct { + iodir string + server serverutils.TestServerInterface + env *jobstest.JobSchedulerTestEnv + cfg *scheduledjobs.JobExecutionConfig + sqlDB *sqlutils.SQLRunner + executeSchedules func() error +} + +// newAlterSchedulesTestHelper creates and initializes appropriate state for a test, +// returning alterSchedulesTestHelper as well as a cleanup function. +func newAlterSchedulesTestHelper(t *testing.T) (*alterSchedulesTestHelper, func()) { + dir, dirCleanupFn := testutils.TempDir(t) + + th := &alterSchedulesTestHelper{ + env: jobstest.NewJobSchedulerTestEnv( + jobstest.UseSystemTables, timeutil.Now(), tree.ScheduledBackupExecutor), + iodir: dir, + } + + knobs := &jobs.TestingKnobs{ + JobSchedulerEnv: th.env, + TakeOverJobsScheduling: func(fn execAlterSchedulesFn) { + th.executeSchedules = func() error { + defer th.server.JobRegistry().(*jobs.Registry).TestingNudgeAdoptionQueue() + return fn(context.Background(), allSchedules) + } + }, + CaptureJobExecutionConfig: func(config *scheduledjobs.JobExecutionConfig) { + th.cfg = config + }, + IntervalOverrides: jobs.NewTestingKnobsWithShortIntervals().IntervalOverrides, + } + + args := base.TestServerArgs{ + ExternalIODir: dir, + // Some scheduled backup tests fail when run within a tenant. More + // investigation is required. Tracked with #76378. + DisableDefaultTestTenant: true, + Knobs: base.TestingKnobs{ + JobsTestingKnobs: knobs, + }, + } + s, db, _ := serverutils.StartServer(t, args) + require.NotNil(t, th.cfg) + th.sqlDB = sqlutils.MakeSQLRunner(db) + th.server = s + th.sqlDB.Exec(t, `SET CLUSTER SETTING bulkio.backup.merge_file_buffer_size = '1MiB'`) + th.sqlDB.Exec(t, `SET CLUSTER SETTING kv.closed_timestamp.target_duration = '100ms'`) // speeds up test + + return th, func() { + dirCleanupFn() + s.Stopper().Stop(context.Background()) + } +} + +func TestAlterBackupScheduleEmitsSummary(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + th, cleanup := newAlterSchedulesTestHelper(t) + defer cleanup() + + th.sqlDB.Exec(t, ` +CREATE DATABASE mydb; +USE mydb; + +CREATE TABLE t1(a int); +INSERT INTO t1 values (1), (10), (100); +`) + + rows := th.sqlDB.Query(t, + `CREATE SCHEDULE FOR BACKUP t1 INTO 'nodelocal://0/backup/alter-schedule' RECURRING '@daily';`) + require.NoError(t, rows.Err()) + + var scheduleID int64 + var unusedStr string + var unusedTS *time.Time + rowCount := 0 + for rows.Next() { + // We just need to retrieve one of the schedule IDs, don't care whether + // it's the incremental or full. + require.NoError(t, rows.Scan(&scheduleID, &unusedStr, &unusedStr, &unusedTS, &unusedStr, &unusedStr)) + rowCount++ + } + require.Equal(t, 2, rowCount) + + var status, schedule, backupStmt string + var statuses, schedules, backupStmts []string + rows = th.sqlDB.Query(t, + fmt.Sprintf(`ALTER BACKUP SCHEDULE %d SET FULL BACKUP '@weekly';`, scheduleID)) + require.NoError(t, rows.Err()) + for rows.Next() { + require.NoError(t, rows.Scan(&scheduleID, &unusedStr, &status, &unusedTS, &schedule, &backupStmt)) + statuses = append(statuses, status) + schedules = append(schedules, schedule) + backupStmts = append(backupStmts, backupStmt) + } + + // Incremental should be emitted first. + require.Equal(t, []string{"PAUSED: Waiting for initial backup to complete", "ACTIVE"}, statuses) + require.Equal(t, []string{"@daily", "@weekly"}, schedules) + require.Equal(t, []string{ + "BACKUP TABLE mydb.public.t1 INTO LATEST IN 'nodelocal://0/backup/alter-schedule' WITH detached", + "BACKUP TABLE mydb.public.t1 INTO 'nodelocal://0/backup/alter-schedule' WITH detached", + }, + backupStmts) + +} diff --git a/pkg/ccl/backupccl/backup_planning.go b/pkg/ccl/backupccl/backup_planning.go index 67cca812dc22..9e68a76e8b58 100644 --- a/pkg/ccl/backupccl/backup_planning.go +++ b/pkg/ccl/backupccl/backup_planning.go @@ -224,9 +224,10 @@ func GetRedactedBackupNode( hasBeenPlanned bool, ) (*tree.Backup, error) { b := &tree.Backup{ - AsOf: backup.AsOf, - Targets: backup.Targets, - Nested: backup.Nested, + AsOf: backup.AsOf, + Targets: backup.Targets, + Nested: backup.Nested, + AppendToLatest: backup.AppendToLatest, } // We set Subdir to the directory resolved during BACKUP planning. diff --git a/pkg/ccl/backupccl/datadriven_test.go b/pkg/ccl/backupccl/datadriven_test.go index 16221d61f8fa..dff084a9a263 100644 --- a/pkg/ccl/backupccl/datadriven_test.go +++ b/pkg/ccl/backupccl/datadriven_test.go @@ -243,133 +243,130 @@ func (d *datadrivenTestState) getSQLDB(t *testing.T, server string, user string) // commands. The test files are in testdata/backup-restore. The following // syntax is provided: // -// - "new-server name= [args]" -// Create a new server with the input name. +// - "new-server name= [args]" +// Create a new server with the input name. // -// Supported arguments: +// Supported arguments: // -// + share-io-dir: can be specified to share an IO directory with an existing -// server. This is useful when restoring from a backup taken in another -// server. +// - share-io-dir: can be specified to share an IO directory with an existing +// server. This is useful when restoring from a backup taken in another +// server. // -// + allow-implicit-access: can be specified to set -// `EnableNonAdminImplicitAndArbitraryOutbound` to true +// - allow-implicit-access: can be specified to set +// `EnableNonAdminImplicitAndArbitraryOutbound` to true // -// + disable-http: disables use of external HTTP endpoints. +// - disable-http: disables use of external HTTP endpoints. // -// + localities: specifies the localities that will be used when starting up -// the test cluster. The cluster will have len(localities) nodes, with each -// node assigned a locality config corresponding to the locality. Please -// update the `localityCfgs` map when adding new localities. +// - localities: specifies the localities that will be used when starting up +// the test cluster. The cluster will have len(localities) nodes, with each +// node assigned a locality config corresponding to the locality. Please +// update the `localityCfgs` map when adding new localities. // -// + nodes: specifies the number of nodes in the test cluster. +// - nodes: specifies the number of nodes in the test cluster. // -// + splits: specifies the number of ranges the bank table is split into. +// - splits: specifies the number of ranges the bank table is split into. // -// + before-version=: creates a mixed version cluster where all -// nodes running the test server binary think the clusterVersion is one -// version before the passed in key. See cockroach_versions.go -// for possible values. +// - before-version=: creates a mixed version cluster where all +// nodes running the test server binary think the clusterVersion is one +// version before the passed in key. See cockroach_versions.go +// for possible values. // -// + testingKnobCfg: specifies a key to a hardcoded testingKnob configuration +// - testingKnobCfg: specifies a key to a hardcoded testingKnob configuration // +// - "upgrade-server version=" +// Upgrade the cluster version of the active server to the passed in +// clusterVersion key. See cockroach_versions.go for possible values. // -// - "upgrade-server version=" -// Upgrade the cluster version of the active server to the passed in -// clusterVersion key. See cockroach_versions.go for possible values. +// - "exec-sql [server=] [user=] [args]" +// Executes the input SQL query on the target server. By default, server is +// the last created server. // +// Supported arguments: // -// - "exec-sql [server=] [user=] [args]" -// Executes the input SQL query on the target server. By default, server is -// the last created server. +// - expect-error-regex=: expects the query to return an error with a string +// matching the provided regex // -// Supported arguments: +// - expect-error-ignore: expects the query to return an error, but we will +// ignore it. // -// + expect-error-regex=: expects the query to return an error with a string -// matching the provided regex +// - ignore-notice: does not print out the notice that is buffered during +// query execution. // -// + expect-error-ignore: expects the query to return an error, but we will -// ignore it. +// - "query-sql [server=] [user=] [regex=]" +// Executes the input SQL query and print the results. // -// + ignore-notice: does not print out the notice that is buffered during -// query execution. +// - regex: return true if the query result matches the regex pattern and +// false otherwise. // -// - "query-sql [server=] [user=] [regex=]" -// Executes the input SQL query and print the results. +// - "reset" +// Clear all state associated with the test. // -// + regex: return true if the query result matches the regex pattern and -// false otherwise. +// - "job" [server=] [user=] [args] +// Executes job specific operations. // -// - "reset" -// Clear all state associated with the test. +// Supported arguments: // -// - "job" [server=] [user=] [args] -// Executes job specific operations. +// - resume=: resumes the job referenced by the tag, use in conjunction +// with wait-for-state. // -// Supported arguments: +// - cancel=: cancels the job referenced by the tag and waits for it to +// reach a CANCELED state. // -// + resume=: resumes the job referenced by the tag, use in conjunction -// with wait-for-state. +// - wait-for-state= tag=: wait for +// the job referenced by the tag to reach the specified state. // -// + cancel=: cancels the job referenced by the tag and waits for it to -// reach a CANCELED state. +// - "let" [args] +// Assigns the returned value of the SQL query to the provided args as variables. // -// + wait-for-state= tag=: wait for -// the job referenced by the tag to reach the specified state. +// - "save-cluster-ts" tag= +// Saves the `SELECT cluster_logical_timestamp()` with the tag. Can be used +// in the future with intstructions such as `aost`. // -// - "let" [args] -// Assigns the returned value of the SQL query to the provided args as variables. +// - "backup" [args] +// Executes backup specific operations. // -// - "save-cluster-ts" tag= -// Saves the `SELECT cluster_logical_timestamp()` with the tag. Can be used -// in the future with intstructions such as `aost`. +// Supported arguments: // -// - "backup" [args] -// Executes backup specific operations. +// - tag=: tag the backup job to reference it in the future. // -// Supported arguments: +// - expect-pausepoint: expects the backup job to end up in a paused state because +// of a pausepoint error. // -// + tag=: tag the backup job to reference it in the future. +// - "restore" [args] +// Executes restore specific operations. // -// + expect-pausepoint: expects the backup job to end up in a paused state because -// of a pausepoint error. +// Supported arguments: // -// - "restore" [args] -// Executes restore specific operations. +// - tag=: tag the restore job to reference it in the future. // -// Supported arguments: +// - expect-pausepoint: expects the restore job to end up in a paused state because +// of a pausepoint error. // -// + tag=: tag the restore job to reference it in the future. +// - aost: expects a tag referencing a previously saved cluster timestamp +// using `save-cluster-ts`. It then runs the restore as of the saved cluster +// timestamp. // -// + expect-pausepoint: expects the restore job to end up in a paused state because -// of a pausepoint error. +// - "schema" [args] +// Executes schema change specific operations. // -// + aost: expects a tag referencing a previously saved cluster timestamp -// using `save-cluster-ts`. It then runs the restore as of the saved cluster -// timestamp. +// Supported arguments: // -// - "schema" [args] -// Executes schema change specific operations. +// - tag=: tag the schema change job to reference it in the future. // -// Supported arguments: +// - expect-pausepoint: expects the schema change job to end up in a paused state because +// of a pausepoint error. // -// + tag=: tag the schema change job to reference it in the future. +// - "kv" [args] +// Issues a kv request // -// + expect-pausepoint: expects the schema change job to end up in a paused state because -// of a pausepoint error. +// Supported arguments: // -// - "kv" [args] -// Issues a kv request +// - type: kv request type. Currently, only DeleteRange is supported // -// Supported arguments: +// - target: SQL target. Currently, only table names are supported. // -// + type: kv request type. Currently, only DeleteRange is supported -// -// + target: SQL target. Currently, only table names are supported. -// -// -// - "corrupt-backup" uri= -// Finds the latest backup in the provided collection uri an flips a bit in one SST in the backup +// - "corrupt-backup" uri= +// Finds the latest backup in the provided collection uri an flips a bit in one SST in the backup func TestDataDriven(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -521,7 +518,11 @@ func TestDataDriven(t *testing.T) { require.NotNilf(t, err, "expected error") var expectErrorRegex string d.ScanArgs(t, "expect-error-regex", &expectErrorRegex) - testutils.IsError(err, expectErrorRegex) + require.True(t, + testutils.IsError(err, expectErrorRegex), + "Regex `%s` did not match `%s`", + expectErrorRegex, + err) ret = append(ret, "regex matches error") return strings.Join(ret, "\n") } diff --git a/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/backup-options b/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/backup-options index 04f1cc3c7fe7..1babcaa762cb 100644 --- a/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/backup-options +++ b/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/backup-options @@ -19,15 +19,15 @@ $incID "BACKUP INTO LATEST IN 'nodelocal://1/example-schedule' WITH detached" # Can't use the same command twice. -exec-sql expect-error-ignore +exec-sql expect-error-regex=(can specify SET RECURRING at most once) alter backup schedule $fullID set recurring '0 0 1 * *', set recurring '@weekly'; ---- -ignoring expected error +regex matches error -exec-sql expect-error-ignore +exec-sql expect-error-regex=(can specify SET FULL BACKUP at most once) alter backup schedule $fullID set full backup '0 0 1 * *', set recurring '0 0 1 * *', set full backup '@weekly'; ---- -ignoring expected error +regex matches error # Set an option @@ -53,22 +53,30 @@ with schedules as (show schedules) select id, command->'backup_statement' from s $fullID "BACKUP INTO 'nodelocal://1/example-schedule' WITH revision_history = true, encryption_passphrase = '*****', detached" $incID "BACKUP INTO LATEST IN 'nodelocal://1/example-schedule' WITH revision_history = true, encryption_passphrase = '*****', detached" +# Add an incompatible option + +exec-sql expect-error-regex=(cannot have both encryption_passphrase and kms option set) +alter backup schedule $incID set with kms = ('aws:///key1?region=r1', 'aws:///key2?region=r2'), set with incremental_location = 'inc'; +---- +regex matches error + # Add a list-option exec-sql +alter backup schedule $incID set with encryption_passphrase = ''; alter backup schedule $incID set with kms = ('aws:///key1?region=r1', 'aws:///key2?region=r2'), set with incremental_location = 'inc'; ---- query-sql with schedules as (show schedules) select id, command->'backup_statement' from schedules where label='datatest' order by command->>'backup_type' asc; ---- -$fullID "BACKUP INTO 'nodelocal://1/example-schedule' WITH revision_history = true, encryption_passphrase = '*****', detached, kms = ('aws:///redacted?region=r1', 'aws:///redacted?region=r2'), incremental_location = 'inc'" -$incID "BACKUP INTO LATEST IN 'nodelocal://1/example-schedule' WITH revision_history = true, encryption_passphrase = '*****', detached, kms = ('aws:///redacted?region=r1', 'aws:///redacted?region=r2'), incremental_location = 'inc'" +$fullID "BACKUP INTO 'nodelocal://1/example-schedule' WITH revision_history = true, detached, kms = ('aws:///redacted?region=r1', 'aws:///redacted?region=r2'), incremental_location = 'inc'" +$incID "BACKUP INTO LATEST IN 'nodelocal://1/example-schedule' WITH revision_history = true, detached, kms = ('aws:///redacted?region=r1', 'aws:///redacted?region=r2'), incremental_location = 'inc'" # Set options to empty (unset). exec-sql -alter backup schedule $incID set with kms = '', set with incremental_location = (''), set with encryption_passphrase = ''; +alter backup schedule $incID set with kms = '', set with incremental_location = (''); ---- query-sql @@ -79,15 +87,15 @@ $incID "BACKUP INTO LATEST IN 'nodelocal://1/example-schedule' WITH revision_his # Setting DETACHED throws an error. -exec-sql expect-error-ignore +exec-sql expect-error-regex=(DETACHED is required for scheduled backups and cannot be altered) alter backup schedule $incID set with detached = true; ---- -ignoring expected error +regex matches error -exec-sql expect-error-ignore +exec-sql expect-error-regex=(DETACHED is required for scheduled backups and cannot be altered) alter backup schedule $incID set with detached = false; ---- -ignoring expected error +regex matches error query-sql with schedules as (show schedules) select id, command->'backup_statement' from schedules where label='datatest' order by command->>'backup_type' asc; diff --git a/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/recurrence b/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/recurrence index 005618d720f1..b3d4b843f553 100644 --- a/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/recurrence +++ b/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/recurrence @@ -177,10 +177,10 @@ $incID Waiting for initial backup to complete @weekly root {"backup_statement": # Can't set incremental schedule to be slower than full. -exec-sql expect-error-ignore +exec-sql expect-error-regex=(incremental backups must occur more often than full backups) alter backup schedule $fullID set recurring '@weekly', set full backup '@daily'; ---- -ignoring expected error +regex matches error # Remove incremental backup and change full cadence in the same command. diff --git a/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/schedule-options b/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/schedule-options index d27981110ff8..168958cfcf3a 100644 --- a/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/schedule-options +++ b/pkg/ccl/backupccl/testdata/backup-restore/alter-schedule/schedule-options @@ -51,10 +51,10 @@ alter backup schedule $fullID set schedule option updates_cluster_last_backup_ti alter backup schedule $fullID set schedule option updates_cluster_last_backup_time_metric = 't'; ---- -exec-sql expect-error-ignore +exec-sql expect-error-regex=(unexpected value) alter backup schedule $fullID set schedule option updates_cluster_last_backup_time_metric = 'yeah for sure true'; ---- -ignoring expected error +regex matches error exec-sql create user testuser; @@ -74,8 +74,8 @@ let $fullID $incID with schedules as (show schedules) select id from schedules where label='datatest3' order by command->>'backup_type' asc; ---- -exec-sql user=testuser expect-error-ignore +exec-sql user=testuser expect-error-regex=(only users with the admin role are allowed to updates_cluster_last_backup_time_metric) alter backup schedule $fullID set schedule option updates_cluster_last_backup_time_metric = '1'; ---- -ignoring expected error +regex matches error diff --git a/pkg/ccl/backupccl/testdata/backup-restore/lock-concurrent-backups b/pkg/ccl/backupccl/testdata/backup-restore/lock-concurrent-backups index 2aa38aeb8d45..671a7bb9cc98 100644 --- a/pkg/ccl/backupccl/testdata/backup-restore/lock-concurrent-backups +++ b/pkg/ccl/backupccl/testdata/backup-restore/lock-concurrent-backups @@ -99,7 +99,7 @@ BACKUP TO 'userfile://defaultdb.public.baz/baz'; ---- job paused at pausepoint -exec-sql expect-error-regex='userfile://defaultdb.public.baz/baz already contains a `BACKUP-LOCK`' +exec-sql expect-error-regex=(userfile://defaultdb.public.baz/baz already contains a `BACKUP-LOCK`) BACKUP TO 'userfile://defaultdb.public.baz/baz'; ---- regex matches error diff --git a/pkg/ccl/backupccl/testdata/backup-restore/restore-validation-only b/pkg/ccl/backupccl/testdata/backup-restore/restore-validation-only index 86974f1e08da..501ff9e709d9 100644 --- a/pkg/ccl/backupccl/testdata/backup-restore/restore-validation-only +++ b/pkg/ccl/backupccl/testdata/backup-restore/restore-validation-only @@ -170,7 +170,7 @@ RESTORE DATABASE d FROM LATEST IN 'nodelocal://0/full_database_backup/' with sch # But verify-backup-table-data catches it # TODO(msbutler): refactor the test once this is addressed -exec-sql expect-error-regex='pebble/table: invalid table 000000' +exec-sql expect-error-regex=(pebble/table: invalid table 000000) RESTORE DATABASE d FROM LATEST IN 'nodelocal://0/full_database_backup/' with schema_only, verify_backup_table_data, new_db_name='d4'; ---- regex matches error