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 }