github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/workload/schemachange/schemachange.go (about)

     1  // Copyright 2020 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package schemachange
    12  
    13  import (
    14  	"context"
    15  	gosql "database/sql"
    16  	"fmt"
    17  	"math/rand"
    18  	"runtime"
    19  	"strings"
    20  	"sync/atomic"
    21  
    22  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    24  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    25  	"github.com/cockroachdb/cockroach/pkg/workload"
    26  	"github.com/cockroachdb/cockroach/pkg/workload/histogram"
    27  	"github.com/cockroachdb/errors"
    28  	"github.com/jackc/pgx"
    29  	"github.com/spf13/pflag"
    30  )
    31  
    32  // This workload executes batches of schema changes asynchronously. Each
    33  // batch is executed in a separate transaction and transactions run in
    34  // parallel. Batches are drawn from a pre-defined distribution.
    35  // Currently all schema change ops are equally likely to be chosen. This
    36  // includes table creation but note that the tables contain no data.
    37  //
    38  // Example usage:
    39  // `bin/workload run schemachange --init --concurrency=2 --verbose=false --max-ops=1000`
    40  // will execute up to 1000 schema change operations per txn in two concurrent txns.
    41  //
    42  // TODO(peter): This is still work in progress, we need to
    43  // - support more than 1 database
    44  // - reference sequences in column defaults
    45  // - create foreign keys
    46  // - support `ADD CONSTRAINT`
    47  // - support `SET COLUMN DEFAULT`
    48  //
    49  // TODO(spaskob): introspect errors returned from the workload and determine
    50  // whether they're expected or unexpected. Flag `tolerate-errors` should be
    51  // added to tolerate unexpected errors and then unexpected errors should fail
    52  // the workload.
    53  //
    54  //For example, an attempt to do something we don't support should be swallowed (though if we can detect that maybe we should just not do it, e.g). It will be hard to use this test for anything more than liveness detection until we go through the tedious process of classifying errors.:
    55  
    56  const (
    57  	defaultMaxOpsPerWorker = 5
    58  	defaultExistingPct     = 10
    59  )
    60  
    61  type schemaChange struct {
    62  	flags           workload.Flags
    63  	dbOverride      string
    64  	concurrency     int
    65  	maxOpsPerWorker int
    66  	existingPct     int
    67  	verbose         int
    68  	dryRun          bool
    69  }
    70  
    71  var schemaChangeMeta = workload.Meta{
    72  	Name:        `schemachange`,
    73  	Description: `schemachange randomly generates concurrent schema changes`,
    74  	Version:     `1.0.0`,
    75  	New: func() workload.Generator {
    76  		s := &schemaChange{}
    77  		s.flags.FlagSet = pflag.NewFlagSet(`schemachange`, pflag.ContinueOnError)
    78  		s.flags.StringVar(&s.dbOverride, `db`, ``,
    79  			`Override for the SQL database to use. If empty, defaults to the generator name`)
    80  		s.flags.IntVar(&s.concurrency, `concurrency`, 2*runtime.NumCPU(), /* TODO(spaskob): sensible default? */
    81  			`Number of concurrent workers`)
    82  		s.flags.IntVar(&s.maxOpsPerWorker, `max-ops-per-worker`, defaultMaxOpsPerWorker,
    83  			`Number of operations to execute in a single transaction`)
    84  		s.flags.IntVar(&s.existingPct, `existing-pct`, defaultExistingPct,
    85  			`Percentage of times to use existing name`)
    86  		s.flags.IntVarP(&s.verbose, `verbose`, `v`, 0, ``)
    87  		s.flags.BoolVarP(&s.dryRun, `dry-run`, `n`, false, ``)
    88  		return s
    89  	},
    90  }
    91  
    92  func init() {
    93  	workload.Register(schemaChangeMeta)
    94  }
    95  
    96  //go:generate stringer -type=opType
    97  type opType int
    98  
    99  const (
   100  	addColumn     opType = iota // ALTER TABLE <table> ADD [COLUMN] <column> <type>
   101  	addConstraint               // ALTER TABLE <table> ADD CONSTRAINT <constraint> <def>
   102  
   103  	createIndex    // CREATE INDEX <index> ON <table> <def>
   104  	createSequence // CREATE SEQUENCE <sequence> <def>
   105  	createTable    // CREATE TABLE <table> <def>
   106  	createTableAs  // CREATE TABLE <table> AS <def>
   107  	createView     // CREATE VIEW <view> AS <def>
   108  
   109  	dropColumn        // ALTER TABLE <table> DROP COLUMN <column>
   110  	dropColumnDefault // ALTER TABLE <table> ALTER [COLUMN] <column> DROP DEFAULT
   111  	dropColumnNotNull // ALTER TABLE <table> ALTER [COLUMN] <column> DROP NOT NULL
   112  	dropColumnStored  // ALTER TABLE <table> ALTER [COLUMN] <column> DROP STORED
   113  	dropConstraint    // ALTER TABLE <table> DROP CONSTRAINT <constraint>
   114  	dropIndex         // DROP INDEX <index>@<table>
   115  	dropSequence      // DROP SEQUENCE <sequence>
   116  	dropTable         // DROP TABLE <table>
   117  	dropView          // DROP VIEW <view>
   118  
   119  	renameColumn   // ALTER TABLE <table> RENAME [COLUMN] <column> TO <column>
   120  	renameIndex    // ALTER TABLE <table> RENAME CONSTRAINT <constraint> TO <constraint>
   121  	renameSequence // ALTER SEQUENCE <sequence> RENAME TO <sequence>
   122  	renameTable    // ALTER TABLE <table> RENAME TO <table>
   123  	renameView     // ALTER VIEW <view> RENAME TO <view>
   124  
   125  	setColumnDefault // ALTER TABLE <table> ALTER [COLUMN] <column> SET DEFAULT <expr>
   126  	setColumnNotNull // ALTER TABLE <table> ALTER [COLUMN] <column> SET NOT NULL
   127  	setColumnType    // ALTER TABLE <table> ALTER [COLUMN] <column> [SET DATA] TYPE <type>
   128  )
   129  
   130  var opWeights = []int{
   131  	addColumn:         1,
   132  	addConstraint:     0, // TODO(spaskob): unimplemented
   133  	createIndex:       1,
   134  	createSequence:    1,
   135  	createTable:       1,
   136  	createTableAs:     1,
   137  	createView:        1,
   138  	dropColumn:        1,
   139  	dropColumnDefault: 1,
   140  	dropColumnNotNull: 1,
   141  	dropColumnStored:  1,
   142  	dropConstraint:    1,
   143  	dropIndex:         1,
   144  	dropSequence:      1,
   145  	dropTable:         1,
   146  	dropView:          1,
   147  	renameColumn:      1,
   148  	renameIndex:       1,
   149  	renameSequence:    1,
   150  	renameTable:       1,
   151  	renameView:        1,
   152  	setColumnDefault:  0, // TODO(spaskob): unimplemented
   153  	setColumnNotNull:  1,
   154  	setColumnType:     1,
   155  }
   156  
   157  // Meta implements the workload.Generator interface.
   158  func (s *schemaChange) Meta() workload.Meta {
   159  	return schemaChangeMeta
   160  }
   161  
   162  // Flags implements the workload.Flagser interface.
   163  func (s *schemaChange) Flags() workload.Flags {
   164  	return s.flags
   165  }
   166  
   167  // Tables implements the workload.Generator interface.
   168  func (s *schemaChange) Tables() []workload.Table {
   169  	return nil
   170  }
   171  
   172  // Tables implements the workload.Opser interface.
   173  func (s *schemaChange) Ops(urls []string, reg *histogram.Registry) (workload.QueryLoad, error) {
   174  	sqlDatabase, err := workload.SanitizeUrls(s, s.dbOverride, urls)
   175  	if err != nil {
   176  		return workload.QueryLoad{}, err
   177  	}
   178  	cfg := workload.MultiConnPoolCfg{
   179  		MaxTotalConnections: s.concurrency * 2, //TODO(spaskob): pick a sensible default.
   180  	}
   181  	pool, err := workload.NewMultiConnPool(cfg, urls...)
   182  	if err != nil {
   183  		return workload.QueryLoad{}, err
   184  	}
   185  
   186  	seqNum, err := s.initSeqNum(pool)
   187  	if err != nil {
   188  		return workload.QueryLoad{}, err
   189  	}
   190  
   191  	ops := newDeck(rand.New(rand.NewSource(timeutil.Now().UnixNano())), opWeights...)
   192  	ql := workload.QueryLoad{SQLDatabase: sqlDatabase}
   193  	for i := 0; i < s.concurrency; i++ {
   194  		w := &schemaChangeWorker{
   195  			verbose:         s.verbose,
   196  			dryRun:          s.dryRun,
   197  			maxOpsPerWorker: s.maxOpsPerWorker,
   198  			existingPct:     s.existingPct,
   199  			rng:             rand.New(rand.NewSource(timeutil.Now().UnixNano())),
   200  			ops:             ops,
   201  			pool:            pool,
   202  			hists:           reg.GetHandle(),
   203  			seqNum:          seqNum,
   204  		}
   205  		ql.WorkerFns = append(ql.WorkerFns, w.run)
   206  	}
   207  	return ql, nil
   208  }
   209  
   210  // initSeqName returns the smallest available sequence number to be
   211  // used to generate new unique names. Note that this assumes that no
   212  // other workload is being run at the same time.
   213  // TODO(spaskob): Do we need to protect from workloads running concurrently.
   214  // It's not obvious how the workloads will behave when accessing the same
   215  // cluster.
   216  func (s *schemaChange) initSeqNum(pool *workload.MultiConnPool) (*int64, error) {
   217  	seqNum := new(int64)
   218  
   219  	const q = `
   220  SELECT max(regexp_extract(name, '[0-9]+$')::int)
   221    FROM ((SELECT table_name FROM [SHOW TABLES]) UNION (SELECT sequence_name FROM [SHOW SEQUENCES])) AS obj(name)
   222   WHERE name ~ '^(table|view|seq)[0-9]+$';
   223  `
   224  	var max gosql.NullInt64
   225  	if err := pool.Get().QueryRow(q).Scan(&max); err != nil {
   226  		return nil, err
   227  	}
   228  	if max.Valid {
   229  		*seqNum = max.Int64 + 1
   230  	}
   231  
   232  	return seqNum, nil
   233  }
   234  
   235  type schemaChangeWorker struct {
   236  	verbose         int
   237  	dryRun          bool
   238  	maxOpsPerWorker int
   239  	existingPct     int
   240  	rng             *rand.Rand
   241  	ops             *deck
   242  	pool            *workload.MultiConnPool
   243  	hists           *histogram.Histograms
   244  	seqNum          *int64
   245  }
   246  
   247  // handleOpError returns an error if the op error is considered serious and
   248  // we should terminate the workload.
   249  func handleOpError(err error) error {
   250  	if err == nil {
   251  		return nil
   252  	}
   253  	if pgErr := (pgx.PgError{}); errors.As(err, &pgErr) {
   254  		sqlstate := pgErr.SQLState()
   255  		class := sqlstate[0:2]
   256  		switch class {
   257  		case "09":
   258  			return errors.Wrap(err, "Class 09 - Triggered Action Exception")
   259  		case "XX":
   260  			return errors.Wrap(err, "Class XX - Internal Error")
   261  		}
   262  	} else {
   263  		return errors.Wrapf(err, "unexpected error %v", err)
   264  	}
   265  	return nil
   266  }
   267  
   268  var (
   269  	errRunInTxnFatalSentinel = errors.New("fatal error when running txn")
   270  	errRunInTxnRbkSentinel   = errors.New("txn needs to rollback")
   271  )
   272  
   273  func (w *schemaChangeWorker) runInTxn(tx *pgx.Tx, opsNum int) (string, error) {
   274  	var log strings.Builder
   275  	for i := 0; i < opsNum; i++ {
   276  		op, noops, err := w.randOp(tx)
   277  		if err != nil {
   278  			return noops, errors.Mark(
   279  				errors.Wrap(err, "could not generate a random operation"),
   280  				errRunInTxnFatalSentinel,
   281  			)
   282  		}
   283  		if w.verbose >= 2 {
   284  			// Print the failed attempts to produce a random operation.
   285  			log.WriteString(noops)
   286  		}
   287  		log.WriteString(fmt.Sprintf("  %s;\n", op))
   288  		if !w.dryRun {
   289  			histBin := "opOk"
   290  			start := timeutil.Now()
   291  			if _, err = tx.Exec(op); err != nil {
   292  				histBin = "txnRbk"
   293  				log.WriteString(fmt.Sprintf("***FAIL: %v\n", err))
   294  				log.WriteString("ROLLBACK;\n")
   295  				return log.String(), errors.Mark(err, errRunInTxnRbkSentinel)
   296  			}
   297  			elapsed := timeutil.Since(start)
   298  			w.hists.Get(histBin).Record(elapsed)
   299  		}
   300  	}
   301  	return log.String(), nil
   302  }
   303  
   304  func (w *schemaChangeWorker) run(_ context.Context) error {
   305  	tx, err := w.pool.Get().Begin()
   306  	if err != nil {
   307  		return errors.Wrap(err, "cannot get a connection and begin a txn")
   308  	}
   309  	opsNum := 1 + w.rng.Intn(w.maxOpsPerWorker)
   310  
   311  	// Run between 1 and maxOpsPerWorker schema change operations.
   312  	start := timeutil.Now()
   313  	logs, err := w.runInTxn(tx, opsNum)
   314  	logs = "BEGIN\n" + logs
   315  	defer func() {
   316  		if w.verbose >= 1 {
   317  			fmt.Print(logs)
   318  		}
   319  	}()
   320  
   321  	if err != nil {
   322  		// Rollback in all cases to release the txn object and its conn pool.
   323  		if rbkErr := tx.Rollback(); rbkErr != nil {
   324  			return errors.Wrapf(err, "Could not rollback %v", rbkErr)
   325  		}
   326  		switch {
   327  		case errors.Is(err, errRunInTxnFatalSentinel):
   328  			return err
   329  		case errors.Is(err, errRunInTxnRbkSentinel):
   330  			if seriousErr := handleOpError(err); seriousErr != nil {
   331  				return seriousErr
   332  			}
   333  			return nil
   334  		default:
   335  			return errors.Wrapf(err, "Unexpected error")
   336  		}
   337  	}
   338  
   339  	// If there were no errors commit the txn.
   340  	histBin := "txnOk"
   341  	cmtErrMsg := ""
   342  	if err = tx.Commit(); err != nil {
   343  		histBin = "txnCmtErr"
   344  		cmtErrMsg = fmt.Sprintf("***FAIL: %v", err)
   345  	}
   346  	w.hists.Get(histBin).Record(timeutil.Since(start))
   347  	logs = logs + fmt.Sprintf("COMMIT;  %s\n", cmtErrMsg)
   348  	return nil
   349  }
   350  
   351  // randOp attempts to produce a random schema change operation. It returns a
   352  // triple `(randOp, log, error)`. On success `randOp` is the random schema
   353  // change constructed. Constructing a random schema change may require a few
   354  // stochastic attempts and if verbosity is >= 2 the unsuccessful attempts are
   355  // recorded in `log` to help with debugging of the workload.
   356  func (w *schemaChangeWorker) randOp(tx *pgx.Tx) (string, string, error) {
   357  	var log strings.Builder
   358  	for {
   359  		var stmt string
   360  		var err error
   361  		op := opType(w.ops.Int())
   362  		switch op {
   363  		case addColumn:
   364  			stmt, err = w.addColumn(tx)
   365  
   366  		case addConstraint:
   367  			stmt, err = w.addConstraint(tx)
   368  
   369  		case createIndex:
   370  			stmt, err = w.createIndex(tx)
   371  
   372  		case createSequence:
   373  			stmt, err = w.createSequence(tx)
   374  
   375  		case createTable:
   376  			stmt, err = w.createTable(tx)
   377  
   378  		case createTableAs:
   379  			stmt, err = w.createTableAs(tx)
   380  
   381  		case createView:
   382  			stmt, err = w.createView(tx)
   383  
   384  		case dropColumn:
   385  			stmt, err = w.dropColumn(tx)
   386  
   387  		case dropColumnDefault:
   388  			stmt, err = w.dropColumnDefault(tx)
   389  
   390  		case dropColumnNotNull:
   391  			stmt, err = w.dropColumnNotNull(tx)
   392  
   393  		case dropColumnStored:
   394  			stmt, err = w.dropColumnStored(tx)
   395  
   396  		case dropConstraint:
   397  			stmt, err = w.dropConstraint(tx)
   398  
   399  		case dropIndex:
   400  			stmt, err = w.dropIndex(tx)
   401  
   402  		case dropSequence:
   403  			stmt, err = w.dropSequence(tx)
   404  
   405  		case dropTable:
   406  			stmt, err = w.dropTable(tx)
   407  
   408  		case dropView:
   409  			stmt, err = w.dropView(tx)
   410  
   411  		case renameColumn:
   412  			stmt, err = w.renameColumn(tx)
   413  
   414  		case renameIndex:
   415  			stmt, err = w.renameIndex(tx)
   416  
   417  		case renameSequence:
   418  			stmt, err = w.renameSequence(tx)
   419  
   420  		case renameTable:
   421  			stmt, err = w.renameTable(tx)
   422  
   423  		case renameView:
   424  			stmt, err = w.renameView(tx)
   425  
   426  		case setColumnDefault:
   427  			stmt, err = w.setColumnDefault(tx)
   428  
   429  		case setColumnNotNull:
   430  			stmt, err = w.setColumnNotNull(tx)
   431  
   432  		case setColumnType:
   433  			stmt, err = w.setColumnType(tx)
   434  		}
   435  
   436  		// TODO(spaskob): use more fine-grained error reporting.
   437  		if stmt == "" || errors.Is(err, pgx.ErrNoRows) {
   438  			log.WriteString(fmt.Sprintf("NOOP: %s -> %v\n", op, err))
   439  			continue
   440  		}
   441  		return stmt, log.String(), err
   442  	}
   443  }
   444  
   445  func (w *schemaChangeWorker) addColumn(tx *pgx.Tx) (string, error) {
   446  	tableName, err := w.randTable(tx, 100)
   447  	if err != nil {
   448  		return "", err
   449  	}
   450  
   451  	columnName, err := w.randColumn(tx, tableName, w.existingPct)
   452  	if err != nil {
   453  		return "", err
   454  	}
   455  
   456  	def := &tree.ColumnTableDef{
   457  		Name: tree.Name(columnName),
   458  		Type: sqlbase.RandSortingType(w.rng),
   459  	}
   460  	def.Nullable.Nullability = tree.Nullability(rand.Intn(1 + int(tree.SilentNull)))
   461  	return fmt.Sprintf(`ALTER TABLE "%s" ADD COLUMN %s`, tableName, tree.Serialize(def)), nil
   462  }
   463  
   464  func (w *schemaChangeWorker) addConstraint(tx *pgx.Tx) (string, error) {
   465  	// TODO(peter): unimplemented
   466  	// - Export sqlbase.randColumnTableDef.
   467  	return "", nil
   468  }
   469  
   470  func (w *schemaChangeWorker) createIndex(tx *pgx.Tx) (string, error) {
   471  	tableName, err := w.randTable(tx, 100)
   472  	if err != nil {
   473  		return "", err
   474  	}
   475  
   476  	columnNames, err := w.tableColumnsShuffled(tx, tableName)
   477  	if err != nil {
   478  		return "", err
   479  	}
   480  
   481  	indexName, err := w.randIndex(tx, tableName, w.existingPct)
   482  	if err != nil {
   483  		return "", err
   484  	}
   485  
   486  	def := &tree.CreateIndex{
   487  		Name:        tree.Name(indexName),
   488  		Table:       tree.MakeUnqualifiedTableName(tree.Name(tableName)),
   489  		Unique:      w.rng.Intn(4) == 0,  // 25% UNIQUE
   490  		Inverted:    w.rng.Intn(10) == 0, // 10% INVERTED
   491  		IfNotExists: w.rng.Intn(2) == 0,  // 50% IF NOT EXISTS
   492  		Columns:     make(tree.IndexElemList, 1+w.rng.Intn(len(columnNames))),
   493  	}
   494  
   495  	for i := range def.Columns {
   496  		def.Columns[i].Column = tree.Name(columnNames[i])
   497  		def.Columns[i].Direction = tree.Direction(w.rng.Intn(1 + int(tree.Descending)))
   498  	}
   499  	columnNames = columnNames[len(def.Columns):]
   500  
   501  	if n := len(columnNames); n > 0 {
   502  		def.Storing = make(tree.NameList, w.rng.Intn(1+n))
   503  		for i := range def.Storing {
   504  			def.Storing[i] = tree.Name(columnNames[i])
   505  		}
   506  	}
   507  
   508  	return tree.Serialize(def), nil
   509  }
   510  
   511  func (w *schemaChangeWorker) createSequence(tx *pgx.Tx) (string, error) {
   512  	return fmt.Sprintf(`CREATE SEQUENCE "seq%d"`, atomic.AddInt64(w.seqNum, 1)), nil
   513  }
   514  
   515  func (w *schemaChangeWorker) createTable(tx *pgx.Tx) (string, error) {
   516  	tableName, err := w.randTable(tx, 10)
   517  	if err != nil {
   518  		return "", err
   519  	}
   520  
   521  	stmt := sqlbase.RandCreateTable(w.rng, "table", int(atomic.AddInt64(w.seqNum, 1)))
   522  	stmt.Table = tree.MakeUnqualifiedTableName(tree.Name(tableName))
   523  	stmt.IfNotExists = w.rng.Intn(2) == 0
   524  	return tree.Serialize(stmt), nil
   525  }
   526  
   527  func (w *schemaChangeWorker) createTableAs(tx *pgx.Tx) (string, error) {
   528  	tableName, err := w.randTable(tx, 100)
   529  	if err != nil {
   530  		return "", err
   531  	}
   532  
   533  	columnNames, err := w.tableColumnsShuffled(tx, tableName)
   534  	if err != nil {
   535  		return "", err
   536  	}
   537  	columnNames = columnNames[:1+w.rng.Intn(len(columnNames))]
   538  
   539  	names := make(tree.NameList, len(columnNames))
   540  	for i := range names {
   541  		names[i] = tree.Name(columnNames[i])
   542  	}
   543  
   544  	destTableName, err := w.randTable(tx, 10)
   545  	if err != nil {
   546  		return "", err
   547  	}
   548  
   549  	return fmt.Sprintf(`CREATE TABLE "%s" AS SELECT %s FROM "%s"`,
   550  		destTableName, tree.Serialize(&names), tableName), nil
   551  }
   552  
   553  func (w *schemaChangeWorker) createView(tx *pgx.Tx) (string, error) {
   554  	tableName, err := w.randTable(tx, 100)
   555  	if err != nil {
   556  		return "", err
   557  	}
   558  
   559  	columnNames, err := w.tableColumnsShuffled(tx, tableName)
   560  	if err != nil {
   561  		return "", err
   562  	}
   563  	columnNames = columnNames[:1+w.rng.Intn(len(columnNames))]
   564  
   565  	names := make(tree.NameList, len(columnNames))
   566  	for i := range names {
   567  		names[i] = tree.Name(columnNames[i])
   568  	}
   569  
   570  	destViewName, err := w.randView(tx, w.existingPct)
   571  	if err != nil {
   572  		return "", err
   573  	}
   574  
   575  	// TODO(peter): Create views that are dependent on multiple tables.
   576  	return fmt.Sprintf(`CREATE VIEW "%s" AS SELECT %s FROM "%s"`,
   577  		destViewName, tree.Serialize(&names), tableName), nil
   578  }
   579  
   580  func (w *schemaChangeWorker) dropColumn(tx *pgx.Tx) (string, error) {
   581  	tableName, err := w.randTable(tx, 100)
   582  	if err != nil {
   583  		return "", err
   584  	}
   585  	columnName, err := w.randColumn(tx, tableName, 100)
   586  	if err != nil {
   587  		return "", err
   588  	}
   589  	return fmt.Sprintf(`ALTER TABLE "%s" DROP COLUMN "%s"`, tableName, columnName), nil
   590  }
   591  
   592  func (w *schemaChangeWorker) dropColumnDefault(tx *pgx.Tx) (string, error) {
   593  	tableName, err := w.randTable(tx, 100)
   594  	if err != nil {
   595  		return "", err
   596  	}
   597  	columnName, err := w.randColumn(tx, tableName, 100)
   598  	if err != nil {
   599  		return "", err
   600  	}
   601  	return fmt.Sprintf(`ALTER TABLE "%s" ALTER COLUMN "%s" DROP DEFAULT`, tableName, columnName), nil
   602  }
   603  
   604  func (w *schemaChangeWorker) dropColumnNotNull(tx *pgx.Tx) (string, error) {
   605  	tableName, err := w.randTable(tx, 100)
   606  	if err != nil {
   607  		return "", err
   608  	}
   609  	columnName, err := w.randColumn(tx, tableName, 100)
   610  	if err != nil {
   611  		return "", err
   612  	}
   613  	return fmt.Sprintf(`ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL`, tableName, columnName), nil
   614  }
   615  
   616  func (w *schemaChangeWorker) dropColumnStored(tx *pgx.Tx) (string, error) {
   617  	tableName, err := w.randTable(tx, 100)
   618  	if err != nil {
   619  		return "", err
   620  	}
   621  	columnName, err := w.randColumn(tx, tableName, 100)
   622  	if err != nil {
   623  		return "", err
   624  	}
   625  	return fmt.Sprintf(`ALTER TABLE "%s" ALTER COLUMN "%s" DROP STORED`, tableName, columnName), nil
   626  }
   627  
   628  func (w *schemaChangeWorker) dropConstraint(tx *pgx.Tx) (string, error) {
   629  	tableName, err := w.randTable(tx, 100)
   630  	if err != nil {
   631  		return "", err
   632  	}
   633  	constraintName, err := w.randConstraint(tx, tableName)
   634  	if err != nil {
   635  		return "", err
   636  	}
   637  	return fmt.Sprintf(`ALTER TABLE "%s" DROP CONSTRAINT "%s"`, tableName, constraintName), nil
   638  }
   639  
   640  func (w *schemaChangeWorker) dropIndex(tx *pgx.Tx) (string, error) {
   641  	tableName, err := w.randTable(tx, 100)
   642  	if err != nil {
   643  		return "", err
   644  	}
   645  	indexName, err := w.randIndex(tx, tableName, 100)
   646  	if err != nil {
   647  		return "", err
   648  	}
   649  	return fmt.Sprintf(`DROP INDEX "%s"@"%s"`, tableName, indexName), nil
   650  }
   651  
   652  func (w *schemaChangeWorker) dropSequence(tx *pgx.Tx) (string, error) {
   653  	sequenceName, err := w.randSequence(tx, 100)
   654  	if err != nil {
   655  		return "", err
   656  	}
   657  	return fmt.Sprintf(`DROP SEQUENCE "%s"`, sequenceName), nil
   658  }
   659  
   660  func (w *schemaChangeWorker) dropTable(tx *pgx.Tx) (string, error) {
   661  	tableName, err := w.randTable(tx, 100)
   662  	if err != nil {
   663  		return "", err
   664  	}
   665  	return fmt.Sprintf(`DROP TABLE "%s"`, tableName), nil
   666  }
   667  
   668  func (w *schemaChangeWorker) dropView(tx *pgx.Tx) (string, error) {
   669  	viewName, err := w.randView(tx, 100)
   670  	if err != nil {
   671  		return "", err
   672  	}
   673  	return fmt.Sprintf(`DROP VIEW "%s"`, viewName), nil
   674  }
   675  
   676  func (w *schemaChangeWorker) renameColumn(tx *pgx.Tx) (string, error) {
   677  	tableName, err := w.randTable(tx, 100)
   678  	if err != nil {
   679  		return "", err
   680  	}
   681  
   682  	srcColumnName, err := w.randColumn(tx, tableName, 100)
   683  	if err != nil {
   684  		return "", err
   685  	}
   686  
   687  	destColumnName, err := w.randColumn(tx, tableName, 50)
   688  	if err != nil {
   689  		return "", err
   690  	}
   691  
   692  	return fmt.Sprintf(`ALTER TABLE "%s" RENAME COLUMN "%s" TO "%s"`,
   693  		tableName, srcColumnName, destColumnName), nil
   694  }
   695  
   696  func (w *schemaChangeWorker) renameIndex(tx *pgx.Tx) (string, error) {
   697  	tableName, err := w.randTable(tx, 100)
   698  	if err != nil {
   699  		return "", err
   700  	}
   701  
   702  	srcIndexName, err := w.randIndex(tx, tableName, w.existingPct)
   703  	if err != nil {
   704  		return "", err
   705  	}
   706  
   707  	destIndexName, err := w.randIndex(tx, tableName, 50)
   708  	if err != nil {
   709  		return "", err
   710  	}
   711  
   712  	return fmt.Sprintf(`ALTER TABLE "%s" RENAME CONSTRAINT "%s" TO "%s"`,
   713  		tableName, srcIndexName, destIndexName), nil
   714  }
   715  
   716  func (w *schemaChangeWorker) renameSequence(tx *pgx.Tx) (string, error) {
   717  	srcSequenceName, err := w.randSequence(tx, 100)
   718  	if err != nil {
   719  		return "", err
   720  	}
   721  
   722  	destSequenceName, err := w.randSequence(tx, 50)
   723  	if err != nil {
   724  		return "", err
   725  	}
   726  
   727  	return fmt.Sprintf(`ALTER SEQUENCE "%s" RENAME TO "%s"`, srcSequenceName, destSequenceName), nil
   728  }
   729  
   730  func (w *schemaChangeWorker) renameTable(tx *pgx.Tx) (string, error) {
   731  	srcTableName, err := w.randTable(tx, 100)
   732  	if err != nil {
   733  		return "", err
   734  	}
   735  
   736  	destTableName, err := w.randTable(tx, 50)
   737  	if err != nil {
   738  		return "", err
   739  	}
   740  
   741  	return fmt.Sprintf(`ALTER TABLE "%s" RENAME TO "%s"`, srcTableName, destTableName), nil
   742  }
   743  
   744  func (w *schemaChangeWorker) renameView(tx *pgx.Tx) (string, error) {
   745  	srcViewName, err := w.randView(tx, 100)
   746  	if err != nil {
   747  		return "", err
   748  	}
   749  
   750  	destViewName, err := w.randView(tx, 50)
   751  	if err != nil {
   752  		return "", err
   753  	}
   754  
   755  	return fmt.Sprintf(`ALTER VIEW "%s" RENAME TO "%s"`, srcViewName, destViewName), nil
   756  }
   757  
   758  func (w *schemaChangeWorker) setColumnDefault(tx *pgx.Tx) (string, error) {
   759  	// TODO(peter): unimplemented
   760  	return "", nil
   761  }
   762  
   763  func (w *schemaChangeWorker) setColumnNotNull(tx *pgx.Tx) (string, error) {
   764  	tableName, err := w.randTable(tx, 100)
   765  	if err != nil {
   766  		return "", err
   767  	}
   768  	columnName, err := w.randColumn(tx, tableName, 100)
   769  	if err != nil {
   770  		return "", err
   771  	}
   772  	return fmt.Sprintf(`ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL`, tableName, columnName), nil
   773  }
   774  
   775  func (w *schemaChangeWorker) setColumnType(tx *pgx.Tx) (string, error) {
   776  	tableName, err := w.randTable(tx, 100)
   777  	if err != nil {
   778  		return "", err
   779  	}
   780  	columnName, err := w.randColumn(tx, tableName, 100)
   781  	if err != nil {
   782  		return "", err
   783  	}
   784  	return fmt.Sprintf(`ALTER TABLE "%s" ALTER COLUMN "%s" SET DATA TYPE %s`,
   785  		tableName, columnName, sqlbase.RandSortingType(w.rng)), nil
   786  }
   787  
   788  func (w *schemaChangeWorker) randColumn(
   789  	tx *pgx.Tx, tableName string, pctExisting int,
   790  ) (string, error) {
   791  	if w.rng.Intn(100) >= pctExisting {
   792  		// We make a unique name for all columns by prefixing them with the table
   793  		// index to make it easier to reference columns from different tables.
   794  		return fmt.Sprintf("col%s_%d",
   795  			strings.TrimPrefix(tableName, "table"), atomic.AddInt64(w.seqNum, 1)), nil
   796  	}
   797  	q := fmt.Sprintf(`
   798    SELECT column_name
   799      FROM [SHOW COLUMNS FROM "%s"]
   800  ORDER BY random()
   801     LIMIT 1;
   802  `, tableName)
   803  	var name string
   804  	if err := tx.QueryRow(q).Scan(&name); err != nil {
   805  		return "", err
   806  	}
   807  	return name, nil
   808  }
   809  
   810  func (w *schemaChangeWorker) randConstraint(tx *pgx.Tx, tableName string) (string, error) {
   811  	q := fmt.Sprintf(`
   812    SELECT constraint_name
   813      FROM [SHOW CONSTRAINTS FROM "%s"]
   814  ORDER BY random()
   815     LIMIT 1;
   816  `, tableName)
   817  	var name string
   818  	err := tx.QueryRow(q).Scan(&name)
   819  	if err != nil {
   820  		return "", err
   821  	}
   822  	return name, nil
   823  }
   824  
   825  func (w *schemaChangeWorker) randIndex(
   826  	tx *pgx.Tx, tableName string, pctExisting int,
   827  ) (string, error) {
   828  	if w.rng.Intn(100) >= pctExisting {
   829  		// We make a unique name for all indices by prefixing them with the table
   830  		// index to make it easier to reference columns from different tables.
   831  		return fmt.Sprintf("index%s_%d",
   832  			strings.TrimPrefix(tableName, "table"), atomic.AddInt64(w.seqNum, 1)), nil
   833  	}
   834  	q := fmt.Sprintf(`
   835    SELECT index_name
   836      FROM [SHOW INDEXES FROM "%s"]
   837  ORDER BY random()
   838     LIMIT 1;
   839  `, tableName)
   840  	var name string
   841  	if err := tx.QueryRow(q).Scan(&name); err != nil {
   842  		return "", err
   843  	}
   844  	return name, nil
   845  }
   846  
   847  func (w *schemaChangeWorker) randSequence(tx *pgx.Tx, pctExisting int) (string, error) {
   848  	if w.rng.Intn(100) >= pctExisting {
   849  		return fmt.Sprintf(`seq%d`, atomic.AddInt64(w.seqNum, 1)), nil
   850  	}
   851  	const q = `
   852    SELECT sequence_name
   853      FROM [SHOW SEQUENCES]
   854     WHERE sequence_name LIKE 'seq%'
   855  ORDER BY random()
   856     LIMIT 1;
   857  `
   858  	var name string
   859  	if err := tx.QueryRow(q).Scan(&name); err != nil {
   860  		return "", err
   861  	}
   862  	return name, nil
   863  }
   864  
   865  func (w *schemaChangeWorker) randTable(tx *pgx.Tx, pctExisting int) (string, error) {
   866  	if w.rng.Intn(100) >= pctExisting {
   867  		return fmt.Sprintf("table%d", atomic.AddInt64(w.seqNum, 1)), nil
   868  	}
   869  	const q = `
   870    SELECT table_name
   871      FROM [SHOW TABLES]
   872     WHERE table_name LIKE 'table%'
   873  ORDER BY random()
   874     LIMIT 1;
   875  `
   876  	var name string
   877  	if err := tx.QueryRow(q).Scan(&name); err != nil {
   878  		return "", err
   879  	}
   880  	return name, nil
   881  }
   882  
   883  func (w *schemaChangeWorker) randView(tx *pgx.Tx, pctExisting int) (string, error) {
   884  	if w.rng.Intn(100) >= pctExisting {
   885  		return fmt.Sprintf("view%d", atomic.AddInt64(w.seqNum, 1)), nil
   886  	}
   887  	const q = `
   888    SELECT table_name
   889      FROM [SHOW TABLES]
   890     WHERE table_name LIKE 'view%'
   891  ORDER BY random()
   892     LIMIT 1;
   893  `
   894  	var name string
   895  	if err := tx.QueryRow(q).Scan(&name); err != nil {
   896  		return "", err
   897  	}
   898  	return name, nil
   899  }
   900  
   901  func (w *schemaChangeWorker) tableColumnsShuffled(tx *pgx.Tx, tableName string) ([]string, error) {
   902  	q := fmt.Sprintf(`
   903  SELECT column_name
   904  FROM [SHOW COLUMNS FROM "%s"];
   905  `, tableName)
   906  
   907  	rows, err := tx.Query(q)
   908  	if err != nil {
   909  		return nil, err
   910  	}
   911  	defer rows.Close()
   912  
   913  	var columnNames []string
   914  	for rows.Next() {
   915  		var name string
   916  		if err := rows.Scan(&name); err != nil {
   917  			return nil, err
   918  		}
   919  		columnNames = append(columnNames, name)
   920  	}
   921  	if rows.Err() != nil {
   922  		return nil, rows.Err()
   923  	}
   924  
   925  	w.rng.Shuffle(len(columnNames), func(i, j int) {
   926  		columnNames[i], columnNames[j] = columnNames[j], columnNames[i]
   927  	})
   928  
   929  	if len(columnNames) <= 0 {
   930  		return nil, errors.Errorf("table %s has no columns", tableName)
   931  	}
   932  	return columnNames, nil
   933  }