Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sql: Add unit tests for planning inside the new schema changer #65780

Merged
merged 1 commit into from
Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ ALL_TESTS = [
"//pkg/sql/schemachanger/scbuild:scbuild_test",
"//pkg/sql/schemachanger/scexec:scexec_test",
"//pkg/sql/schemachanger/scpb:scpb_test",
"//pkg/sql/schemachanger/scplan:scplan_test",
"//pkg/sql/schemachanger:schemachanger_test",
"//pkg/sql/sem/builtins:builtins_test",
"//pkg/sql/sem/tree/eval_test:eval_test_test",
Expand Down
8 changes: 4 additions & 4 deletions pkg/sql/schemachanger/scgraphviz/graphviz.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,11 @@ func htmlLabel(o interface{}) dot.HTML {
return dot.HTML(buf.String())
}

// toMap converts a struct to a map, field by field. If at any point a protobuf
// ToMap converts a struct to a map, field by field. If at any point a protobuf
// message is encountered, it is converted to a map using jsonpb to marshal it
// to json and then marshaling it back to a map. This approach allows zero
// values to be effectively omitted.
func toMap(v interface{}) (interface{}, error) {
func ToMap(v interface{}) (interface{}, error) {
if v == nil {
return nil, nil
}
Expand Down Expand Up @@ -202,7 +202,7 @@ func toMap(v interface{}) (interface{}, error) {
continue
}
var err error
if m[vt.Field(i).Name], err = toMap(vvf.Interface()); err != nil {
if m[vt.Field(i).Name], err = ToMap(vvf.Interface()); err != nil {
return nil, err
}
}
Expand Down Expand Up @@ -235,7 +235,7 @@ var objectTemplate = template.Must(template.New("obj").Funcs(template.FuncMap{
"isStruct": func(v interface{}) bool {
return reflect.Indirect(reflect.ValueOf(v)).Kind() == reflect.Struct
},
"toMap": toMap,
"toMap": ToMap,
}).Parse(`
{{- define "key" -}}
<td>
Expand Down
10 changes: 7 additions & 3 deletions pkg/sql/schemachanger/scpb/attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (id IndexID) String() string {

// String implements AttributeValue.
func (id ElementTypeID) String() string {
return strconv.Itoa(int(id))
return ElementIDToString(id)
}

// String implements AttributeValue.
Expand Down Expand Up @@ -146,16 +146,20 @@ func (a Attributes) Equal(other Attributes) bool {
// String seralizes attribute into a string
func (a Attributes) String() string {
result := strings.Builder{}
result.WriteString("[ ")
result.WriteString(a.Get(AttributeType).String())
result.WriteString(":{")
for attribIdx, attrib := range a.values {
if attrib.key == AttributeType {
continue
}
result.WriteString(attrib.key.String())
result.WriteString(": ")
result.WriteString(attrib.value.String())
if attribIdx < len(a.values)-1 {
result.WriteString(", ")
}
}
result.WriteString(" ]")
result.WriteString("}")
return result.String()
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/sql/schemachanger/scpb/attribute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,22 @@ func TestGetAttribute(t *testing.T) {

// Sanity: Validate basic string conversion, equality,
// and inequality.
expectedStr := `[ Type: 4, DescID: 3, DepID: 1, ColumnID: 2 ]`
expectedStr := `SequenceDependency:{DescID: 3, DepID: 1, ColumnID: 2}`
require.Equal(t, expectedStr, seqElem.GetAttributes().String(), "Attribute string conversion is broken.")
require.True(t, seqElem.GetAttributes().Equal(seqElem.GetAttributes()))
require.False(t, seqElem.GetAttributes().Equal(seqElemDiff.GetAttributes()))

// Sanity: Validate type references, then check if type comparisons
// work.
typeBackRef := TypeReference{TypeID: 3, DescID: 1}
expectedStr = `[ Type: 10, DescID: 3, DepID: 1 ]`
expectedStr = `TypeReference:{DescID: 3, DepID: 1}`
require.Equal(t, expectedStr, typeBackRef.GetAttributes().String(), "Attribute string conversion is broken.")
require.False(t, seqElem.GetAttributes().Equal(typeBackRef.GetAttributes()))
require.False(t, typeBackRef.GetAttributes().Equal(seqElem.GetAttributes()))

// Sanity: Validate attribute fetching for both types.
require.Equal(t, "1", typeBackRef.GetAttributes().Get(AttributeDepID).String())
require.Equal(t, "3", typeBackRef.GetAttributes().Get(AttributeDescID).String())
require.Equal(t, "10", typeBackRef.GetAttributes().Get(AttributeType).String())
require.Equal(t, "TypeReference", typeBackRef.GetAttributes().Get(AttributeType).String())
require.Equal(t, "4", seqElemDiff.GetAttributes().Get(AttributeColumnID).String())
}
8 changes: 8 additions & 0 deletions pkg/sql/schemachanger/scpb/elements.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ func NewTarget(dir Target_Direction, elem Element) *Target {
type ElementTypeID int

var typeToElementID map[reflect.Type]ElementTypeID
var elementIDToString map[ElementTypeID]string

func init() {
typ := reflect.TypeOf((*ElementProto)(nil)).Elem()
typeToElementID = make(map[reflect.Type]ElementTypeID, typ.NumField())
elementIDToString = make(map[ElementTypeID]string, typ.NumField())
for i := 0; i < typ.NumField(); i++ {
f := typ.Field(i)
protoFlags := strings.Split(f.Tag.Get("protobuf"), ",")
Expand All @@ -75,6 +77,7 @@ func init() {
panic(errors.Wrapf(err, "failed to extract ID from protobuf tag: %q", protoFlags))
}
typeToElementID[f.Type] = ElementTypeID(id)
elementIDToString[ElementTypeID(id)] = strings.TrimPrefix(f.Type.String(), "*scpb.")
}
}

Expand All @@ -83,6 +86,11 @@ func ElementType(el Element) ElementTypeID {
return typeToElementID[reflect.TypeOf(el)]
}

// ElementIDToString determines the type ID of a element
func ElementIDToString(id ElementTypeID) string {
return elementIDToString[id]
}

// DescriptorID implements the Element interface.
func (e *Column) DescriptorID() descpb.ID { return e.TableID }

Expand Down
40 changes: 39 additions & 1 deletion pkg/sql/schemachanger/scplan/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "scplan",
Expand All @@ -21,3 +21,41 @@ go_library(
"@com_github_cockroachdb_errors//:errors",
],
)

go_test(
name = "scplan_test",
srcs = [
"main_test.go",
"plan_test.go",
],
data = glob(["testdata/**"]),
deps = [
":scplan",
"//pkg/base",
"//pkg/kv",
"//pkg/security",
"//pkg/security/securitytest",
"//pkg/server",
"//pkg/sql",
"//pkg/sql/catalog/descpb",
"//pkg/sql/catalog/descs",
"//pkg/sql/catalog/resolver",
"//pkg/sql/parser",
"//pkg/sql/schemachanger/scbuild",
"//pkg/sql/schemachanger/scgraph",
"//pkg/sql/schemachanger/scgraphviz",
"//pkg/sql/schemachanger/scpb",
"//pkg/sql/sem/tree",
"//pkg/sql/sessiondatapb",
"//pkg/testutils/serverutils",
"//pkg/testutils/sqlutils",
"//pkg/testutils/testcluster",
"//pkg/util/leaktest",
"//pkg/util/log",
"//pkg/util/randutil",
"@com_github_cockroachdb_datadriven//:datadriven",
"@com_github_cockroachdb_errors//:errors",
"@com_github_stretchr_testify//require",
"@in_gopkg_yaml_v2//:yaml_v2",
],
)
31 changes: 31 additions & 0 deletions pkg/sql/schemachanger/scplan/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2021 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package scplan_test

import (
"os"
"testing"

"github.com/cockroachdb/cockroach/pkg/security"
"github.com/cockroachdb/cockroach/pkg/security/securitytest"
"github.com/cockroachdb/cockroach/pkg/server"
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
"github.com/cockroachdb/cockroach/pkg/testutils/testcluster"
"github.com/cockroachdb/cockroach/pkg/util/randutil"
)

func TestMain(m *testing.M) {
security.SetAssetLoader(securitytest.EmbeddedAssets)
randutil.SeedForTests()
serverutils.InitTestServerFactory(server.TestServerFactory)
serverutils.InitTestClusterFactory(testcluster.TestClusterFactory)
os.Exit(m.Run())
}
23 changes: 15 additions & 8 deletions pkg/sql/schemachanger/scplan/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ type Params struct {
//
// This doesn't do anything right now.
CreatedDescriptorIDs catalog.DescriptorIDSet
// DisableOpRandomization disables randomization for the final set of
// operations.
DisableOpRandomization bool
}

// A Plan is a schema change plan, primarily containing ops to be executed that
Expand Down Expand Up @@ -110,7 +113,7 @@ func MakePlan(initialStates []*scpb.Node, params Params) (_ Plan, err error) {
}); err != nil {
return Plan{}, err
}
stages := buildStages(initialStates, g)
stages := buildStages(initialStates, g, params)
return Plan{
Params: params,
InitialNodes: initialStates,
Expand All @@ -119,7 +122,7 @@ func MakePlan(initialStates []*scpb.Node, params Params) (_ Plan, err error) {
}, nil
}

func buildStages(init []*scpb.Node, g *scgraph.Graph) []Stage {
func buildStages(init []*scpb.Node, g *scgraph.Graph, params Params) []Stage {
// TODO(ajwerner): deal with the case where the target state was
// fulfilled by something that preceded the initial state.
cur := init
Expand Down Expand Up @@ -236,12 +239,15 @@ func buildStages(init []*scpb.Node, g *scgraph.Graph) []Stage {
// be order independent, however we will
// try to execute non-failing ones first.
opsSlice := s.Ops.Slice()
rand.Seed(timeutil.Now().UnixNano())
rand.Shuffle(len(opsSlice), func(i, j int) {
tmp := opsSlice[i]
opsSlice[i] = opsSlice[j]
opsSlice[j] = tmp
})
if !params.DisableOpRandomization {

rand.Seed(timeutil.Now().UnixNano())
rand.Shuffle(len(opsSlice), func(i, j int) {
tmp := opsSlice[i]
opsSlice[i] = opsSlice[j]
opsSlice[j] = tmp
})
}
// Place non-revertible operations at the end
sort.SliceStable(opsSlice, func(i, j int) bool {
if opsSlice[i].Revertible() == opsSlice[j].Revertible() {
Expand All @@ -251,6 +257,7 @@ func buildStages(init []*scpb.Node, g *scgraph.Graph) []Stage {
})
stages = append(stages, s)
cur = s.After

}
return stages
}
Loading