Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
123323: randgen: make a few things deterministic r=yuzefovich a=yuzefovich

This commit audits all usages of `map` in `randgen` package to ensure that deterministic output is produced. Namely, if we iterate over a map and accumulate state dependent on the iteration order, maps are now replaced with slices.

See each commit for details.

Informs: cockroachdb#122766.

Epic: None

Release note: None

Co-authored-by: Yahor Yuzefovich <[email protected]>
  • Loading branch information
craig[bot] and yuzefovich committed Apr 30, 2024
2 parents 82bbf6b + e9e9107 commit 6286301
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 112 deletions.
1 change: 0 additions & 1 deletion pkg/sql/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,6 @@ go_test(
"pgwire_internal_test.go",
"plan_opt_test.go",
"privileged_accessor_test.go",
"rand_test.go",
"region_util_test.go",
"rename_test.go",
"revert_test.go",
Expand Down
37 changes: 0 additions & 37 deletions pkg/sql/rand_test.go

This file was deleted.

52 changes: 38 additions & 14 deletions pkg/sql/randgen/mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@ func statisticsMutator(
}
rowCount := randNonNegInt(rng)
cols := map[tree.Name]*tree.ColumnTableDef{}
colStats := map[tree.Name]*stats.JSONStatistic{}
var allStats []*stats.JSONStatistic
// colNameToStatIdx is a mapping from column name to index within
// allStats for the corresponding statistic object.
colNameToStatIdx := map[tree.Name]int{}
makeHistogram := func(col *tree.ColumnTableDef) {
// If an index appeared before a column definition, col
// can be nil.
Expand All @@ -218,8 +221,8 @@ func statisticsMutator(
}
colType := tree.MustBeStaticallyKnownType(col.Type)
h := randHistogram(rng, colType)
stat := colStats[col.Name]
if err := stat.SetHistogram(&h); err != nil {
statIdx := colNameToStatIdx[col.Name]
if err := allStats[statIdx].SetHistogram(&h); err != nil {
panic(err)
}
}
Expand All @@ -235,15 +238,16 @@ func statisticsMutator(
avgSize = uint64(rng.Int63n(32))
}
cols[def.Name] = def
colStats[def.Name] = &stats.JSONStatistic{
colNameToStatIdx[def.Name] = len(allStats)
allStats = append(allStats, &stats.JSONStatistic{
Name: "__auto__",
CreatedAt: "2000-01-01 00:00:00+00:00",
RowCount: uint64(rowCount),
Columns: []string{def.Name.String()},
DistinctCount: distinctCount,
NullCount: nullCount,
AvgSize: avgSize,
}
})
if (def.Unique.IsUnique && !def.Unique.WithoutIndex) || def.PrimaryKey.IsPrimaryKey {
makeHistogram(def)
}
Expand All @@ -259,11 +263,7 @@ func statisticsMutator(
}
}
}
if len(colStats) > 0 {
var allStats []*stats.JSONStatistic
for _, cs := range colStats {
allStats = append(allStats, cs)
}
if len(allStats) > 0 {
b, err := json.Marshal(allStats)
if err != nil {
// Should not happen.
Expand Down Expand Up @@ -406,7 +406,20 @@ func foreignKeyMutator(
rng *rand.Rand, stmts []tree.Statement,
) (mutated []tree.Statement, changed bool) {
// Find columns in the tables.
cols := map[tree.TableName][]*tree.ColumnTableDef{}
var cols [][]*tree.ColumnTableDef
// tableNames is 1-to-1 mapping with cols and contains the corresponding
// table name.
var tableNames []tree.TableName
// tableNameToColIdx returns the index for the given table name within cols
// (or ok=false if not found).
tableNameToColIdx := func(t tree.TableName) (idx int, ok bool) {
for i, table := range tableNames {
if t == table {
return i, true
}
}
return 0, false
}
byName := map[tree.TableName]*tree.CreateTable{}

// Keep track of referencing columns since we have a limitation that a
Expand Down Expand Up @@ -450,7 +463,13 @@ func foreignKeyMutator(
for _, def := range table.Defs {
switch def := def.(type) {
case *tree.ColumnTableDef:
cols[table.Table] = append(cols[table.Table], def)
idx, ok := tableNameToColIdx(table.Table)
if !ok {
idx = len(cols)
cols = append(cols, []*tree.ColumnTableDef{})
tableNames = append(tableNames, table.Table)
}
cols[idx] = append(cols[idx], def)
}
}
}
Expand Down Expand Up @@ -478,7 +497,8 @@ func foreignKeyMutator(
table := tables[rng.Intn(len(tables))]
// Choose a random column subset.
var fkCols []*tree.ColumnTableDef
for _, c := range cols[table.Table] {
colIdx, _ := tableNameToColIdx(table.Table)
for _, c := range cols[colIdx] {
if c.Computed.Computed {
// We don't support FK references from computed columns (#46672).
continue
Expand All @@ -505,7 +525,8 @@ func foreignKeyMutator(

// Check if a table has the needed column types.
LoopTable:
for refTable, refCols := range cols {
for j, refCols := range cols {
refTable := tableNames[j]
// Prevent circular and self references because
// generating valid INSERTs could become impossible or
// difficult algorithmically.
Expand All @@ -527,6 +548,9 @@ func foreignKeyMutator(
// indirectly).
continue LoopTable
}
// Note that this iteration over the 'dependsOn' map can
// produce different 'stack' (i.e. with random order of
// table names), but it doesn't impact the cycle detection.
for t := range dependsOn[curTable] {
stack = append(stack, t)
}
Expand Down
60 changes: 0 additions & 60 deletions pkg/sql/randgen/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,66 +407,6 @@ func PopulateTableWithRandData(
return numRowsInserted, nil
}

// GenerateRandInterestingTable takes a gosql.DB connection and creates
// a table with all the types in randInterestingDatums and rows of the
// interesting datums.
func GenerateRandInterestingTable(db *gosql.DB, dbName, tableName string) error {
var (
randTypes []*types.T
colNames []string
)
numRows := 0
for _, v := range randInterestingDatums {
colTyp := v[0].ResolvedType()
randTypes = append(randTypes, colTyp)
colNames = append(colNames, colTyp.Name())
if len(v) > numRows {
numRows = len(v)
}
}

var columns strings.Builder
comma := ""
for i, typ := range randTypes {
columns.WriteString(comma)
columns.WriteString(colNames[i])
columns.WriteString(" ")
columns.WriteString(typ.SQLString())
comma = ", "
}

createStatement := fmt.Sprintf("CREATE TABLE %s.%s (%s)", tree.NameString(dbName), tree.NameString(tableName), columns.String())
if _, err := db.Exec(createStatement); err != nil {
return err
}

row := make([]string, len(randTypes))
for i := 0; i < numRows; i++ {
for j, typ := range randTypes {
datums := randInterestingDatums[typ.Family()]
var d tree.Datum
if i < len(datums) {
d = datums[i]
} else {
d = tree.DNull
}
row[j] = tree.AsStringWithFlags(d, tree.FmtParsable)
}
var builder strings.Builder
comma := ""
for _, d := range row {
builder.WriteString(comma)
builder.WriteString(d)
comma = ", "
}
insertStmt := fmt.Sprintf("INSERT INTO %s.%s VALUES (%s)", tree.NameString(dbName), tree.NameString(tableName), builder.String())
if _, err := db.Exec(insertStmt); err != nil {
return err
}
}
return nil
}

// randColumnTableDef produces a random ColumnTableDef for a non-computed
// column, with a random type and nullability.
func randColumnTableDef(rng *rand.Rand, tableIdx int, colSuffix string) *tree.ColumnTableDef {
Expand Down

0 comments on commit 6286301

Please sign in to comment.