github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/internal/sqlsmith/alter.go (about)

     1  // Copyright 2019 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 sqlsmith
    12  
    13  import (
    14  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    15  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    16  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    17  )
    18  
    19  var (
    20  	alters               = append(altersTableExistence, altersExistingTable...)
    21  	altersTableExistence = []statementWeight{
    22  		{10, makeCreateTable},
    23  		{1, makeDropTable},
    24  	}
    25  	altersExistingTable = []statementWeight{
    26  		{5, makeRenameTable},
    27  
    28  		{10, makeAddColumn},
    29  		{10, makeJSONComputedColumn},
    30  		{10, makeAlterPrimaryKey},
    31  		{1, makeDropColumn},
    32  		{5, makeRenameColumn},
    33  		{5, makeAlterColumnType},
    34  
    35  		{10, makeCreateIndex},
    36  		{1, makeDropIndex},
    37  		{5, makeRenameIndex},
    38  	}
    39  )
    40  
    41  func makeAlter(s *Smither) (tree.Statement, bool) {
    42  	if s.canRecurse() {
    43  		// Schema changes aren't visible immediately, so try to
    44  		// sync the change from the last alter before trying the
    45  		// next one. This is instead of running ReloadSchemas right
    46  		// after the alter succeeds (which doesn't always pick
    47  		// up the change). This has the added benefit of leaving
    48  		// behind old column references for a bit, which should
    49  		// test some additional logic.
    50  		_ = s.ReloadSchemas()
    51  		for i := 0; i < retryCount; i++ {
    52  			stmt, ok := s.alterSampler.Next()(s)
    53  			if ok {
    54  				return stmt, ok
    55  			}
    56  		}
    57  	}
    58  	return nil, false
    59  }
    60  
    61  func makeCreateTable(s *Smither) (tree.Statement, bool) {
    62  	table := sqlbase.RandCreateTable(s.rnd, "", 0)
    63  	table.Table = tree.MakeUnqualifiedTableName(s.name("tab"))
    64  	return table, true
    65  }
    66  
    67  func makeDropTable(s *Smither) (tree.Statement, bool) {
    68  	_, _, tableRef, _, ok := s.getSchemaTable()
    69  	if !ok {
    70  		return nil, false
    71  	}
    72  
    73  	return &tree.DropTable{
    74  		Names:        tree.TableNames{*tableRef.TableName},
    75  		DropBehavior: s.randDropBehavior(),
    76  	}, true
    77  }
    78  
    79  func makeRenameTable(s *Smither) (tree.Statement, bool) {
    80  	_, _, tableRef, _, ok := s.getSchemaTable()
    81  	if !ok {
    82  		return nil, false
    83  	}
    84  
    85  	newName, err := tree.NewUnresolvedObjectName(
    86  		1 /* numParts */, [3]string{string(s.name("tab"))}, tree.NoAnnotation,
    87  	)
    88  	if err != nil {
    89  		return nil, false
    90  	}
    91  
    92  	return &tree.RenameTable{
    93  		Name:    tableRef.TableName.ToUnresolvedObjectName(),
    94  		NewName: newName,
    95  	}, true
    96  }
    97  
    98  func makeRenameColumn(s *Smither) (tree.Statement, bool) {
    99  	_, _, tableRef, _, ok := s.getSchemaTable()
   100  	if !ok {
   101  		return nil, false
   102  	}
   103  	col := tableRef.Columns[s.rnd.Intn(len(tableRef.Columns))]
   104  
   105  	return &tree.RenameColumn{
   106  		Table:   *tableRef.TableName,
   107  		Name:    col.Name,
   108  		NewName: s.name("col"),
   109  	}, true
   110  }
   111  
   112  func makeAlterColumnType(s *Smither) (tree.Statement, bool) {
   113  	_, _, tableRef, _, ok := s.getSchemaTable()
   114  	if !ok {
   115  		return nil, false
   116  	}
   117  	typ := sqlbase.RandColumnType(s.rnd)
   118  	col := tableRef.Columns[s.rnd.Intn(len(tableRef.Columns))]
   119  
   120  	return &tree.AlterTable{
   121  		Table: tableRef.TableName.ToUnresolvedObjectName(),
   122  		Cmds: tree.AlterTableCmds{
   123  			&tree.AlterTableAlterColumnType{
   124  				Column: col.Name,
   125  				ToType: typ,
   126  			},
   127  		},
   128  	}, true
   129  }
   130  
   131  func makeAddColumn(s *Smither) (tree.Statement, bool) {
   132  	_, _, tableRef, colRefs, ok := s.getSchemaTable()
   133  	if !ok {
   134  		return nil, false
   135  	}
   136  	colRefs.stripTableName()
   137  	t := sqlbase.RandColumnType(s.rnd)
   138  	col, err := tree.NewColumnTableDef(s.name("col"), t, false /* isSerial */, nil)
   139  	if err != nil {
   140  		return nil, false
   141  	}
   142  	col.Nullable.Nullability = s.randNullability()
   143  	if s.coin() {
   144  		col.DefaultExpr.Expr = &tree.ParenExpr{Expr: makeScalar(s, t, nil)}
   145  	} else if s.coin() {
   146  		col.Computed.Computed = true
   147  		col.Computed.Expr = &tree.ParenExpr{Expr: makeScalar(s, t, colRefs)}
   148  	}
   149  	for s.coin() {
   150  		col.CheckExprs = append(col.CheckExprs, tree.ColumnTableDefCheckExpr{
   151  			Expr: makeBoolExpr(s, colRefs),
   152  		})
   153  	}
   154  
   155  	return &tree.AlterTable{
   156  		Table: tableRef.TableName.ToUnresolvedObjectName(),
   157  		Cmds: tree.AlterTableCmds{
   158  			&tree.AlterTableAddColumn{
   159  				ColumnDef: col,
   160  			},
   161  		},
   162  	}, true
   163  }
   164  
   165  func makeJSONComputedColumn(s *Smither) (tree.Statement, bool) {
   166  	_, _, tableRef, colRefs, ok := s.getSchemaTable()
   167  	if !ok {
   168  		return nil, false
   169  	}
   170  	colRefs.stripTableName()
   171  	// Shuffle columns and find the first one that's JSON.
   172  	s.rnd.Shuffle(len(colRefs), func(i, j int) {
   173  		colRefs[i], colRefs[j] = colRefs[j], colRefs[i]
   174  	})
   175  	var ref *colRef
   176  	for _, c := range colRefs {
   177  		if c.typ.Family() == types.JsonFamily {
   178  			ref = c
   179  			break
   180  		}
   181  	}
   182  	// If we didn't find any JSON columns, return.
   183  	if ref == nil {
   184  		return nil, false
   185  	}
   186  	col, err := tree.NewColumnTableDef(s.name("col"), types.Jsonb, false /* isSerial */, nil)
   187  	if err != nil {
   188  		return nil, false
   189  	}
   190  	col.Computed.Computed = true
   191  	col.Computed.Expr = tree.NewTypedBinaryExpr(tree.JSONFetchText, ref.typedExpr(), sqlbase.RandDatumSimple(s.rnd, types.String), types.String)
   192  
   193  	return &tree.AlterTable{
   194  		Table: tableRef.TableName.ToUnresolvedObjectName(),
   195  		Cmds: tree.AlterTableCmds{
   196  			&tree.AlterTableAddColumn{
   197  				ColumnDef: col,
   198  			},
   199  		},
   200  	}, true
   201  }
   202  
   203  func makeDropColumn(s *Smither) (tree.Statement, bool) {
   204  	_, _, tableRef, _, ok := s.getSchemaTable()
   205  	if !ok {
   206  		return nil, false
   207  	}
   208  	col := tableRef.Columns[s.rnd.Intn(len(tableRef.Columns))]
   209  
   210  	return &tree.AlterTable{
   211  		Table: tableRef.TableName.ToUnresolvedObjectName(),
   212  		Cmds: tree.AlterTableCmds{
   213  			&tree.AlterTableDropColumn{
   214  				Column:       col.Name,
   215  				DropBehavior: s.randDropBehavior(),
   216  			},
   217  		},
   218  	}, true
   219  }
   220  
   221  func makeAlterPrimaryKey(s *Smither) (tree.Statement, bool) {
   222  	_, _, tableRef, _, ok := s.getSchemaTable()
   223  	if !ok {
   224  		return nil, false
   225  	}
   226  	// Collect all columns that are NOT NULL to be candidate new primary keys.
   227  	var candidateColumns tree.IndexElemList
   228  	for _, c := range tableRef.Columns {
   229  		if c.Nullable.Nullability == tree.NotNull {
   230  			candidateColumns = append(candidateColumns, tree.IndexElem{Column: c.Name})
   231  		}
   232  	}
   233  	if len(candidateColumns) == 0 {
   234  		return nil, false
   235  	}
   236  	s.rnd.Shuffle(len(candidateColumns), func(i, j int) {
   237  		candidateColumns[i], candidateColumns[j] = candidateColumns[j], candidateColumns[i]
   238  	})
   239  	// Pick some randomly short prefix of the candidate columns as a potential new primary key.
   240  	i := 1
   241  	for len(candidateColumns) > i && s.rnd.Intn(2) == 0 {
   242  		i++
   243  	}
   244  	candidateColumns = candidateColumns[:i]
   245  	return &tree.AlterTable{
   246  		Table: tableRef.TableName.ToUnresolvedObjectName(),
   247  		Cmds: tree.AlterTableCmds{
   248  			&tree.AlterTableAlterPrimaryKey{
   249  				Columns: candidateColumns,
   250  			},
   251  		},
   252  	}, true
   253  }
   254  
   255  func makeCreateIndex(s *Smither) (tree.Statement, bool) {
   256  	_, _, tableRef, _, ok := s.getSchemaTable()
   257  	if !ok {
   258  		return nil, false
   259  	}
   260  	var cols tree.IndexElemList
   261  	seen := map[tree.Name]bool{}
   262  	inverted := false
   263  	unique := s.coin()
   264  	for len(cols) < 1 || s.coin() {
   265  		col := tableRef.Columns[s.rnd.Intn(len(tableRef.Columns))]
   266  		if seen[col.Name] {
   267  			continue
   268  		}
   269  		seen[col.Name] = true
   270  		// If this is the first column and it's invertable (i.e., JSONB), make an inverted index.
   271  		if len(cols) == 0 &&
   272  			sqlbase.ColumnTypeIsInvertedIndexable(tree.MustBeStaticallyKnownType(col.Type)) {
   273  			inverted = true
   274  			unique = false
   275  			cols = append(cols, tree.IndexElem{
   276  				Column: col.Name,
   277  			})
   278  			break
   279  		}
   280  		if sqlbase.ColumnTypeIsIndexable(tree.MustBeStaticallyKnownType(col.Type)) {
   281  			cols = append(cols, tree.IndexElem{
   282  				Column:    col.Name,
   283  				Direction: s.randDirection(),
   284  			})
   285  		}
   286  	}
   287  	var storing tree.NameList
   288  	for !inverted && s.coin() {
   289  		col := tableRef.Columns[s.rnd.Intn(len(tableRef.Columns))]
   290  		if seen[col.Name] {
   291  			continue
   292  		}
   293  		seen[col.Name] = true
   294  		storing = append(storing, col.Name)
   295  	}
   296  
   297  	return &tree.CreateIndex{
   298  		Name:         s.name("idx"),
   299  		Table:        *tableRef.TableName,
   300  		Unique:       unique,
   301  		Columns:      cols,
   302  		Storing:      storing,
   303  		Inverted:     inverted,
   304  		Concurrently: s.coin(),
   305  	}, true
   306  }
   307  
   308  func makeDropIndex(s *Smither) (tree.Statement, bool) {
   309  	tin, _, _, ok := s.getRandIndex()
   310  	return &tree.DropIndex{
   311  		IndexList:    tree.TableIndexNames{tin},
   312  		DropBehavior: s.randDropBehavior(),
   313  		Concurrently: s.coin(),
   314  	}, ok
   315  }
   316  
   317  func makeRenameIndex(s *Smither) (tree.Statement, bool) {
   318  	tin, _, _, ok := s.getRandIndex()
   319  	return &tree.RenameIndex{
   320  		Index:   tin,
   321  		NewName: tree.UnrestrictedName(s.name("idx")),
   322  	}, ok
   323  }