diff --git a/pkg/sql/opt/testutils/opttester/opt_tester.go b/pkg/sql/opt/testutils/opttester/opt_tester.go index d9b90ad616a3..90e0b02f1f1b 100644 --- a/pkg/sql/opt/testutils/opttester/opt_tester.go +++ b/pkg/sql/opt/testutils/opttester/opt_tester.go @@ -370,10 +370,18 @@ func New(catalog cat.Catalog, sql string) *OptTester { // Outputs the lowest cost tree for each step in optimization using the // standard unified diff format. Used for debugging the optimizer. // +// - normsteps [flags] +// +// Similar to optsteps, but only runs normalization rules. +// // - optstepsweb [flags] // // Similar to optsteps, but outputs a URL which displays the results. // +// - normstepsweb [flags] +// +// Similar to optstepsweb, but only runs normalization rules. +// // - exploretrace [flags] // // Outputs information about exploration rule application. Used for debugging @@ -725,14 +733,28 @@ func (ot *OptTester) RunCommand(tb testing.TB, d *datadriven.TestData) string { return tp.String() case "optsteps": - result, err := ot.OptSteps() + result, err := ot.OptSteps(true /* explore */) + if err != nil { + d.Fatalf(tb, "%+v", err) + } + return result + + case "normsteps": + result, err := ot.OptSteps(false /* explore */) if err != nil { d.Fatalf(tb, "%+v", err) } return result case "optstepsweb": - result, err := ot.OptStepsWeb() + result, err := ot.OptStepsWeb(true /* explore */) + if err != nil { + d.Fatalf(tb, "%+v", err) + } + return result + + case "normstepsweb": + result, err := ot.OptStepsWeb(false /* explore */) if err != nil { d.Fatalf(tb, "%+v", err) } @@ -1492,7 +1514,7 @@ func (ot *OptTester) RuleStats() (string, error) { // a better plan has been found, and weaker "----" header delimiters when not. // In both cases, the output shows the expressions that were changed or added by // the rule, even if the total expression tree cost worsened. -func (ot *OptTester) OptSteps() (string, error) { +func (ot *OptTester) OptSteps(explore bool) (string, error) { var prevBest, prev, next string ot.builder.Reset() @@ -1512,6 +1534,14 @@ func (ot *OptTester) OptSteps() (string, error) { break } + if !explore { + rule := os.LastRuleName() + if rule.IsExplore() { + // Stop at the first exploration rule. + break + } + } + if prev == "" { // Output starting tree. ot.optStepsDisplay("", next, os) @@ -1543,15 +1573,18 @@ func (ot *OptTester) OptSteps() (string, error) { // OptStepsWeb is similar to Optsteps but it uses a special web page for // formatting the output. The result will be an URL which contains the encoded // data. -func (ot *OptTester) OptStepsWeb() (string, error) { +func (ot *OptTester) OptStepsWeb(explore bool) (string, error) { normDiffStr, err := ot.optStepsNormDiff() if err != nil { return "", err } - exploreDiffStr, err := ot.optStepsExploreDiff() - if err != nil { - return "", err + var exploreDiffStr string + if explore { + exploreDiffStr, err = ot.optStepsExploreDiff() + if err != nil { + return "", err + } } url, err := ot.encodeOptstepsURL(normDiffStr, exploreDiffStr) if err != nil { diff --git a/pkg/sql/opt/testutils/opttester/testdata/opt-steps b/pkg/sql/opt/testutils/opttester/testdata/opt-steps index 16b0b0f0129d..3ffb8f2c7cc1 100644 --- a/pkg/sql/opt/testutils/opttester/testdata/opt-steps +++ b/pkg/sql/opt/testutils/opttester/testdata/opt-steps @@ -177,6 +177,99 @@ Final best expression ├── key: (1) └── fd: ()-->(2) +normsteps +SELECT * FROM ab WHERE b=1 +---- +================================================================================ +Initial expression + Cost: 1098.77 +================================================================================ + project + ├── columns: a:1(int!null) b:2(int!null) + ├── key: (1) + ├── fd: ()-->(2) + └── select + ├── columns: a:1(int!null) b:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid) + ├── key: (1) + ├── fd: ()-->(2), (1)-->(3,4) + ├── scan ab + │ ├── columns: a:1(int!null) b:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid) + │ ├── key: (1) + │ └── fd: (1)-->(2-4) + └── filters + └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)] + ├── variable: b:2 [type=int] + └── const: 1 [type=int] +================================================================================ +PruneSelectCols + Cost: 1078.57 +================================================================================ + project + ├── columns: a:1(int!null) b:2(int!null) + ├── key: (1) + ├── fd: ()-->(2) + └── select + - ├── columns: a:1(int!null) b:2(int!null) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid) + + ├── columns: a:1(int!null) b:2(int!null) + ├── key: (1) + - ├── fd: ()-->(2), (1)-->(3,4) + + ├── fd: ()-->(2) + ├── scan ab + - │ ├── columns: a:1(int!null) b:2(int) crdb_internal_mvcc_timestamp:3(decimal) tableoid:4(oid) + + │ ├── columns: a:1(int!null) b:2(int) + │ ├── key: (1) + - │ └── fd: (1)-->(2-4) + + │ └── fd: (1)-->(2) + └── filters + └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)] + ├── variable: b:2 [type=int] + └── const: 1 [type=int] +================================================================================ +EliminateProject + Cost: 1078.45 +================================================================================ + -project + +select + ├── columns: a:1(int!null) b:2(int!null) + ├── key: (1) + ├── fd: ()-->(2) + - └── select + - ├── columns: a:1(int!null) b:2(int!null) + - ├── key: (1) + - ├── fd: ()-->(2) + - ├── scan ab + - │ ├── columns: a:1(int!null) b:2(int) + - │ ├── key: (1) + - │ └── fd: (1)-->(2) + - └── filters + - └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)] + - ├── variable: b:2 [type=int] + - └── const: 1 [type=int] + + ├── scan ab + + │ ├── columns: a:1(int!null) b:2(int) + + │ ├── key: (1) + + │ └── fd: (1)-->(2) + + └── filters + + └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)] + + ├── variable: b:2 [type=int] + + └── const: 1 [type=int] +================================================================================ +Final best expression + Cost: 1078.45 +================================================================================ + select + ├── columns: a:1(int!null) b:2(int!null) + ├── key: (1) + ├── fd: ()-->(2) + ├── scan ab@ab_b_idx + │ ├── columns: a:1(int!null) b:2(int) + │ ├── key: (1) + │ └── fd: (1)-->(2) + └── filters + └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)] + ├── variable: b:2 [type=int] + └── const: 1 [type=int] + exec-ddl CREATE TABLE customers ( id INT8 NOT NULL,