vitess.io/vitess@v0.16.2/go/test/endtoend/onlineddl/scheduler/onlineddl_scheduler_test.go (about)

     1  /*
     2  Copyright 2021 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package scheduler
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"flag"
    23  	"fmt"
    24  	"io"
    25  	"math/rand"
    26  	"os"
    27  	"path"
    28  	"strings"
    29  	"sync"
    30  	"testing"
    31  	"time"
    32  
    33  	"vitess.io/vitess/go/mysql"
    34  	"vitess.io/vitess/go/textutil"
    35  	"vitess.io/vitess/go/vt/log"
    36  	"vitess.io/vitess/go/vt/schema"
    37  	"vitess.io/vitess/go/vt/sqlparser"
    38  
    39  	"vitess.io/vitess/go/test/endtoend/cluster"
    40  	"vitess.io/vitess/go/test/endtoend/onlineddl"
    41  
    42  	"github.com/stretchr/testify/assert"
    43  	"github.com/stretchr/testify/require"
    44  )
    45  
    46  const (
    47  	anyErrorIndicator = "<any-error-of-any-kind>"
    48  )
    49  
    50  type testOnlineDDLStatementParams struct {
    51  	ddlStatement     string
    52  	ddlStrategy      string
    53  	executeStrategy  string
    54  	expectHint       string
    55  	expectError      string
    56  	skipWait         bool
    57  	migrationContext string
    58  }
    59  
    60  type testRevertMigrationParams struct {
    61  	revertUUID       string
    62  	executeStrategy  string
    63  	ddlStrategy      string
    64  	migrationContext string
    65  	expectError      string
    66  	skipWait         bool
    67  }
    68  
    69  var (
    70  	clusterInstance *cluster.LocalProcessCluster
    71  	shards          []cluster.Shard
    72  	vtParams        mysql.ConnParams
    73  
    74  	normalWaitTime            = 20 * time.Second
    75  	extendedWaitTime          = 60 * time.Second
    76  	ensureStateNotChangedTime = 5 * time.Second
    77  
    78  	hostname              = "localhost"
    79  	keyspaceName          = "ks"
    80  	cell                  = "zone1"
    81  	schemaChangeDirectory = ""
    82  	overrideVtctlParams   *cluster.VtctlClientParams
    83  )
    84  
    85  type WriteMetrics struct {
    86  	mu                                                      sync.Mutex
    87  	insertsAttempts, insertsFailures, insertsNoops, inserts int64
    88  	updatesAttempts, updatesFailures, updatesNoops, updates int64
    89  	deletesAttempts, deletesFailures, deletesNoops, deletes int64
    90  }
    91  
    92  func (w *WriteMetrics) Clear() {
    93  	w.mu.Lock()
    94  	defer w.mu.Unlock()
    95  
    96  	w.inserts = 0
    97  	w.updates = 0
    98  	w.deletes = 0
    99  
   100  	w.insertsAttempts = 0
   101  	w.insertsFailures = 0
   102  	w.insertsNoops = 0
   103  
   104  	w.updatesAttempts = 0
   105  	w.updatesFailures = 0
   106  	w.updatesNoops = 0
   107  
   108  	w.deletesAttempts = 0
   109  	w.deletesFailures = 0
   110  	w.deletesNoops = 0
   111  }
   112  
   113  func (w *WriteMetrics) String() string {
   114  	return fmt.Sprintf(`WriteMetrics: inserts-deletes=%d, updates-deletes=%d,
   115  insertsAttempts=%d, insertsFailures=%d, insertsNoops=%d, inserts=%d,
   116  updatesAttempts=%d, updatesFailures=%d, updatesNoops=%d, updates=%d,
   117  deletesAttempts=%d, deletesFailures=%d, deletesNoops=%d, deletes=%d,
   118  `,
   119  		w.inserts-w.deletes, w.updates-w.deletes,
   120  		w.insertsAttempts, w.insertsFailures, w.insertsNoops, w.inserts,
   121  		w.updatesAttempts, w.updatesFailures, w.updatesNoops, w.updates,
   122  		w.deletesAttempts, w.deletesFailures, w.deletesNoops, w.deletes,
   123  	)
   124  }
   125  
   126  func parseTableName(t *testing.T, sql string) (tableName string) {
   127  	// ddlStatement could possibly be composed of multiple DDL statements
   128  	tokenizer := sqlparser.NewStringTokenizer(sql)
   129  	for {
   130  		stmt, err := sqlparser.ParseNextStrictDDL(tokenizer)
   131  		if err != nil && errors.Is(err, io.EOF) {
   132  			break
   133  		}
   134  		require.NoErrorf(t, err, "parsing sql: [%v]", sql)
   135  		ddlStmt, ok := stmt.(sqlparser.DDLStatement)
   136  		require.True(t, ok)
   137  		tableName = ddlStmt.GetTable().Name.String()
   138  		if tableName == "" {
   139  			tbls := ddlStmt.AffectedTables()
   140  			require.NotEmpty(t, tbls)
   141  			tableName = tbls[0].Name.String()
   142  		}
   143  		require.NotEmptyf(t, tableName, "could not parse table name from SQL: %s", sqlparser.String(ddlStmt))
   144  	}
   145  	require.NotEmptyf(t, tableName, "could not parse table name from SQL: %s", sql)
   146  	return tableName
   147  }
   148  
   149  // testOnlineDDLStatement runs an online DDL, ALTER statement
   150  func TestParseTableName(t *testing.T) {
   151  	sqls := []string{
   152  		`ALTER TABLE t1_test ENGINE=InnoDB`,
   153  		`ALTER TABLE t1_test ENGINE=InnoDB;`,
   154  		`DROP TABLE IF EXISTS t1_test`,
   155  		`
   156  			ALTER TABLE stress_test ENGINE=InnoDB;
   157  			ALTER TABLE stress_test ENGINE=InnoDB;
   158  			ALTER TABLE stress_test ENGINE=InnoDB;
   159  		`,
   160  	}
   161  
   162  	for _, sql := range sqls {
   163  		t.Run(sql, func(t *testing.T) {
   164  			parseTableName(t, sql)
   165  		})
   166  	}
   167  }
   168  
   169  func TestMain(m *testing.M) {
   170  	defer cluster.PanicHandler(nil)
   171  	flag.Parse()
   172  
   173  	exitcode, err := func() (int, error) {
   174  		clusterInstance = cluster.NewCluster(cell, hostname)
   175  		schemaChangeDirectory = path.Join("/tmp", fmt.Sprintf("schema_change_dir_%d", clusterInstance.GetAndReserveTabletUID()))
   176  		defer os.RemoveAll(schemaChangeDirectory)
   177  		defer clusterInstance.Teardown()
   178  
   179  		if _, err := os.Stat(schemaChangeDirectory); os.IsNotExist(err) {
   180  			_ = os.Mkdir(schemaChangeDirectory, 0700)
   181  		}
   182  
   183  		clusterInstance.VtctldExtraArgs = []string{
   184  			"--schema_change_dir", schemaChangeDirectory,
   185  			"--schema_change_controller", "local",
   186  			"--schema_change_check_interval", "1"}
   187  
   188  		clusterInstance.VtTabletExtraArgs = []string{
   189  			"--enable-lag-throttler",
   190  			"--throttle_threshold", "1s",
   191  			"--heartbeat_enable",
   192  			"--heartbeat_interval", "250ms",
   193  			"--heartbeat_on_demand_duration", "5s",
   194  			"--watch_replication_stream",
   195  		}
   196  		clusterInstance.VtGateExtraArgs = []string{}
   197  
   198  		if err := clusterInstance.StartTopo(); err != nil {
   199  			return 1, err
   200  		}
   201  
   202  		// Start keyspace
   203  		keyspace := &cluster.Keyspace{
   204  			Name: keyspaceName,
   205  		}
   206  
   207  		// No need for replicas in this stress test
   208  		if err := clusterInstance.StartKeyspace(*keyspace, []string{"1"}, 0, false); err != nil {
   209  			return 1, err
   210  		}
   211  
   212  		vtgateInstance := clusterInstance.NewVtgateInstance()
   213  		// Start vtgate
   214  		if err := vtgateInstance.Setup(); err != nil {
   215  			return 1, err
   216  		}
   217  		// ensure it is torn down during cluster TearDown
   218  		clusterInstance.VtgateProcess = *vtgateInstance
   219  		vtParams = mysql.ConnParams{
   220  			Host: clusterInstance.Hostname,
   221  			Port: clusterInstance.VtgateMySQLPort,
   222  		}
   223  
   224  		return m.Run(), nil
   225  	}()
   226  	if err != nil {
   227  		fmt.Printf("%v\n", err)
   228  		os.Exit(1)
   229  	} else {
   230  		os.Exit(exitcode)
   231  	}
   232  
   233  }
   234  
   235  func TestSchemaChange(t *testing.T) {
   236  	t.Run("scheduler", testScheduler)
   237  	t.Run("singleton", testSingleton)
   238  	t.Run("declarative", testDeclarative)
   239  	t.Run("foreign-keys", testForeignKeys)
   240  	t.Run("summary: validate sequential migration IDs", func(t *testing.T) {
   241  		onlineddl.ValidateSequentialMigrationIDs(t, &vtParams, shards)
   242  	})
   243  }
   244  
   245  func testScheduler(t *testing.T) {
   246  	defer cluster.PanicHandler(t)
   247  	shards = clusterInstance.Keyspaces[0].Shards
   248  	require.Equal(t, 1, len(shards))
   249  
   250  	ddlStrategy := "vitess"
   251  
   252  	createParams := func(ddlStatement string, ddlStrategy string, executeStrategy string, expectHint string, expectError string, skipWait bool) *testOnlineDDLStatementParams {
   253  		return &testOnlineDDLStatementParams{
   254  			ddlStatement:    ddlStatement,
   255  			ddlStrategy:     ddlStrategy,
   256  			executeStrategy: executeStrategy,
   257  			expectHint:      expectHint,
   258  			expectError:     expectError,
   259  			skipWait:        skipWait,
   260  		}
   261  	}
   262  
   263  	createRevertParams := func(revertUUID string, ddlStrategy string, executeStrategy string, expectError string, skipWait bool) *testRevertMigrationParams {
   264  		return &testRevertMigrationParams{
   265  			revertUUID:      revertUUID,
   266  			executeStrategy: executeStrategy,
   267  			ddlStrategy:     ddlStrategy,
   268  			expectError:     expectError,
   269  			skipWait:        skipWait,
   270  		}
   271  	}
   272  
   273  	mysqlVersion := onlineddl.GetMySQLVersion(t, clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet())
   274  	require.NotEmpty(t, mysqlVersion)
   275  	_, capableOf, _ := mysql.GetFlavor(mysqlVersion, nil)
   276  
   277  	var (
   278  		t1uuid string
   279  		t2uuid string
   280  
   281  		t1Name            = "t1_test"
   282  		t2Name            = "t2_test"
   283  		createT1Statement = `
   284  			CREATE TABLE t1_test (
   285  				id bigint(20) not null,
   286  				hint_col varchar(64) not null default 'just-created',
   287  				PRIMARY KEY (id)
   288  			) ENGINE=InnoDB
   289  		`
   290  		createT2Statement = `
   291  			CREATE TABLE t2_test (
   292  				id bigint(20) not null,
   293  				hint_col varchar(64) not null default 'just-created',
   294  				PRIMARY KEY (id)
   295  			) ENGINE=InnoDB
   296  		`
   297  		createT1IfNotExistsStatement = `
   298  			CREATE TABLE IF NOT EXISTS t1_test (
   299  				id bigint(20) not null,
   300  				hint_col varchar(64) not null default 'should_not_appear',
   301  				PRIMARY KEY (id)
   302  			) ENGINE=InnoDB
   303  		`
   304  		trivialAlterT1Statement = `
   305  			ALTER TABLE t1_test ENGINE=InnoDB;
   306  		`
   307  		trivialAlterT2Statement = `
   308  			ALTER TABLE t2_test ENGINE=InnoDB;
   309  		`
   310  		instantAlterT1Statement = `
   311  			ALTER TABLE t1_test ADD COLUMN i0 INT NOT NULL DEFAULT 0;
   312  		`
   313  		dropT1Statement = `
   314  			DROP TABLE IF EXISTS t1_test
   315  		`
   316  		dropT3Statement = `
   317  			DROP TABLE IF EXISTS t3_test
   318  		`
   319  		dropT4Statement = `
   320  			DROP TABLE IF EXISTS t4_test
   321  		`
   322  		alterExtraColumn = `
   323  			ALTER TABLE t1_test ADD COLUMN extra_column int NOT NULL DEFAULT 0
   324  		`
   325  		createViewDependsOnExtraColumn = `
   326  			CREATE VIEW t1_test_view AS SELECT id, extra_column FROM t1_test
   327  		`
   328  	)
   329  
   330  	testReadTimestamp := func(t *testing.T, uuid string, timestampColumn string) (timestamp string) {
   331  		rs := onlineddl.ReadMigrations(t, &vtParams, uuid)
   332  		require.NotNil(t, rs)
   333  		for _, row := range rs.Named().Rows {
   334  			timestamp = row.AsString(timestampColumn, "")
   335  			assert.NotEmpty(t, timestamp)
   336  		}
   337  		return timestamp
   338  	}
   339  	testTableSequentialTimes := func(t *testing.T, uuid1, uuid2 string) {
   340  		// expect uuid2 to start after uuid1 completes
   341  		t.Run("Compare t1, t2 sequential times", func(t *testing.T) {
   342  			endTime1 := testReadTimestamp(t, uuid1, "completed_timestamp")
   343  			startTime2 := testReadTimestamp(t, uuid2, "started_timestamp")
   344  			assert.GreaterOrEqual(t, startTime2, endTime1)
   345  		})
   346  	}
   347  	testTableCompletionTimes := func(t *testing.T, uuid1, uuid2 string) {
   348  		// expect uuid1 to complete before uuid2
   349  		t.Run("Compare t1, t2 completion times", func(t *testing.T) {
   350  			endTime1 := testReadTimestamp(t, uuid1, "completed_timestamp")
   351  			endTime2 := testReadTimestamp(t, uuid2, "completed_timestamp")
   352  			assert.GreaterOrEqual(t, endTime2, endTime1)
   353  		})
   354  	}
   355  	testAllowConcurrent := func(t *testing.T, name string, uuid string, expect int64) {
   356  		t.Run("verify allow_concurrent: "+name, func(t *testing.T) {
   357  			rs := onlineddl.ReadMigrations(t, &vtParams, uuid)
   358  			require.NotNil(t, rs)
   359  			for _, row := range rs.Named().Rows {
   360  				allowConcurrent := row.AsInt64("allow_concurrent", 0)
   361  				assert.Equal(t, expect, allowConcurrent)
   362  			}
   363  		})
   364  	}
   365  
   366  	// CREATE
   367  	t.Run("CREATE TABLEs t1, t1", func(t *testing.T) {
   368  		{ // The table does not exist
   369  			t1uuid = testOnlineDDLStatement(t, createParams(createT1Statement, ddlStrategy, "vtgate", "just-created", "", false))
   370  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   371  			checkTable(t, t1Name, true)
   372  		}
   373  		{
   374  			// The table does not exist
   375  			t2uuid = testOnlineDDLStatement(t, createParams(createT2Statement, ddlStrategy, "vtgate", "just-created", "", false))
   376  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete)
   377  			checkTable(t, t2Name, true)
   378  		}
   379  		testTableSequentialTimes(t, t1uuid, t2uuid)
   380  	})
   381  	t.Run("Postpone launch CREATE", func(t *testing.T) {
   382  		t1uuid = testOnlineDDLStatement(t, createParams(createT1IfNotExistsStatement, ddlStrategy+" --postpone-launch", "vtgate", "", "", true)) // skip wait
   383  		time.Sleep(2 * time.Second)
   384  		rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid)
   385  		require.NotNil(t, rs)
   386  		for _, row := range rs.Named().Rows {
   387  			postponeLaunch := row.AsInt64("postpone_launch", 0)
   388  			assert.Equal(t, int64(1), postponeLaunch)
   389  		}
   390  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusQueued)
   391  
   392  		t.Run("launch all shards", func(t *testing.T) {
   393  			onlineddl.CheckLaunchMigration(t, &vtParams, shards, t1uuid, "", true)
   394  			rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid)
   395  			require.NotNil(t, rs)
   396  			for _, row := range rs.Named().Rows {
   397  				postponeLaunch := row.AsInt64("postpone_launch", 0)
   398  				assert.Equal(t, int64(0), postponeLaunch)
   399  			}
   400  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   401  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   402  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   403  		})
   404  	})
   405  	t.Run("Postpone launch ALTER", func(t *testing.T) {
   406  		t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" --postpone-launch", "vtgate", "", "", true)) // skip wait
   407  		time.Sleep(2 * time.Second)
   408  		rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid)
   409  		require.NotNil(t, rs)
   410  		for _, row := range rs.Named().Rows {
   411  			postponeLaunch := row.AsInt64("postpone_launch", 0)
   412  			assert.Equal(t, int64(1), postponeLaunch)
   413  		}
   414  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusQueued)
   415  
   416  		t.Run("launch irrelevant UUID", func(t *testing.T) {
   417  			someOtherUUID := "00000000_1111_2222_3333_444444444444"
   418  			onlineddl.CheckLaunchMigration(t, &vtParams, shards, someOtherUUID, "", false)
   419  			time.Sleep(2 * time.Second)
   420  			rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid)
   421  			require.NotNil(t, rs)
   422  			for _, row := range rs.Named().Rows {
   423  				postponeLaunch := row.AsInt64("postpone_launch", 0)
   424  				assert.Equal(t, int64(1), postponeLaunch)
   425  			}
   426  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusQueued)
   427  		})
   428  		t.Run("launch irrelevant shards", func(t *testing.T) {
   429  			onlineddl.CheckLaunchMigration(t, &vtParams, shards, t1uuid, "x,y,z", false)
   430  			time.Sleep(2 * time.Second)
   431  			rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid)
   432  			require.NotNil(t, rs)
   433  			for _, row := range rs.Named().Rows {
   434  				postponeLaunch := row.AsInt64("postpone_launch", 0)
   435  				assert.Equal(t, int64(1), postponeLaunch)
   436  			}
   437  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusQueued)
   438  		})
   439  		t.Run("launch relevant shard", func(t *testing.T) {
   440  			onlineddl.CheckLaunchMigration(t, &vtParams, shards, t1uuid, "x, y, 1", true)
   441  			rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid)
   442  			require.NotNil(t, rs)
   443  			for _, row := range rs.Named().Rows {
   444  				postponeLaunch := row.AsInt64("postpone_launch", 0)
   445  				assert.Equal(t, int64(0), postponeLaunch)
   446  			}
   447  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   448  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   449  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   450  		})
   451  	})
   452  	t.Run("ALTER both tables non-concurrent", func(t *testing.T) {
   453  		t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy, "vtgate", "", "", true)) // skip wait
   454  		t2uuid = testOnlineDDLStatement(t, createParams(trivialAlterT2Statement, ddlStrategy, "vtgate", "", "", true)) // skip wait
   455  
   456  		t.Run("wait for t1 complete", func(t *testing.T) {
   457  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   458  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   459  		})
   460  		t.Run("wait for t1 complete", func(t *testing.T) {
   461  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   462  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   463  		})
   464  		t.Run("check both complete", func(t *testing.T) {
   465  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   466  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete)
   467  		})
   468  		testTableSequentialTimes(t, t1uuid, t2uuid)
   469  	})
   470  	t.Run("ALTER both tables non-concurrent, postponed", func(t *testing.T) {
   471  		t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" -postpone-completion", "vtgate", "", "", true)) // skip wait
   472  		t2uuid = testOnlineDDLStatement(t, createParams(trivialAlterT2Statement, ddlStrategy+" -postpone-completion", "vtgate", "", "", true)) // skip wait
   473  
   474  		testAllowConcurrent(t, "t1", t1uuid, 0)
   475  		t.Run("expect t1 running, t2 queued", func(t *testing.T) {
   476  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   477  			// now that t1 is running, let's unblock t2. We expect it to remain queued.
   478  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t2uuid, true)
   479  			time.Sleep(ensureStateNotChangedTime)
   480  			// t1 should be still running!
   481  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   482  			// non-concurrent -- should be queued!
   483  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady)
   484  		})
   485  		t.Run("complete t1", func(t *testing.T) {
   486  			// Issue a complete and wait for successful completion
   487  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true)
   488  			// This part may take a while, because we depend on vreplication polling
   489  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   490  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   491  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   492  		})
   493  		t.Run("expect t2 to complete", func(t *testing.T) {
   494  			// This part may take a while, because we depend on vreplication polling
   495  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   496  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   497  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete)
   498  		})
   499  		testTableSequentialTimes(t, t1uuid, t2uuid)
   500  	})
   501  
   502  	t.Run("ALTER both tables, elligible for concurrenct", func(t *testing.T) {
   503  		// ALTER TABLE is allowed to run concurrently when no other ALTER is busy with copy state. Our tables are tiny so we expect to find both migrations running
   504  		t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" --allow-concurrent --postpone-completion", "vtgate", "", "", true)) // skip wait
   505  		t2uuid = testOnlineDDLStatement(t, createParams(trivialAlterT2Statement, ddlStrategy+" --allow-concurrent --postpone-completion", "vtgate", "", "", true)) // skip wait
   506  
   507  		testAllowConcurrent(t, "t1", t1uuid, 1)
   508  		testAllowConcurrent(t, "t2", t2uuid, 1)
   509  		t.Run("expect both running", func(t *testing.T) {
   510  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   511  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   512  			time.Sleep(ensureStateNotChangedTime)
   513  			// both should be still running!
   514  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   515  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusRunning)
   516  		})
   517  		t.Run("complete t2", func(t *testing.T) {
   518  			// Issue a complete and wait for successful completion
   519  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t2uuid, true)
   520  			// This part may take a while, because we depend on vreplication polling
   521  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   522  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   523  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete)
   524  		})
   525  		t.Run("complete t1", func(t *testing.T) {
   526  			// t1 should still be running!
   527  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   528  			// Issue a complete and wait for successful completion
   529  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true)
   530  			// This part may take a while, because we depend on vreplication polling
   531  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   532  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   533  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   534  		})
   535  		testTableCompletionTimes(t, t2uuid, t1uuid)
   536  	})
   537  	t.Run("ALTER both tables, elligible for concurrenct, with throttling", func(t *testing.T) {
   538  		onlineddl.ThrottleAllMigrations(t, &vtParams)
   539  		defer onlineddl.UnthrottleAllMigrations(t, &vtParams)
   540  		// ALTER TABLE is allowed to run concurrently when no other ALTER is busy with copy state. Our tables are tiny so we expect to find both migrations running
   541  		t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", "", true)) // skip wait
   542  		t2uuid = testOnlineDDLStatement(t, createParams(trivialAlterT2Statement, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", "", true)) // skip wait
   543  
   544  		testAllowConcurrent(t, "t1", t1uuid, 1)
   545  		testAllowConcurrent(t, "t2", t2uuid, 1)
   546  		t.Run("expect t1 running", func(t *testing.T) {
   547  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   548  			// since all migrations are throttled, t1 migration is not ready_to_complete, hence
   549  			// t2 should not be running
   550  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady)
   551  			time.Sleep(ensureStateNotChangedTime)
   552  			// both should be still running!
   553  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   554  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady)
   555  		})
   556  		t.Run("unthrottle, expect t2 running", func(t *testing.T) {
   557  			onlineddl.UnthrottleAllMigrations(t, &vtParams)
   558  			// t1 should now be ready_to_complete, hence t2 should start running
   559  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, extendedWaitTime, schema.OnlineDDLStatusRunning)
   560  			time.Sleep(ensureStateNotChangedTime)
   561  			// both should be still running!
   562  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   563  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusRunning)
   564  		})
   565  		t.Run("complete t2", func(t *testing.T) {
   566  			// Issue a complete and wait for successful completion
   567  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t2uuid, true)
   568  			// This part may take a while, because we depend on vreplication polling
   569  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   570  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   571  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete)
   572  		})
   573  		t.Run("complete t1", func(t *testing.T) {
   574  			// t1 should still be running!
   575  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   576  			// Issue a complete and wait for successful completion
   577  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true)
   578  			// This part may take a while, because we depend on vreplication polling
   579  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   580  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   581  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   582  		})
   583  		testTableCompletionTimes(t, t2uuid, t1uuid)
   584  	})
   585  	t.Run("REVERT both tables concurrent, postponed", func(t *testing.T) {
   586  		t1uuid = testRevertMigration(t, createRevertParams(t1uuid, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", true))
   587  		t2uuid = testRevertMigration(t, createRevertParams(t2uuid, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", true))
   588  
   589  		testAllowConcurrent(t, "t1", t1uuid, 1)
   590  		t.Run("expect both migrations to run", func(t *testing.T) {
   591  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   592  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   593  		})
   594  		t.Run("test ready-to-complete", func(t *testing.T) {
   595  			rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid)
   596  			require.NotNil(t, rs)
   597  			for _, row := range rs.Named().Rows {
   598  				readyToComplete := row.AsInt64("ready_to_complete", 0)
   599  				assert.Equal(t, int64(1), readyToComplete)
   600  			}
   601  		})
   602  		t.Run("complete t2", func(t *testing.T) {
   603  			// now that both are running, let's unblock t2. We expect it to complete.
   604  			// Issue a complete and wait for successful completion
   605  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t2uuid, true)
   606  			// This part may take a while, because we depend on vreplication polling
   607  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   608  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   609  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete)
   610  		})
   611  		t.Run("complete t1", func(t *testing.T) {
   612  			// t1 should be still running!
   613  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   614  			// Issue a complete and wait for successful completion
   615  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true)
   616  			// This part may take a while, because we depend on vreplication polling
   617  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   618  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   619  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   620  		})
   621  	})
   622  	t.Run("concurrent REVERT vs two non-concurrent DROPs", func(t *testing.T) {
   623  		t1uuid = testRevertMigration(t, createRevertParams(t1uuid, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", true))
   624  		drop3uuid := testOnlineDDLStatement(t, createParams(dropT3Statement, ddlStrategy, "vtgate", "", "", true)) // skip wait
   625  
   626  		testAllowConcurrent(t, "t1", t1uuid, 1)
   627  		testAllowConcurrent(t, "drop3", drop3uuid, 0)
   628  		t.Run("expect t1 migration to run", func(t *testing.T) {
   629  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   630  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   631  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   632  		})
   633  		drop1uuid := testOnlineDDLStatement(t, createParams(dropT1Statement, ddlStrategy, "vtgate", "", "", true)) // skip wait
   634  		t.Run("drop3 complete", func(t *testing.T) {
   635  			// drop3 migration should not block. It can run concurrently to t1, and does not conflict
   636  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop3uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   637  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   638  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop3uuid, schema.OnlineDDLStatusComplete)
   639  		})
   640  		t.Run("drop1 blocked", func(t *testing.T) {
   641  			// drop1 migration should block. It can run concurrently to t1, but conflicts on table name
   642  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady)
   643  			// let's cancel it
   644  			onlineddl.CheckCancelMigration(t, &vtParams, shards, drop1uuid, true)
   645  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop1uuid, normalWaitTime, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled)
   646  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   647  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusCancelled)
   648  		})
   649  		t.Run("complete t1", func(t *testing.T) {
   650  			// t1 should be still running!
   651  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   652  			// Issue a complete and wait for successful completion
   653  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true)
   654  			// This part may take a while, because we depend on vreplication polling
   655  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   656  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   657  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   658  		})
   659  	})
   660  	t.Run("non-concurrent REVERT vs three concurrent drops", func(t *testing.T) {
   661  		t1uuid = testRevertMigration(t, createRevertParams(t1uuid, ddlStrategy+" -postpone-completion", "vtgate", "", true))
   662  		drop3uuid := testOnlineDDLStatement(t, createParams(dropT3Statement, ddlStrategy+" -allow-concurrent", "vtgate", "", "", true))                      // skip wait
   663  		drop4uuid := testOnlineDDLStatement(t, createParams(dropT4Statement, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", "", true)) // skip wait
   664  
   665  		testAllowConcurrent(t, "drop3", drop3uuid, 1)
   666  		t.Run("expect t1 migration to run", func(t *testing.T) {
   667  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   668  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   669  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   670  		})
   671  		drop1uuid := testOnlineDDLStatement(t, createParams(dropT1Statement, ddlStrategy+" -allow-concurrent", "vtgate", "", "", true)) // skip wait
   672  		testAllowConcurrent(t, "drop1", drop1uuid, 1)
   673  		t.Run("t3drop complete", func(t *testing.T) {
   674  			// drop3 migration should not block. It can run concurrently to t1, and does not conflict
   675  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop3uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   676  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   677  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop3uuid, schema.OnlineDDLStatusComplete)
   678  		})
   679  		t.Run("t1drop blocked", func(t *testing.T) {
   680  			// drop1 migration should block. It can run concurrently to t1, but conflicts on table name
   681  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady)
   682  		})
   683  		t.Run("t4 postponed", func(t *testing.T) {
   684  			// drop4 migration should postpone.
   685  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop4uuid, schema.OnlineDDLStatusQueued)
   686  			// Issue a complete and wait for successful completion. drop4 is non-conflicting and should be able to proceed
   687  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, drop4uuid, true)
   688  			// This part may take a while, because we depend on vreplication polling
   689  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop4uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   690  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   691  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop4uuid, schema.OnlineDDLStatusComplete)
   692  		})
   693  		t.Run("complete t1", func(t *testing.T) {
   694  			// t1 should be still running!
   695  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   696  			// Issue a complete and wait for successful completion
   697  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true)
   698  			// This part may take a while, because we depend on vreplication polling
   699  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   700  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   701  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   702  		})
   703  		t.Run("t1drop unblocked", func(t *testing.T) {
   704  			// t1drop should now be unblocked!
   705  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   706  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   707  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusComplete)
   708  			checkTable(t, t1Name, false)
   709  		})
   710  		t.Run("revert t1 drop", func(t *testing.T) {
   711  			revertDrop3uuid := testRevertMigration(t, createRevertParams(drop1uuid, ddlStrategy+" -allow-concurrent", "vtgate", "", true))
   712  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, revertDrop3uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   713  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   714  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, revertDrop3uuid, schema.OnlineDDLStatusComplete)
   715  			checkTable(t, t1Name, true)
   716  		})
   717  	})
   718  	t.Run("conflicting migration does not block other queued migrations", func(t *testing.T) {
   719  		t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy, "vtgate", "", "", false)) // skip wait
   720  		t.Run("trivial t1 migration", func(t *testing.T) {
   721  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   722  			checkTable(t, t1Name, true)
   723  		})
   724  
   725  		t1uuid = testRevertMigration(t, createRevertParams(t1uuid, ddlStrategy+" -postpone-completion", "vtgate", "", true))
   726  		t.Run("expect t1 revert migration to run", func(t *testing.T) {
   727  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   728  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   729  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   730  		})
   731  		drop1uuid := testOnlineDDLStatement(t, createParams(dropT1Statement, ddlStrategy+" -allow-concurrent", "vtgate", "", "", true)) // skip wait
   732  		t.Run("t1drop blocked", func(t *testing.T) {
   733  			time.Sleep(ensureStateNotChangedTime)
   734  			// drop1 migration should block. It can run concurrently to t1, but conflicts on table name
   735  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusReady)
   736  		})
   737  		t.Run("t3 ready to complete", func(t *testing.T) {
   738  			rs := onlineddl.ReadMigrations(t, &vtParams, drop1uuid)
   739  			require.NotNil(t, rs)
   740  			for _, row := range rs.Named().Rows {
   741  				readyToComplete := row.AsInt64("ready_to_complete", 0)
   742  				assert.Equal(t, int64(1), readyToComplete)
   743  			}
   744  		})
   745  		t.Run("t3drop complete", func(t *testing.T) {
   746  			// drop3 migration should not block. It can run concurrently to t1, and does not conflict
   747  			// even though t1drop is blocked! This is the heart of this test
   748  			drop3uuid := testOnlineDDLStatement(t, createParams(dropT3Statement, ddlStrategy+" -allow-concurrent", "vtgate", "", "", false))
   749  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop3uuid, schema.OnlineDDLStatusComplete)
   750  		})
   751  		t.Run("cancel drop1", func(t *testing.T) {
   752  			// drop1 migration should block. It can run concurrently to t1, but conflicts on table name
   753  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusReady)
   754  			// let's cancel it
   755  			onlineddl.CheckCancelMigration(t, &vtParams, shards, drop1uuid, true)
   756  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop1uuid, normalWaitTime, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled)
   757  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   758  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusCancelled)
   759  		})
   760  		t.Run("complete t1", func(t *testing.T) {
   761  			// t1 should be still running!
   762  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning)
   763  			// Issue a complete and wait for successful completion
   764  			onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true)
   765  			// This part may take a while, because we depend on vreplication polling
   766  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   767  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   768  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   769  		})
   770  	})
   771  
   772  	t.Run("Idempotent submission, retry failed migration", func(t *testing.T) {
   773  		uuid := "00000000_1111_2222_3333_444444444444"
   774  		overrideVtctlParams = &cluster.VtctlClientParams{DDLStrategy: ddlStrategy, SkipPreflight: true, UUIDList: uuid, MigrationContext: "idempotent:1111-2222-3333"}
   775  		defer func() { overrideVtctlParams = nil }()
   776  		// create a migration and cancel it. We don't let it complete. We want it in "failed" state
   777  		t.Run("start and fail migration", func(t *testing.T) {
   778  			executedUUID := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" -postpone-completion", "vtctl", "", "", true)) // skip wait
   779  			require.Equal(t, uuid, executedUUID)
   780  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   781  			// let's cancel it
   782  			onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, true)
   783  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled)
   784  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   785  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusCancelled)
   786  		})
   787  
   788  		// now, we submit the exact same migratoin again: same UUID, same migration context.
   789  		t.Run("resubmit migration", func(t *testing.T) {
   790  			executedUUID := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy, "vtctl", "", "", true)) // skip wait
   791  			require.Equal(t, uuid, executedUUID)
   792  
   793  			// expect it to complete
   794  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   795  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   796  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
   797  
   798  			rs := onlineddl.ReadMigrations(t, &vtParams, uuid)
   799  			require.NotNil(t, rs)
   800  			for _, row := range rs.Named().Rows {
   801  				retries := row.AsInt64("retries", 0)
   802  				assert.Greater(t, retries, int64(0))
   803  			}
   804  		})
   805  	})
   806  
   807  	t.Run("Idempotent submission, retry failed migration in singleton context", func(t *testing.T) {
   808  		uuid := "00000000_1111_3333_3333_444444444444"
   809  		ddlStrategy := ddlStrategy + " --singleton-context"
   810  		overrideVtctlParams = &cluster.VtctlClientParams{DDLStrategy: ddlStrategy, SkipPreflight: true, UUIDList: uuid, MigrationContext: "idempotent:1111-3333-3333"}
   811  		defer func() { overrideVtctlParams = nil }()
   812  		// create a migration and cancel it. We don't let it complete. We want it in "failed" state
   813  		t.Run("start and fail migration", func(t *testing.T) {
   814  			executedUUID := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" --postpone-completion", "vtctl", "", "", true)) // skip wait
   815  			require.Equal(t, uuid, executedUUID)
   816  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   817  			// let's cancel it
   818  			onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, true)
   819  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled)
   820  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   821  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusCancelled)
   822  		})
   823  
   824  		// now, we submit the exact same migratoin again: same UUID, same migration context.
   825  		t.Run("resubmit migration", func(t *testing.T) {
   826  			executedUUID := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy, "vtctl", "", "", true)) // skip wait
   827  			require.Equal(t, uuid, executedUUID)
   828  
   829  			// expect it to complete
   830  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   831  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   832  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
   833  
   834  			rs := onlineddl.ReadMigrations(t, &vtParams, uuid)
   835  			require.NotNil(t, rs)
   836  			for _, row := range rs.Named().Rows {
   837  				retries := row.AsInt64("retries", 0)
   838  				assert.Greater(t, retries, int64(0))
   839  			}
   840  		})
   841  	})
   842  
   843  	// INSTANT DDL
   844  	instantDDLCapable, err := capableOf(mysql.InstantAddLastColumnFlavorCapability)
   845  	require.NoError(t, err)
   846  	if instantDDLCapable {
   847  		t.Run("INSTANT DDL: postpone-completion", func(t *testing.T) {
   848  			t1uuid := testOnlineDDLStatement(t, createParams(instantAlterT1Statement, ddlStrategy+" --prefer-instant-ddl --postpone-completion", "vtgate", "", "", true))
   849  
   850  			t.Run("expect t1 queued", func(t *testing.T) {
   851  				// we want to validate that the migration remains queued even after some time passes. It must not move beyond 'queued'
   852  				time.Sleep(ensureStateNotChangedTime)
   853  				onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady)
   854  				onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady)
   855  			})
   856  			t.Run("complete t1", func(t *testing.T) {
   857  				// Issue a complete and wait for successful completion
   858  				onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true)
   859  				status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   860  				fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   861  				onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   862  			})
   863  		})
   864  	}
   865  	// 'mysql' strategy
   866  	t.Run("mysql strategy", func(t *testing.T) {
   867  		t.Run("declarative", func(t *testing.T) {
   868  			t1uuid = testOnlineDDLStatement(t, createParams(createT1Statement, "mysql --declarative", "vtgate", "just-created", "", false))
   869  
   870  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   871  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   872  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   873  			checkTable(t, t1Name, true)
   874  		})
   875  
   876  		t.Run("fail postpone-completion", func(t *testing.T) {
   877  			t1uuid := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, "mysql --postpone-completion", "vtgate", "", "", true))
   878  
   879  			// --postpone-completion not supported in mysql strategy
   880  			time.Sleep(ensureStateNotChangedTime)
   881  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusFailed)
   882  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusFailed)
   883  		})
   884  		t.Run("trivial", func(t *testing.T) {
   885  			t1uuid := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, "mysql", "vtgate", "", "", true))
   886  
   887  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   888  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   889  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   890  
   891  			rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid)
   892  			require.NotNil(t, rs)
   893  			for _, row := range rs.Named().Rows {
   894  				artifacts := row.AsString("artifacts", "-")
   895  				assert.Empty(t, artifacts)
   896  			}
   897  		})
   898  		t.Run("instant", func(t *testing.T) {
   899  			t1uuid := testOnlineDDLStatement(t, createParams(instantAlterT1Statement, "mysql", "vtgate", "", "", true))
   900  
   901  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   902  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   903  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   904  		})
   905  	})
   906  	// in-order-completion
   907  	t.Run("in-order-completion: multiple drops for nonexistent tables and views", func(t *testing.T) {
   908  		u, err := schema.CreateOnlineDDLUUID()
   909  		require.NoError(t, err)
   910  
   911  		sqls := []string{
   912  			fmt.Sprintf("drop table if exists t4_%s", u),
   913  			fmt.Sprintf("drop view  if exists t1_%s", u),
   914  			fmt.Sprintf("drop table if exists t2_%s", u),
   915  			fmt.Sprintf("drop view  if exists t3_%s", u),
   916  		}
   917  		sql := strings.Join(sqls, ";")
   918  		var vuuids []string
   919  		t.Run("drop multiple tables and views, in-order-completion", func(t *testing.T) {
   920  			uuidList := testOnlineDDLStatement(t, createParams(sql, ddlStrategy+" --allow-concurrent --in-order-completion", "vtctl", "", "", true)) // skip wait
   921  			vuuids = strings.Split(uuidList, "\n")
   922  			assert.Equal(t, 4, len(vuuids))
   923  			for _, uuid := range vuuids {
   924  				status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   925  				fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   926  				onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
   927  			}
   928  		})
   929  		require.Equal(t, 4, len(vuuids))
   930  		for i := range vuuids {
   931  			if i > 0 {
   932  				testTableCompletionTimes(t, vuuids[i-1], vuuids[i])
   933  			}
   934  		}
   935  	})
   936  	t.Run("in-order-completion: two new views, one depends on the other", func(t *testing.T) {
   937  		u, err := schema.CreateOnlineDDLUUID()
   938  		require.NoError(t, err)
   939  		v2name := fmt.Sprintf("v2_%s", u)
   940  		createv2 := fmt.Sprintf("create view %s as select id from t1_test", v2name)
   941  		v1name := fmt.Sprintf("v1_%s", u)
   942  		createv1 := fmt.Sprintf("create view %s as select id from %s", v1name, v2name)
   943  
   944  		sql := fmt.Sprintf("%s; %s;", createv2, createv1)
   945  		var vuuids []string
   946  		t.Run("create two views, expect both complete", func(t *testing.T) {
   947  			uuidList := testOnlineDDLStatement(t, createParams(sql, ddlStrategy+" --allow-concurrent --in-order-completion", "vtctl", "", "", true)) // skip wait
   948  			vuuids = strings.Split(uuidList, "\n")
   949  			assert.Equal(t, 2, len(vuuids))
   950  			for _, uuid := range vuuids {
   951  				status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   952  				fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   953  				onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
   954  			}
   955  		})
   956  		require.Equal(t, 2, len(vuuids))
   957  		testTableCompletionTimes(t, vuuids[0], vuuids[1])
   958  	})
   959  	t.Run("in-order-completion: new table column, new view depends on said column", func(t *testing.T) {
   960  		// The VIEW creation can only succeed when the ALTER has completed and the table has the new column
   961  		t1uuid = testOnlineDDLStatement(t, createParams(alterExtraColumn, ddlStrategy+" --allow-concurrent --postpone-completion --in-order-completion", "vtctl", "", "", true))                // skip wait
   962  		v1uuid := testOnlineDDLStatement(t, createParams(createViewDependsOnExtraColumn, ddlStrategy+" --allow-concurrent --postpone-completion --in-order-completion", "vtctl", "", "", true)) // skip wait
   963  
   964  		testAllowConcurrent(t, "t1", t1uuid, 1)
   965  		testAllowConcurrent(t, "v1", v1uuid, 1)
   966  		t.Run("expect table running, expect view ready", func(t *testing.T) {
   967  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   968  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, v1uuid, normalWaitTime, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady)
   969  			time.Sleep(ensureStateNotChangedTime)
   970  			// nothing should change
   971  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning)
   972  			onlineddl.WaitForMigrationStatus(t, &vtParams, shards, v1uuid, normalWaitTime, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady)
   973  		})
   974  		t.Run("complete both", func(t *testing.T) {
   975  			onlineddl.CheckCompleteAllMigrations(t, &vtParams, len(shards)*2)
   976  		})
   977  		t.Run("expect table success", func(t *testing.T) {
   978  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   979  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   980  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete)
   981  		})
   982  		t.Run("expect view success", func(t *testing.T) {
   983  			status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, v1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
   984  			fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
   985  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, v1uuid, schema.OnlineDDLStatusComplete)
   986  		})
   987  		testTableCompletionTimes(t, t1uuid, v1uuid)
   988  	})
   989  }
   990  
   991  func testSingleton(t *testing.T) {
   992  	defer cluster.PanicHandler(t)
   993  	shards = clusterInstance.Keyspaces[0].Shards
   994  	require.Equal(t, 1, len(shards))
   995  
   996  	createParams := func(ddlStatement string, ddlStrategy string, executeStrategy string, migrationContext string, expectHint string, expectError string, skipWait bool) *testOnlineDDLStatementParams {
   997  		return &testOnlineDDLStatementParams{
   998  			ddlStatement:     ddlStatement,
   999  			ddlStrategy:      ddlStrategy,
  1000  			executeStrategy:  executeStrategy,
  1001  			migrationContext: migrationContext,
  1002  			expectHint:       expectHint,
  1003  			expectError:      expectError,
  1004  			skipWait:         skipWait,
  1005  		}
  1006  	}
  1007  
  1008  	createRevertParams := func(revertUUID string, ddlStrategy string, executeStrategy string, migrationContext string, expectError string, skipWait bool) *testRevertMigrationParams {
  1009  		return &testRevertMigrationParams{
  1010  			revertUUID:       revertUUID,
  1011  			executeStrategy:  executeStrategy,
  1012  			ddlStrategy:      ddlStrategy,
  1013  			migrationContext: migrationContext,
  1014  			expectError:      expectError,
  1015  			skipWait:         skipWait,
  1016  		}
  1017  	}
  1018  
  1019  	var (
  1020  		tableName                         = `stress_test`
  1021  		onlineSingletonDDLStrategy        = "online --singleton"
  1022  		onlineSingletonContextDDLStrategy = "online --singleton-context"
  1023  		createStatement                   = `
  1024  		CREATE TABLE stress_test (
  1025  			id bigint(20) not null,
  1026  			rand_val varchar(32) null default '',
  1027  			hint_col varchar(64) not null default 'just-created',
  1028  			created_timestamp timestamp not null default current_timestamp,
  1029  			updates int unsigned not null default 0,
  1030  			PRIMARY KEY (id),
  1031  			key created_idx(created_timestamp),
  1032  			key updates_idx(updates)
  1033  		) ENGINE=InnoDB
  1034  	`
  1035  		// We will run this query with "gh-ost --max-load=Threads_running=1"
  1036  		alterTableThrottlingStatement = `
  1037  		ALTER TABLE stress_test DROP COLUMN created_timestamp
  1038  	`
  1039  		multiAlterTableThrottlingStatement = `
  1040  		ALTER TABLE stress_test ENGINE=InnoDB;
  1041  		ALTER TABLE stress_test ENGINE=InnoDB;
  1042  		ALTER TABLE stress_test ENGINE=InnoDB;
  1043  	`
  1044  		// A trivial statement which must succeed and does not change the schema
  1045  		alterTableTrivialStatement = `
  1046  		ALTER TABLE stress_test ENGINE=InnoDB
  1047  	`
  1048  		dropStatement = `
  1049  	DROP TABLE stress_test
  1050  `
  1051  		dropIfExistsStatement = `
  1052  DROP TABLE IF EXISTS stress_test
  1053  `
  1054  		dropNonexistentTableStatement = `
  1055  		DROP TABLE IF EXISTS t_non_existent
  1056  	`
  1057  		multiDropStatements = `DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t2; DROP TABLE IF EXISTS t3;`
  1058  	)
  1059  
  1060  	var uuids []string
  1061  	// init-cleanup
  1062  	t.Run("init DROP TABLE", func(t *testing.T) {
  1063  		uuid := testOnlineDDLStatement(t, createParams(dropIfExistsStatement, onlineSingletonDDLStrategy, "vtgate", "", "", "", false))
  1064  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1065  		checkTable(t, tableName, false)
  1066  	})
  1067  
  1068  	// CREATE
  1069  	t.Run("CREATE TABLE", func(t *testing.T) {
  1070  		// The table does not exist
  1071  		uuid := testOnlineDDLStatement(t, createParams(createStatement, onlineSingletonDDLStrategy, "vtgate", "", "", "", false))
  1072  		uuids = append(uuids, uuid)
  1073  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1074  		checkTable(t, tableName, true)
  1075  	})
  1076  	t.Run("revert CREATE TABLE", func(t *testing.T) {
  1077  		// The table existed, so it will now be dropped (renamed)
  1078  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], onlineSingletonDDLStrategy, "vtgate", "", "", false))
  1079  		uuids = append(uuids, uuid)
  1080  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1081  		checkTable(t, tableName, false)
  1082  	})
  1083  	t.Run("revert revert CREATE TABLE", func(t *testing.T) {
  1084  		// Table was dropped (renamed) so it will now be restored
  1085  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], onlineSingletonDDLStrategy, "vtgate", "", "", false))
  1086  		uuids = append(uuids, uuid)
  1087  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1088  		checkTable(t, tableName, true)
  1089  	})
  1090  
  1091  	var throttledUUID string
  1092  	t.Run("throttled migration", func(t *testing.T) {
  1093  		throttledUUID = testOnlineDDLStatement(t, createParams(alterTableThrottlingStatement, "gh-ost --singleton --max-load=Threads_running=1", "vtgate", "", "hint_col", "", false))
  1094  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, throttledUUID, schema.OnlineDDLStatusRunning)
  1095  	})
  1096  	t.Run("failed singleton migration, vtgate", func(t *testing.T) {
  1097  		uuid := testOnlineDDLStatement(t, createParams(alterTableThrottlingStatement, "gh-ost --singleton --max-load=Threads_running=1", "vtgate", "", "hint_col", "rejected", true))
  1098  		assert.Empty(t, uuid)
  1099  	})
  1100  	t.Run("failed singleton migration, vtctl", func(t *testing.T) {
  1101  		uuid := testOnlineDDLStatement(t, createParams(alterTableThrottlingStatement, "gh-ost --singleton --max-load=Threads_running=1", "vtctl", "", "hint_col", "rejected", true))
  1102  		assert.Empty(t, uuid)
  1103  	})
  1104  	t.Run("failed revert migration", func(t *testing.T) {
  1105  		uuid := testRevertMigration(t, createRevertParams(throttledUUID, onlineSingletonDDLStrategy, "vtgate", "", "rejected", true))
  1106  		assert.Empty(t, uuid)
  1107  	})
  1108  	t.Run("terminate throttled migration", func(t *testing.T) {
  1109  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, throttledUUID, schema.OnlineDDLStatusRunning)
  1110  		onlineddl.CheckCancelMigration(t, &vtParams, shards, throttledUUID, true)
  1111  		status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, throttledUUID, 20*time.Second, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled)
  1112  		fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
  1113  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, throttledUUID, schema.OnlineDDLStatusCancelled)
  1114  	})
  1115  	t.Run("successful gh-ost alter, vtctl", func(t *testing.T) {
  1116  		uuid := testOnlineDDLStatement(t, createParams(alterTableTrivialStatement, "gh-ost --singleton", "vtctl", "", "hint_col", "", false))
  1117  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1118  		onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, false)
  1119  		onlineddl.CheckRetryMigration(t, &vtParams, shards, uuid, false)
  1120  	})
  1121  	t.Run("successful gh-ost alter, vtgate", func(t *testing.T) {
  1122  		uuid := testOnlineDDLStatement(t, createParams(alterTableTrivialStatement, "gh-ost --singleton", "vtgate", "", "hint_col", "", false))
  1123  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1124  		onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, false)
  1125  		onlineddl.CheckRetryMigration(t, &vtParams, shards, uuid, false)
  1126  	})
  1127  
  1128  	t.Run("successful online alter, vtgate", func(t *testing.T) {
  1129  		uuid := testOnlineDDLStatement(t, createParams(alterTableTrivialStatement, onlineSingletonDDLStrategy, "vtgate", "", "hint_col", "", false))
  1130  		uuids = append(uuids, uuid)
  1131  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1132  		onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, false)
  1133  		onlineddl.CheckRetryMigration(t, &vtParams, shards, uuid, false)
  1134  		checkTable(t, tableName, true)
  1135  	})
  1136  	t.Run("revert ALTER TABLE, vttablet", func(t *testing.T) {
  1137  		// The table existed, so it will now be dropped (renamed)
  1138  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], onlineSingletonDDLStrategy, "vtctl", "", "", false))
  1139  		uuids = append(uuids, uuid)
  1140  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1141  		checkTable(t, tableName, true)
  1142  	})
  1143  
  1144  	var throttledUUIDs []string
  1145  	// singleton-context
  1146  	t.Run("throttled migrations, singleton-context", func(t *testing.T) {
  1147  		uuidList := testOnlineDDLStatement(t, createParams(multiAlterTableThrottlingStatement, "gh-ost --singleton-context --max-load=Threads_running=1", "vtctl", "", "hint_col", "", false))
  1148  		throttledUUIDs = strings.Split(uuidList, "\n")
  1149  		assert.Equal(t, 3, len(throttledUUIDs))
  1150  		for _, uuid := range throttledUUIDs {
  1151  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady, schema.OnlineDDLStatusRunning)
  1152  		}
  1153  	})
  1154  	t.Run("failed migrations, singleton-context", func(t *testing.T) {
  1155  		_ = testOnlineDDLStatement(t, createParams(multiAlterTableThrottlingStatement, "gh-ost --singleton-context --max-load=Threads_running=1", "vtctl", "", "hint_col", "rejected", false))
  1156  	})
  1157  	t.Run("terminate throttled migrations", func(t *testing.T) {
  1158  		for _, uuid := range throttledUUIDs {
  1159  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady, schema.OnlineDDLStatusRunning)
  1160  			onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, true)
  1161  		}
  1162  		time.Sleep(2 * time.Second)
  1163  		for _, uuid := range throttledUUIDs {
  1164  			uuid = strings.TrimSpace(uuid)
  1165  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled)
  1166  		}
  1167  	})
  1168  
  1169  	t.Run("successful multiple statement, singleton-context, vtctl", func(t *testing.T) {
  1170  		uuidList := testOnlineDDLStatement(t, createParams(multiDropStatements, onlineSingletonContextDDLStrategy, "vtctl", "", "", "", false))
  1171  		uuidSlice := strings.Split(uuidList, "\n")
  1172  		assert.Equal(t, 3, len(uuidSlice))
  1173  		for _, uuid := range uuidSlice {
  1174  			uuid = strings.TrimSpace(uuid)
  1175  			onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1176  		}
  1177  	})
  1178  
  1179  	//DROP
  1180  
  1181  	t.Run("online DROP TABLE", func(t *testing.T) {
  1182  		uuid := testOnlineDDLStatement(t, createParams(dropStatement, onlineSingletonDDLStrategy, "vtgate", "", "", "", false))
  1183  		uuids = append(uuids, uuid)
  1184  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1185  		checkTable(t, tableName, false)
  1186  	})
  1187  	t.Run("revert DROP TABLE", func(t *testing.T) {
  1188  		// This will recreate the table (well, actually, rename it back into place)
  1189  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], onlineSingletonDDLStrategy, "vttablet", "", "", false))
  1190  		uuids = append(uuids, uuid)
  1191  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1192  		checkTable(t, tableName, true)
  1193  	})
  1194  
  1195  	t.Run("fail concurrent singleton, vtgate", func(t *testing.T) {
  1196  		uuid := testOnlineDDLStatement(t, createParams(alterTableTrivialStatement, "vitess --postpone-completion --singleton", "vtgate", "", "hint_col", "", true))
  1197  		uuids = append(uuids, uuid)
  1198  		_ = testOnlineDDLStatement(t, createParams(dropNonexistentTableStatement, "vitess --singleton", "vtgate", "", "hint_col", "rejected", true))
  1199  		onlineddl.CheckCompleteAllMigrations(t, &vtParams, len(shards))
  1200  		status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, 20*time.Second, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
  1201  		fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
  1202  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1203  	})
  1204  	t.Run("fail concurrent singleton-context with revert", func(t *testing.T) {
  1205  		revertUUID := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], "vitess --allow-concurrent --postpone-completion --singleton-context", "vtctl", "rev:ctx", "", false))
  1206  		onlineddl.WaitForMigrationStatus(t, &vtParams, shards, revertUUID, 20*time.Second, schema.OnlineDDLStatusRunning)
  1207  		// revert is running
  1208  		_ = testOnlineDDLStatement(t, createParams(dropNonexistentTableStatement, "vitess --allow-concurrent --singleton-context", "vtctl", "migrate:ctx", "", "rejected", true))
  1209  		onlineddl.CheckCancelMigration(t, &vtParams, shards, revertUUID, true)
  1210  		status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, revertUUID, 20*time.Second, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled)
  1211  		fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
  1212  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, revertUUID, schema.OnlineDDLStatusCancelled)
  1213  	})
  1214  	t.Run("success concurrent singleton-context with no-context revert", func(t *testing.T) {
  1215  		revertUUID := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], "vitess --allow-concurrent --postpone-completion", "vtctl", "rev:ctx", "", false))
  1216  		onlineddl.WaitForMigrationStatus(t, &vtParams, shards, revertUUID, 20*time.Second, schema.OnlineDDLStatusRunning)
  1217  		// revert is running but has no --singleton-context. Our next migration should be able to run.
  1218  		uuid := testOnlineDDLStatement(t, createParams(dropNonexistentTableStatement, "vitess --allow-concurrent --singleton-context", "vtctl", "migrate:ctx", "", "", false))
  1219  		uuids = append(uuids, uuid)
  1220  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1221  		onlineddl.CheckCancelMigration(t, &vtParams, shards, revertUUID, true)
  1222  		status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, revertUUID, 20*time.Second, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled)
  1223  		fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
  1224  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, revertUUID, schema.OnlineDDLStatusCancelled)
  1225  	})
  1226  }
  1227  func testDeclarative(t *testing.T) {
  1228  	defer cluster.PanicHandler(t)
  1229  	shards = clusterInstance.Keyspaces[0].Shards
  1230  	require.Equal(t, 1, len(shards))
  1231  
  1232  	var (
  1233  		tableName         = `stress_test`
  1234  		viewBaseTableName = `view_base_table_test`
  1235  		viewName          = `view_test`
  1236  		migrationContext  = "1111-2222-3333"
  1237  		createStatement1  = `
  1238  			CREATE TABLE stress_test (
  1239  				id bigint(20) not null,
  1240  				rand_val varchar(32) null default '',
  1241  				hint_col varchar(64) not null default 'create1',
  1242  				created_timestamp timestamp not null default current_timestamp,
  1243  				updates int unsigned not null default 0,
  1244  				PRIMARY KEY (id),
  1245  				key created_idx(created_timestamp),
  1246  				key updates_idx(updates)
  1247  			) ENGINE=InnoDB
  1248  		`
  1249  		createStatement2 = `
  1250  			CREATE TABLE stress_test (
  1251  				id bigint(20) not null,
  1252  				rand_val varchar(32) null default '',
  1253  				hint_col varchar(64) not null default 'create2',
  1254  				created_timestamp timestamp not null default current_timestamp,
  1255  				updates int unsigned not null default 0,
  1256  				PRIMARY KEY (id),
  1257  				key created_idx(created_timestamp),
  1258  				key updates_idx(updates)
  1259  			) ENGINE=InnoDB
  1260  		`
  1261  		createIfNotExistsStatement = `
  1262  			CREATE TABLE IF NOT EXISTS stress_test (
  1263  				id bigint(20) not null,
  1264  				PRIMARY KEY (id)
  1265  			) ENGINE=InnoDB
  1266  		`
  1267  		createStatementZeroDate = `
  1268  			CREATE TABLE zerodate_test (
  1269  				id bigint(20) not null,
  1270  				hint_col varchar(64) not null default 'create_with_zero',
  1271  				zero_datetime datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  1272  				PRIMARY KEY (id)
  1273  			) ENGINE=InnoDB
  1274  		`
  1275  		createStatementZeroDate2 = `
  1276  			CREATE TABLE zerodate_test (
  1277  				id bigint(20) not null,
  1278  				i int not null default 0,
  1279  				hint_col varchar(64) not null default 'create_with_zero2',
  1280  				zero_datetime datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  1281  				zero_datetime2 datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  1282  				PRIMARY KEY (id)
  1283  			) ENGINE=InnoDB
  1284  		`
  1285  		dropZeroDateStatement = `
  1286  			DROP TABLE zerodate_test
  1287  		`
  1288  		dropStatement = `
  1289  			DROP TABLE stress_test
  1290  		`
  1291  		dropIfExistsStatement = `
  1292  			DROP TABLE IF EXISTS stress_test
  1293  		`
  1294  		alterStatement = `
  1295  			ALTER TABLE stress_test modify hint_col varchar(64) not null default 'this-should-fail'
  1296  		`
  1297  		trivialAlterStatement = `
  1298  			ALTER TABLE stress_test ENGINE=InnoDB
  1299  		`
  1300  		dropViewBaseTableStatement = `
  1301  			DROP TABLE IF EXISTS view_base_table_test
  1302  		`
  1303  		createViewBaseTableStatement = `
  1304  			CREATE TABLE view_base_table_test (id INT PRIMARY KEY)
  1305  		`
  1306  		createViewStatement1 = `
  1307  			CREATE VIEW view_test AS SELECT 'success_create1' AS msg FROM view_base_table_test
  1308  		`
  1309  		createViewStatement2 = `
  1310  			CREATE VIEW view_test AS SELECT 'success_create2' AS msg FROM view_base_table_test
  1311  		`
  1312  		createOrReplaceViewStatement = `
  1313  			CREATE OR REPLACE VIEW view_test AS SELECT 'success_replace' AS msg FROM view_base_table_test
  1314  		`
  1315  		alterViewStatement = `
  1316  			ALTER VIEW view_test AS SELECT 'success_alter' AS msg FROM view_base_table_test
  1317  		`
  1318  		dropViewStatement = `
  1319  			DROP VIEW view_test
  1320  		`
  1321  		dropViewIfExistsStatement = `
  1322  			DROP VIEW IF EXISTS view_test
  1323  		`
  1324  		insertRowStatement = `
  1325  			INSERT IGNORE INTO stress_test (id, rand_val) VALUES (%d, left(md5(rand()), 8))
  1326  		`
  1327  		updateRowStatement = `
  1328  			UPDATE stress_test SET updates=updates+1 WHERE id=%d
  1329  		`
  1330  		deleteRowStatement = `
  1331  			DELETE FROM stress_test WHERE id=%d AND updates=1
  1332  		`
  1333  		// We use CAST(SUM(updates) AS SIGNED) because SUM() returns a DECIMAL datatype, and we want to read a SIGNED INTEGER type
  1334  		selectCountRowsStatement = `
  1335  			SELECT COUNT(*) AS num_rows, CAST(SUM(updates) AS SIGNED) AS sum_updates FROM stress_test
  1336  		`
  1337  		truncateStatement = `
  1338  			TRUNCATE TABLE stress_test
  1339  		`
  1340  		writeMetrics WriteMetrics
  1341  		maxTableRows = 4096
  1342  	)
  1343  
  1344  	declarativeStrategy := "online -declarative"
  1345  	var uuids []string
  1346  
  1347  	generateInsert := func(t *testing.T, conn *mysql.Conn) error {
  1348  		id := rand.Int31n(int32(maxTableRows))
  1349  		query := fmt.Sprintf(insertRowStatement, id)
  1350  		qr, err := conn.ExecuteFetch(query, 1000, true)
  1351  
  1352  		func() {
  1353  			writeMetrics.mu.Lock()
  1354  			defer writeMetrics.mu.Unlock()
  1355  
  1356  			writeMetrics.insertsAttempts++
  1357  			if err != nil {
  1358  				writeMetrics.insertsFailures++
  1359  				return
  1360  			}
  1361  			assert.Less(t, qr.RowsAffected, uint64(2))
  1362  			if qr.RowsAffected == 0 {
  1363  				writeMetrics.insertsNoops++
  1364  				return
  1365  			}
  1366  			writeMetrics.inserts++
  1367  		}()
  1368  		return err
  1369  	}
  1370  
  1371  	generateUpdate := func(t *testing.T, conn *mysql.Conn) error {
  1372  		id := rand.Int31n(int32(maxTableRows))
  1373  		query := fmt.Sprintf(updateRowStatement, id)
  1374  		qr, err := conn.ExecuteFetch(query, 1000, true)
  1375  
  1376  		func() {
  1377  			writeMetrics.mu.Lock()
  1378  			defer writeMetrics.mu.Unlock()
  1379  
  1380  			writeMetrics.updatesAttempts++
  1381  			if err != nil {
  1382  				writeMetrics.updatesFailures++
  1383  				return
  1384  			}
  1385  			assert.Less(t, qr.RowsAffected, uint64(2))
  1386  			if qr.RowsAffected == 0 {
  1387  				writeMetrics.updatesNoops++
  1388  				return
  1389  			}
  1390  			writeMetrics.updates++
  1391  		}()
  1392  		return err
  1393  	}
  1394  
  1395  	generateDelete := func(t *testing.T, conn *mysql.Conn) error {
  1396  		id := rand.Int31n(int32(maxTableRows))
  1397  		query := fmt.Sprintf(deleteRowStatement, id)
  1398  		qr, err := conn.ExecuteFetch(query, 1000, true)
  1399  
  1400  		func() {
  1401  			writeMetrics.mu.Lock()
  1402  			defer writeMetrics.mu.Unlock()
  1403  
  1404  			writeMetrics.deletesAttempts++
  1405  			if err != nil {
  1406  				writeMetrics.deletesFailures++
  1407  				return
  1408  			}
  1409  			assert.Less(t, qr.RowsAffected, uint64(2))
  1410  			if qr.RowsAffected == 0 {
  1411  				writeMetrics.deletesNoops++
  1412  				return
  1413  			}
  1414  			writeMetrics.deletes++
  1415  		}()
  1416  		return err
  1417  	}
  1418  
  1419  	initTable := func(t *testing.T) {
  1420  		log.Infof("initTable begin")
  1421  		defer log.Infof("initTable complete")
  1422  
  1423  		ctx := context.Background()
  1424  		conn, err := mysql.Connect(ctx, &vtParams)
  1425  		require.Nil(t, err)
  1426  		defer conn.Close()
  1427  
  1428  		writeMetrics.Clear()
  1429  		_, err = conn.ExecuteFetch(truncateStatement, 1000, true)
  1430  		require.Nil(t, err)
  1431  
  1432  		for i := 0; i < maxTableRows/2; i++ {
  1433  			generateInsert(t, conn)
  1434  		}
  1435  		for i := 0; i < maxTableRows/4; i++ {
  1436  			generateUpdate(t, conn)
  1437  		}
  1438  		for i := 0; i < maxTableRows/4; i++ {
  1439  			generateDelete(t, conn)
  1440  		}
  1441  	}
  1442  
  1443  	testSelectTableMetrics := func(t *testing.T) {
  1444  		writeMetrics.mu.Lock()
  1445  		defer writeMetrics.mu.Unlock()
  1446  
  1447  		log.Infof("%s", writeMetrics.String())
  1448  
  1449  		ctx := context.Background()
  1450  		conn, err := mysql.Connect(ctx, &vtParams)
  1451  		require.Nil(t, err)
  1452  		defer conn.Close()
  1453  
  1454  		rs, err := conn.ExecuteFetch(selectCountRowsStatement, 1000, true)
  1455  		require.Nil(t, err)
  1456  
  1457  		row := rs.Named().Row()
  1458  		require.NotNil(t, row)
  1459  		log.Infof("testSelectTableMetrics, row: %v", row)
  1460  		numRows := row.AsInt64("num_rows", 0)
  1461  		sumUpdates := row.AsInt64("sum_updates", 0)
  1462  
  1463  		assert.NotZero(t, numRows)
  1464  		assert.NotZero(t, sumUpdates)
  1465  		assert.NotZero(t, writeMetrics.inserts)
  1466  		assert.NotZero(t, writeMetrics.deletes)
  1467  		assert.NotZero(t, writeMetrics.updates)
  1468  		assert.Equal(t, writeMetrics.inserts-writeMetrics.deletes, numRows)
  1469  		assert.Equal(t, writeMetrics.updates-writeMetrics.deletes, sumUpdates) // because we DELETE WHERE updates=1
  1470  	}
  1471  
  1472  	testOnlineDDL := func(t *testing.T, alterStatement string, ddlStrategy string, executeStrategy string, expectHint string, expectError string) (uuid string) {
  1473  		params := &testOnlineDDLStatementParams{
  1474  			ddlStatement:    alterStatement,
  1475  			ddlStrategy:     ddlStrategy,
  1476  			executeStrategy: executeStrategy,
  1477  			expectHint:      expectHint,
  1478  			expectError:     expectError,
  1479  		}
  1480  		if executeStrategy != "vtgate" {
  1481  			params.migrationContext = migrationContext
  1482  		}
  1483  		return testOnlineDDLStatement(t, params)
  1484  	}
  1485  	createRevertParams := func(revertUUID string) *testRevertMigrationParams {
  1486  		return &testRevertMigrationParams{
  1487  			revertUUID:      revertUUID,
  1488  			executeStrategy: "vtctl",
  1489  			ddlStrategy:     string(schema.DDLStrategyOnline),
  1490  		}
  1491  	}
  1492  
  1493  	// init-cleaup
  1494  	t.Run("init: drop table", func(t *testing.T) {
  1495  		// IF EXISTS is not supported in -declarative
  1496  		uuid := testOnlineDDL(t, dropIfExistsStatement, "online", "vtgate", "", "")
  1497  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1498  	})
  1499  	t.Run("init: drop view base table", func(t *testing.T) {
  1500  		// IF EXISTS is not supported in -declarative
  1501  		uuid := testOnlineDDL(t, dropViewBaseTableStatement, "online", "vtgate", "", "")
  1502  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1503  	})
  1504  
  1505  	// VIEWS
  1506  	t.Run("create base table for view", func(t *testing.T) {
  1507  		uuid := testOnlineDDL(t, createViewBaseTableStatement, declarativeStrategy, "vtgate", "", "")
  1508  		uuids = append(uuids, uuid)
  1509  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1510  		checkTable(t, viewBaseTableName, true)
  1511  	})
  1512  	// CREATE VIEW 1
  1513  	t.Run("declarative CREATE VIEW where table does not exist", func(t *testing.T) {
  1514  		// The table does not exist
  1515  		uuid := testOnlineDDL(t, createViewStatement1, declarativeStrategy, "vtgate", "success_create1", "")
  1516  		uuids = append(uuids, uuid)
  1517  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1518  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1519  		checkTable(t, viewName, true)
  1520  	})
  1521  	// CREATE VIEW 1 again, noop
  1522  	t.Run("declarative CREATE VIEW with no changes where view exists", func(t *testing.T) {
  1523  		// The exists with exact same schema
  1524  		uuid := testOnlineDDL(t, createViewStatement1, declarativeStrategy, "vtgate", "success_create1", "")
  1525  		uuids = append(uuids, uuid)
  1526  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1527  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, false)
  1528  		checkTable(t, viewName, true)
  1529  	})
  1530  	t.Run("revert CREATE VIEW expecting noop", func(t *testing.T) {
  1531  		// Reverting a noop changes nothing
  1532  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1533  		uuids = append(uuids, uuid)
  1534  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1535  		checkMigratedTable(t, viewName, "success_create1")
  1536  		checkTable(t, viewName, true)
  1537  	})
  1538  	// CREATE OR REPLACE VIEW
  1539  	t.Run("CREATE OR REPLACE VIEW expecting failure", func(t *testing.T) {
  1540  		// IF NOT EXISTS is not supported in -declarative
  1541  		uuid := testOnlineDDL(t, createOrReplaceViewStatement, declarativeStrategy, "vtgate", "", "")
  1542  		uuids = append(uuids, uuid)
  1543  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed)
  1544  		checkMigratedTable(t, viewName, "success_create1")
  1545  		checkTable(t, viewName, true)
  1546  	})
  1547  	t.Run("ALTER VIEW expecting failure", func(t *testing.T) {
  1548  		// IF NOT EXISTS is not supported in -declarative
  1549  		uuid := testOnlineDDL(t, alterViewStatement, declarativeStrategy, "vtgate", "", "")
  1550  		uuids = append(uuids, uuid)
  1551  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed)
  1552  		checkMigratedTable(t, viewName, "success_create1")
  1553  		checkTable(t, viewName, true)
  1554  	})
  1555  	t.Run("DROP VIEW IF EXISTS expecting failure", func(t *testing.T) {
  1556  		// IF NOT EXISTS is not supported in -declarative
  1557  		uuid := testOnlineDDL(t, dropViewIfExistsStatement, declarativeStrategy, "vtgate", "", "")
  1558  		uuids = append(uuids, uuid)
  1559  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed)
  1560  		checkMigratedTable(t, viewName, "success_create1")
  1561  		checkTable(t, viewName, true)
  1562  	})
  1563  	t.Run("declarative DROP VIEW", func(t *testing.T) {
  1564  		uuid := testOnlineDDL(t, dropViewStatement, declarativeStrategy, "vtgate", "", "")
  1565  		uuids = append(uuids, uuid)
  1566  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1567  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1568  		checkTable(t, viewName, false)
  1569  	})
  1570  	// View dropped. Let's start afresh.
  1571  
  1572  	// CREATE VIEW1
  1573  	t.Run("declarative CREATE VIEW where view does not exist", func(t *testing.T) {
  1574  		// The table does not exist
  1575  		uuid := testOnlineDDL(t, createViewStatement1, declarativeStrategy, "vtgate", "success_create1", "")
  1576  		uuids = append(uuids, uuid)
  1577  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1578  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1579  		checkTable(t, viewName, true)
  1580  	})
  1581  	// CREATE VIEW2: Change view
  1582  	t.Run("declarative CREATE VIEW with changes where view exists", func(t *testing.T) {
  1583  		// The table exists with different schema
  1584  		uuid := testOnlineDDL(t, createViewStatement2, declarativeStrategy, "vtgate", "success_create2", "")
  1585  		uuids = append(uuids, uuid)
  1586  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1587  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1588  		checkTable(t, viewName, true)
  1589  	})
  1590  	t.Run("revert CREATE VIEW expecting previous schema", func(t *testing.T) {
  1591  		// Reverting back to 1st version
  1592  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1593  		uuids = append(uuids, uuid)
  1594  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1595  		checkMigratedTable(t, viewName, "success_create1")
  1596  		checkTable(t, viewName, true)
  1597  	})
  1598  	t.Run("declarative DROP VIEW", func(t *testing.T) {
  1599  		// Table exists
  1600  		uuid := testOnlineDDL(t, dropViewStatement, declarativeStrategy, "vtgate", "", "")
  1601  		uuids = append(uuids, uuid)
  1602  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1603  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1604  		checkTable(t, viewName, false)
  1605  	})
  1606  	t.Run("revert DROP VIEW", func(t *testing.T) {
  1607  		// This will recreate the table (well, actually, rename it back into place)
  1608  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1609  		uuids = append(uuids, uuid)
  1610  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1611  		checkTable(t, viewName, true)
  1612  		checkMigratedTable(t, viewName, "success_create1")
  1613  	})
  1614  	t.Run("revert revert DROP VIEW", func(t *testing.T) {
  1615  		// This will reapply DROP VIEW
  1616  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1617  		uuids = append(uuids, uuid)
  1618  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1619  		checkTable(t, viewName, false)
  1620  	})
  1621  	t.Run("declarative DROP VIEW where view does not exist", func(t *testing.T) {
  1622  		uuid := testOnlineDDL(t, dropViewStatement, declarativeStrategy, "vtgate", "", "")
  1623  		uuids = append(uuids, uuid)
  1624  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1625  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, false)
  1626  		checkTable(t, viewName, false)
  1627  	})
  1628  	t.Run("revert DROP VIEW where view did not exist", func(t *testing.T) {
  1629  		// Table will not be recreated because it didn't exist during the previous DROP VIEW
  1630  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1631  		uuids = append(uuids, uuid)
  1632  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1633  		checkTable(t, viewName, false)
  1634  	})
  1635  	// View dropped. Let's start afresh.
  1636  
  1637  	// TABLES
  1638  
  1639  	// CREATE1
  1640  	t.Run("declarative CREATE TABLE where table does not exist", func(t *testing.T) {
  1641  		// The table does not exist
  1642  		uuid := testOnlineDDL(t, createStatement1, declarativeStrategy, "vtgate", "create1", "")
  1643  		uuids = append(uuids, uuid)
  1644  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1645  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1646  		checkTable(t, tableName, true)
  1647  		initTable(t)
  1648  		testSelectTableMetrics(t)
  1649  	})
  1650  	// CREATE1 again, noop
  1651  	t.Run("declarative CREATE TABLE with no changes where table exists", func(t *testing.T) {
  1652  		// The exists with exact same schema
  1653  		uuid := testOnlineDDL(t, createStatement1, declarativeStrategy, "vtgate", "create1", "")
  1654  		uuids = append(uuids, uuid)
  1655  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1656  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, false)
  1657  		checkTable(t, tableName, true)
  1658  		testSelectTableMetrics(t)
  1659  	})
  1660  	t.Run("revert CREATE TABLE expecting noop", func(t *testing.T) {
  1661  		// Reverting a noop changes nothing
  1662  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1663  		uuids = append(uuids, uuid)
  1664  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1665  		checkMigratedTable(t, tableName, "create1")
  1666  		checkTable(t, tableName, true)
  1667  		testSelectTableMetrics(t)
  1668  	})
  1669  	t.Run("declarative DROP TABLE", func(t *testing.T) {
  1670  		uuid := testOnlineDDL(t, dropStatement, declarativeStrategy, "vtgate", "", "")
  1671  		uuids = append(uuids, uuid)
  1672  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1673  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1674  		checkTable(t, tableName, false)
  1675  	})
  1676  	// Table dropped. Let's start afresh.
  1677  
  1678  	// CREATE1
  1679  	t.Run("declarative CREATE TABLE where table does not exist", func(t *testing.T) {
  1680  		// The table does not exist
  1681  		uuid := testOnlineDDL(t, createStatement1, declarativeStrategy, "vtgate", "create1", "")
  1682  		uuids = append(uuids, uuid)
  1683  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1684  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1685  		checkTable(t, tableName, true)
  1686  		initTable(t)
  1687  		testSelectTableMetrics(t)
  1688  	})
  1689  	// CREATE2: Change schema
  1690  	t.Run("declarative CREATE TABLE with changes where table exists", func(t *testing.T) {
  1691  		// The table exists with different schema
  1692  		uuid := testOnlineDDL(t, createStatement2, declarativeStrategy, "vtgate", "create2", "")
  1693  		uuids = append(uuids, uuid)
  1694  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1695  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1696  		checkTable(t, tableName, true)
  1697  		testSelectTableMetrics(t)
  1698  	})
  1699  	t.Run("revert CREATE TABLE expecting previous schema", func(t *testing.T) {
  1700  		// Reverting back to 1st version
  1701  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1702  		uuids = append(uuids, uuid)
  1703  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1704  		checkMigratedTable(t, tableName, "create1")
  1705  		checkTable(t, tableName, true)
  1706  		testSelectTableMetrics(t)
  1707  	})
  1708  	t.Run("declarative DROP TABLE", func(t *testing.T) {
  1709  		// Table exists
  1710  		uuid := testOnlineDDL(t, dropStatement, declarativeStrategy, "vtgate", "", "")
  1711  		uuids = append(uuids, uuid)
  1712  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1713  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1714  		checkTable(t, tableName, false)
  1715  	})
  1716  	t.Run("revert DROP TABLE", func(t *testing.T) {
  1717  		// This will recreate the table (well, actually, rename it back into place)
  1718  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1719  		uuids = append(uuids, uuid)
  1720  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1721  		checkTable(t, tableName, true)
  1722  		checkMigratedTable(t, tableName, "create1")
  1723  		testSelectTableMetrics(t)
  1724  	})
  1725  	t.Run("revert revert DROP TABLE", func(t *testing.T) {
  1726  		// This will reapply DROP TABLE
  1727  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1728  		uuids = append(uuids, uuid)
  1729  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1730  		checkTable(t, tableName, false)
  1731  	})
  1732  	t.Run("declarative DROP TABLE where table does not exist", func(t *testing.T) {
  1733  		uuid := testOnlineDDL(t, dropStatement, declarativeStrategy, "vtgate", "", "")
  1734  		uuids = append(uuids, uuid)
  1735  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1736  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, false)
  1737  		checkTable(t, tableName, false)
  1738  	})
  1739  	t.Run("revert DROP TABLE where table did not exist", func(t *testing.T) {
  1740  		// Table will not be recreated because it didn't exist during the previous DROP TABLE
  1741  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1742  		uuids = append(uuids, uuid)
  1743  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1744  		checkTable(t, tableName, false)
  1745  	})
  1746  	// Table dropped. Let's start afresh.
  1747  
  1748  	// CREATE1
  1749  	t.Run("declarative CREATE TABLE where table does not exist", func(t *testing.T) {
  1750  		// The table does not exist
  1751  		uuid := testOnlineDDL(t, createStatement1, declarativeStrategy, "vtgate", "create1", "")
  1752  		uuids = append(uuids, uuid)
  1753  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1754  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1755  		checkTable(t, tableName, true)
  1756  		initTable(t)
  1757  		testSelectTableMetrics(t)
  1758  	})
  1759  	// CREATE2
  1760  	t.Run("declarative CREATE TABLE with changes where table exists", func(t *testing.T) {
  1761  		// The table exists but with different schema
  1762  		uuid := testOnlineDDL(t, createStatement2, declarativeStrategy, "vtgate", "create2", "")
  1763  		uuids = append(uuids, uuid)
  1764  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1765  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1766  		checkTable(t, tableName, true)
  1767  		testSelectTableMetrics(t)
  1768  	})
  1769  	// CREATE1 again
  1770  	t.Run("declarative CREATE TABLE again with changes where table exists", func(t *testing.T) {
  1771  		// The table exists but with different schema
  1772  		uuid := testOnlineDDL(t, createStatement1, declarativeStrategy, "vtgate", "create1", "")
  1773  		uuids = append(uuids, uuid)
  1774  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1775  		onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true)
  1776  		checkTable(t, tableName, true)
  1777  		testSelectTableMetrics(t)
  1778  	})
  1779  	t.Run("revert CREATE TABLE expecting previous schema", func(t *testing.T) {
  1780  		// Reverting back to previous version
  1781  		uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1]))
  1782  		uuids = append(uuids, uuid)
  1783  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1784  		checkMigratedTable(t, tableName, "create2")
  1785  		checkTable(t, tableName, true)
  1786  		testSelectTableMetrics(t)
  1787  	})
  1788  	t.Run("ALTER TABLE expecting failure", func(t *testing.T) {
  1789  		// ALTER is not supported in -declarative
  1790  		uuid := testOnlineDDL(t, alterStatement, declarativeStrategy, "vtgate", "", "")
  1791  		uuids = append(uuids, uuid)
  1792  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed)
  1793  		checkMigratedTable(t, tableName, "create2")
  1794  		checkTable(t, tableName, true)
  1795  		testSelectTableMetrics(t)
  1796  	})
  1797  	t.Run("CREATE TABLE IF NOT EXISTS expecting failure", func(t *testing.T) {
  1798  		// IF NOT EXISTS is not supported in -declarative
  1799  		uuid := testOnlineDDL(t, createIfNotExistsStatement, declarativeStrategy, "vtgate", "", "")
  1800  		uuids = append(uuids, uuid)
  1801  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed)
  1802  		checkMigratedTable(t, tableName, "create2")
  1803  		checkTable(t, tableName, true)
  1804  		testSelectTableMetrics(t)
  1805  	})
  1806  	t.Run("DROP TABLE IF EXISTS expecting failure", func(t *testing.T) {
  1807  		// IF EXISTS is not supported in -declarative
  1808  		uuid := testOnlineDDL(t, dropIfExistsStatement, declarativeStrategy, "vtgate", "", "")
  1809  		uuids = append(uuids, uuid)
  1810  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed)
  1811  		checkMigratedTable(t, tableName, "create2")
  1812  		checkTable(t, tableName, true)
  1813  		testSelectTableMetrics(t)
  1814  	})
  1815  	t.Run("CREATE TABLE IF NOT EXISTS non-declarative is successful", func(t *testing.T) {
  1816  		// IF NOT EXISTS is supported in non-declarative mode. Just verifying that the statement itself is good,
  1817  		// so that the failure we tested for, above, actually tests the "declarative" logic, rather than some
  1818  		// unrelated error.
  1819  		uuid := testOnlineDDL(t, createIfNotExistsStatement, "online", "vtgate", "", "")
  1820  		uuids = append(uuids, uuid)
  1821  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1822  		// the table existed, so we expect no changes in this non-declarative DDL
  1823  		checkMigratedTable(t, tableName, "create2")
  1824  		checkTable(t, tableName, true)
  1825  		testSelectTableMetrics(t)
  1826  	})
  1827  	t.Run("CREATE TABLE with zero date and --allow-zero-in-date is successful", func(t *testing.T) {
  1828  		uuid := testOnlineDDL(t, createStatementZeroDate, "online --allow-zero-in-date", "vtgate", "", "")
  1829  		uuids = append(uuids, uuid)
  1830  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1831  		checkMigratedTable(t, "zerodate_test", "create_with_zero")
  1832  		checkTable(t, tableName, true)
  1833  		testSelectTableMetrics(t)
  1834  	})
  1835  	t.Run("CREATE TABLE with zero date and --allow-zero-in-date is successful", func(t *testing.T) {
  1836  		uuid := testOnlineDDL(t, createStatementZeroDate, "online -declarative --allow-zero-in-date", "vtgate", "", "")
  1837  		uuids = append(uuids, uuid)
  1838  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1839  		checkMigratedTable(t, "zerodate_test", "create_with_zero")
  1840  		checkTable(t, tableName, true)
  1841  		testSelectTableMetrics(t)
  1842  	})
  1843  	t.Run("CREATE TABLE with zero date and --allow-zero-in-date is successful", func(t *testing.T) {
  1844  		uuid := testOnlineDDL(t, createStatementZeroDate2, "online -declarative --allow-zero-in-date", "vtgate", "", "")
  1845  		uuids = append(uuids, uuid)
  1846  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1847  		checkMigratedTable(t, "zerodate_test", "create_with_zero2")
  1848  		checkTable(t, tableName, true)
  1849  		testSelectTableMetrics(t)
  1850  	})
  1851  
  1852  	// ### The following tests are not strictly 'declarative' but are best served under this endtoend test
  1853  
  1854  	// Test duplicate context/SQL
  1855  	t.Run("Trivial statement with request context is successful", func(t *testing.T) {
  1856  		uuid := testOnlineDDL(t, trivialAlterStatement, "online", "vtctl", "", "")
  1857  		uuids = append(uuids, uuid)
  1858  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1859  		// the table existed, so we expect no changes in this non-declarative DDL
  1860  		checkTable(t, tableName, true)
  1861  
  1862  		rs := onlineddl.ReadMigrations(t, &vtParams, uuid)
  1863  		require.NotNil(t, rs)
  1864  		for _, row := range rs.Named().Rows {
  1865  			message := row["message"].ToString()
  1866  			require.NotContains(t, message, "duplicate DDL")
  1867  		}
  1868  	})
  1869  	t.Run("Duplicate trivial statement with request context is successful", func(t *testing.T) {
  1870  		uuid := testOnlineDDL(t, trivialAlterStatement, "online", "vtctl", "", "")
  1871  		uuids = append(uuids, uuid)
  1872  		onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  1873  		// the table existed, so we expect no changes in this non-declarative DDL
  1874  		checkTable(t, tableName, true)
  1875  
  1876  		rs := onlineddl.ReadMigrations(t, &vtParams, uuid)
  1877  		require.NotNil(t, rs)
  1878  		for _, row := range rs.Named().Rows {
  1879  			message := row["message"].ToString()
  1880  			// Message suggests that the migration was identified as duplicate
  1881  			require.Contains(t, message, "duplicate DDL")
  1882  		}
  1883  	})
  1884  	// Piggyride this test suite, let's also test --allow-zero-in-date for 'direct' strategy
  1885  	t.Run("drop non_online", func(t *testing.T) {
  1886  		_ = testOnlineDDL(t, dropZeroDateStatement, "direct", "vtctl", "", "")
  1887  		checkTable(t, "zerodate_test", false)
  1888  	})
  1889  	t.Run("CREATE TABLE with zero date fails in 'direct' strategy", func(t *testing.T) {
  1890  		_ = testOnlineDDL(t, createStatementZeroDate, "direct", "vtctl", "", "Invalid default value for")
  1891  		checkTable(t, "zerodate_test", false)
  1892  	})
  1893  	t.Run("CREATE TABLE with zero date and --allow-zero-in-date succeeds in 'direct' strategy", func(t *testing.T) {
  1894  		_ = testOnlineDDL(t, createStatementZeroDate, "direct --allow-zero-in-date", "vtctl", "", "")
  1895  		checkTable(t, "zerodate_test", true)
  1896  	})
  1897  }
  1898  
  1899  func testForeignKeys(t *testing.T) {
  1900  	defer cluster.PanicHandler(t)
  1901  
  1902  	var (
  1903  		createStatements = []string{
  1904  			`
  1905  			CREATE TABLE parent_table (
  1906  					id INT NOT NULL,
  1907  					parent_hint_col INT NOT NULL DEFAULT 0,
  1908  					PRIMARY KEY (id)
  1909  			)
  1910  		`,
  1911  			`
  1912  			CREATE TABLE child_table (
  1913  				id INT NOT NULL auto_increment,
  1914  				parent_id INT,
  1915  				child_hint_col INT NOT NULL DEFAULT 0,
  1916  				PRIMARY KEY (id),
  1917  				KEY parent_id_idx (parent_id),
  1918  				CONSTRAINT child_parent_fk FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE
  1919  			)
  1920  		`,
  1921  			`
  1922  			CREATE TABLE child_nofk_table (
  1923  				id INT NOT NULL auto_increment,
  1924  				parent_id INT,
  1925  				child_hint_col INT NOT NULL DEFAULT 0,
  1926  				PRIMARY KEY (id),
  1927  				KEY parent_id_idx (parent_id)
  1928  			)
  1929  		`,
  1930  		}
  1931  		insertStatements = []string{
  1932  			"insert into parent_table (id) values(43)",
  1933  			"insert into child_table (id, parent_id) values(1,43)",
  1934  			"insert into child_table (id, parent_id) values(2,43)",
  1935  			"insert into child_table (id, parent_id) values(3,43)",
  1936  			"insert into child_table (id, parent_id) values(4,43)",
  1937  		}
  1938  		ddlStrategy        = "online --allow-zero-in-date"
  1939  		ddlStrategyAllowFK = ddlStrategy + " --unsafe-allow-foreign-keys"
  1940  	)
  1941  
  1942  	type testCase struct {
  1943  		name             string
  1944  		sql              string
  1945  		allowForeignKeys bool
  1946  		expectHint       string
  1947  	}
  1948  	var testCases = []testCase{
  1949  		{
  1950  			name:             "modify parent, not allowed",
  1951  			sql:              "alter table parent_table engine=innodb",
  1952  			allowForeignKeys: false,
  1953  		},
  1954  		{
  1955  			name:             "modify child, not allowed",
  1956  			sql:              "alter table child_table engine=innodb",
  1957  			allowForeignKeys: false,
  1958  		},
  1959  		{
  1960  			name:             "add foreign key to child, not allowed",
  1961  			sql:              "alter table child_table add CONSTRAINT another_fk FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE",
  1962  			allowForeignKeys: false,
  1963  		},
  1964  		{
  1965  			name:             "add foreign key to table which wasn't a child before, not allowed",
  1966  			sql:              "alter table child_nofk_table add CONSTRAINT new_fk FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE",
  1967  			allowForeignKeys: false,
  1968  		},
  1969  		{
  1970  			// on vanilla MySQL, this migration ends with the child_table referencing the old, original table, and not to the new table now called parent_table.
  1971  			// This is a fundamental foreign key limitation, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/
  1972  			// However, this tests is still valid in the sense that it lets us modify the parent table in the first place.
  1973  			name:             "modify parent, trivial",
  1974  			sql:              "alter table parent_table engine=innodb",
  1975  			allowForeignKeys: true,
  1976  			expectHint:       "parent_hint_col",
  1977  		},
  1978  		{
  1979  			// on vanilla MySQL, this migration ends with two tables, the original and the new child_table, both referencing parent_table. This has
  1980  			// the unwanted property of then limiting actions on the parent_table based on what rows exist or do not exist on the now stale old
  1981  			// child table.
  1982  			// This is a fundamental foreign key limitation, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/
  1983  			// However, this tests is still valid in the sense that it lets us modify the child table in the first place.
  1984  			// A valid use case: using FOREIGN_KEY_CHECKS=0  at all times.
  1985  			name:             "modify child, trivial",
  1986  			sql:              "alter table child_table engine=innodb",
  1987  			allowForeignKeys: true,
  1988  			expectHint:       "REFERENCES `parent_table`",
  1989  		},
  1990  		{
  1991  			// on vanilla MySQL, this migration ends with two tables, the original and the new child_table, both referencing parent_table. This has
  1992  			// the unwanted property of then limiting actions on the parent_table based on what rows exist or do not exist on the now stale old
  1993  			// child table.
  1994  			// This is a fundamental foreign key limitation, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/
  1995  			// However, this tests is still valid in the sense that it lets us modify the child table in the first place.
  1996  			// A valid use case: using FOREIGN_KEY_CHECKS=0  at all times.
  1997  			name:             "add foreign key to child",
  1998  			sql:              "alter table child_table add CONSTRAINT another_fk FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE",
  1999  			allowForeignKeys: true,
  2000  			expectHint:       "another_fk",
  2001  		},
  2002  		{
  2003  			name:             "add foreign key to table which wasn't a child before",
  2004  			sql:              "alter table child_nofk_table add CONSTRAINT new_fk FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE",
  2005  			allowForeignKeys: true,
  2006  			expectHint:       "new_fk",
  2007  		},
  2008  	}
  2009  
  2010  	createParams := func(ddlStatement string, ddlStrategy string, executeStrategy string, expectHint string, expectError string, skipWait bool) *testOnlineDDLStatementParams {
  2011  		return &testOnlineDDLStatementParams{
  2012  			ddlStatement:    ddlStatement,
  2013  			ddlStrategy:     ddlStrategy,
  2014  			executeStrategy: executeStrategy,
  2015  			expectHint:      expectHint,
  2016  			expectError:     expectError,
  2017  			skipWait:        skipWait,
  2018  		}
  2019  	}
  2020  
  2021  	testStatement := func(t *testing.T, sql string, ddlStrategy string, expectHint string, expectError bool) (uuid string) {
  2022  		errorHint := ""
  2023  		if expectError {
  2024  			errorHint = anyErrorIndicator
  2025  		}
  2026  		return testOnlineDDLStatement(t, createParams(sql, ddlStrategy, "vtctl", expectHint, errorHint, false))
  2027  	}
  2028  	for _, testcase := range testCases {
  2029  		t.Run(testcase.name, func(t *testing.T) {
  2030  			t.Run("create tables", func(t *testing.T) {
  2031  				for _, statement := range createStatements {
  2032  					t.Run(statement, func(t *testing.T) {
  2033  						uuid := testStatement(t, statement, ddlStrategyAllowFK, "", false)
  2034  						onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  2035  					})
  2036  				}
  2037  			})
  2038  			t.Run("populate tables", func(t *testing.T) {
  2039  				for _, statement := range insertStatements {
  2040  					t.Run(statement, func(t *testing.T) {
  2041  						onlineddl.VtgateExecQuery(t, &vtParams, statement, "")
  2042  					})
  2043  				}
  2044  			})
  2045  			var uuid string
  2046  			t.Run("run migration", func(t *testing.T) {
  2047  				if testcase.allowForeignKeys {
  2048  					uuid = testStatement(t, testcase.sql, ddlStrategyAllowFK, testcase.expectHint, false)
  2049  					onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete)
  2050  				} else {
  2051  					uuid = testStatement(t, testcase.sql, ddlStrategy, "", true)
  2052  					if uuid != "" {
  2053  						onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed)
  2054  					}
  2055  				}
  2056  			})
  2057  			t.Run("cleanup", func(t *testing.T) {
  2058  				var artifacts []string
  2059  				if uuid != "" {
  2060  					rs := onlineddl.ReadMigrations(t, &vtParams, uuid)
  2061  					require.NotNil(t, rs)
  2062  					row := rs.Named().Row()
  2063  					require.NotNil(t, row)
  2064  
  2065  					artifacts = textutil.SplitDelimitedList(row.AsString("artifacts", ""))
  2066  				}
  2067  
  2068  				artifacts = append(artifacts, "child_table", "child_nofk_table", "parent_table")
  2069  				// brute force drop all tables. In MySQL 8.0 you can do a single `DROP TABLE ... <list of all tables>`
  2070  				// which auto-resovled order. But in 5.7 you can't.
  2071  				droppedTables := map[string]bool{}
  2072  				for range artifacts {
  2073  					for _, artifact := range artifacts {
  2074  						if droppedTables[artifact] {
  2075  							continue
  2076  						}
  2077  						statement := fmt.Sprintf("DROP TABLE IF EXISTS %s", artifact)
  2078  						_, err := clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, statement, cluster.VtctlClientParams{DDLStrategy: "direct", SkipPreflight: true})
  2079  						if err == nil {
  2080  							droppedTables[artifact] = true
  2081  						}
  2082  					}
  2083  				}
  2084  				statement := fmt.Sprintf("DROP TABLE IF EXISTS %s", strings.Join(artifacts, ","))
  2085  				t.Run(statement, func(t *testing.T) {
  2086  					testStatement(t, statement, "direct", "", false)
  2087  				})
  2088  			})
  2089  		})
  2090  	}
  2091  }
  2092  
  2093  // testOnlineDDLStatement runs an online DDL, ALTER statement
  2094  func testOnlineDDLStatement(t *testing.T, params *testOnlineDDLStatementParams) (uuid string) {
  2095  	strategySetting, err := schema.ParseDDLStrategy(params.ddlStrategy)
  2096  	require.NoError(t, err)
  2097  
  2098  	tableName := parseTableName(t, params.ddlStatement)
  2099  
  2100  	if params.executeStrategy == "vtgate" {
  2101  		require.Empty(t, params.migrationContext, "explicit migration context not supported in vtgate. Test via vtctl")
  2102  		result := onlineddl.VtgateExecDDL(t, &vtParams, params.ddlStrategy, params.ddlStatement, params.expectError)
  2103  		if result != nil {
  2104  			row := result.Named().Row()
  2105  			if row != nil {
  2106  				uuid = row.AsString("uuid", "")
  2107  			}
  2108  		}
  2109  	} else {
  2110  		vtctlParams := &cluster.VtctlClientParams{DDLStrategy: params.ddlStrategy, MigrationContext: params.migrationContext, SkipPreflight: true}
  2111  		if overrideVtctlParams != nil {
  2112  			vtctlParams = overrideVtctlParams
  2113  		}
  2114  		output, err := clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, params.ddlStatement, *vtctlParams)
  2115  		switch params.expectError {
  2116  		case anyErrorIndicator:
  2117  			if err != nil {
  2118  				// fine. We got any error.
  2119  				t.Logf("expected any error, got this error: %v", err)
  2120  				return
  2121  			}
  2122  			uuid = output
  2123  		case "":
  2124  			assert.NoError(t, err)
  2125  			uuid = output
  2126  		default:
  2127  			assert.Error(t, err)
  2128  			assert.Contains(t, output, params.expectError)
  2129  		}
  2130  	}
  2131  	uuid = strings.TrimSpace(uuid)
  2132  	fmt.Println("# Generated UUID (for debug purposes):")
  2133  	fmt.Printf("<%s>\n", uuid)
  2134  
  2135  	if !strategySetting.Strategy.IsDirect() && !params.skipWait && uuid != "" {
  2136  		status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed)
  2137  		fmt.Printf("# Migration status (for debug purposes): <%s>\n", status)
  2138  	}
  2139  
  2140  	if params.expectError == "" && params.expectHint != "" {
  2141  		checkMigratedTable(t, tableName, params.expectHint)
  2142  	}
  2143  	return uuid
  2144  }
  2145  
  2146  // testRevertMigration reverts a given migration
  2147  func testRevertMigration(t *testing.T, params *testRevertMigrationParams) (uuid string) {
  2148  	revertQuery := fmt.Sprintf("revert vitess_migration '%s'", params.revertUUID)
  2149  	if params.executeStrategy == "vtgate" {
  2150  		require.Empty(t, params.migrationContext, "explicit migration context not supported in vtgate. Test via vtctl")
  2151  		result := onlineddl.VtgateExecDDL(t, &vtParams, params.ddlStrategy, revertQuery, params.expectError)
  2152  		if result != nil {
  2153  			row := result.Named().Row()
  2154  			if row != nil {
  2155  				uuid = row.AsString("uuid", "")
  2156  			}
  2157  		}
  2158  	} else {
  2159  		output, err := clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, revertQuery, cluster.VtctlClientParams{DDLStrategy: params.ddlStrategy, MigrationContext: params.migrationContext, SkipPreflight: true})
  2160  		if params.expectError == "" {
  2161  			assert.NoError(t, err)
  2162  			uuid = output
  2163  		} else {
  2164  			assert.Error(t, err)
  2165  			assert.Contains(t, output, params.expectError)
  2166  		}
  2167  	}
  2168  
  2169  	if params.expectError == "" {
  2170  		uuid = strings.TrimSpace(uuid)
  2171  		fmt.Println("# Generated UUID (for debug purposes):")
  2172  		fmt.Printf("<%s>\n", uuid)
  2173  	}
  2174  	if !params.skipWait {
  2175  		time.Sleep(time.Second * 20)
  2176  	}
  2177  	return uuid
  2178  }
  2179  
  2180  // checkTable checks the number of tables in the first two shards.
  2181  func checkTable(t *testing.T, showTableName string, expectExists bool) bool {
  2182  	expectCount := 0
  2183  	if expectExists {
  2184  		expectCount = 1
  2185  	}
  2186  	for i := range clusterInstance.Keyspaces[0].Shards {
  2187  		if !checkTablesCount(t, clusterInstance.Keyspaces[0].Shards[i].Vttablets[0], showTableName, expectCount) {
  2188  			return false
  2189  		}
  2190  	}
  2191  	return true
  2192  }
  2193  
  2194  // checkTablesCount checks the number of tables in the given tablet
  2195  func checkTablesCount(t *testing.T, tablet *cluster.Vttablet, showTableName string, expectCount int) bool {
  2196  	query := fmt.Sprintf(`show tables like '%%%s%%';`, showTableName)
  2197  	queryResult, err := tablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
  2198  	require.Nil(t, err)
  2199  	return assert.Equal(t, expectCount, len(queryResult.Rows))
  2200  }
  2201  
  2202  // checkMigratedTables checks the CREATE STATEMENT of a table after migration
  2203  func checkMigratedTable(t *testing.T, tableName, expectHint string) {
  2204  	for i := range clusterInstance.Keyspaces[0].Shards {
  2205  		createStatement := getCreateTableStatement(t, clusterInstance.Keyspaces[0].Shards[i].Vttablets[0], tableName)
  2206  		assert.Contains(t, createStatement, expectHint)
  2207  	}
  2208  }
  2209  
  2210  // getCreateTableStatement returns the CREATE TABLE statement for a given table
  2211  func getCreateTableStatement(t *testing.T, tablet *cluster.Vttablet, tableName string) (statement string) {
  2212  	queryResult, err := tablet.VttabletProcess.QueryTablet(fmt.Sprintf("show create table %s;", tableName), keyspaceName, true)
  2213  	require.Nil(t, err)
  2214  
  2215  	assert.Equal(t, len(queryResult.Rows), 1)
  2216  	assert.GreaterOrEqual(t, len(queryResult.Rows[0]), 2) // table name, create statement, and if it's a view then additional columns
  2217  	statement = queryResult.Rows[0][1].ToString()
  2218  	return statement
  2219  }