github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/xform/optimizer_test.go (about) 1 // Copyright 2018 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 xform_test 12 13 import ( 14 "flag" 15 "strings" 16 "sync" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 20 "github.com/cockroachdb/cockroach/pkg/sql/opt" 21 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 22 "github.com/cockroachdb/cockroach/pkg/sql/opt/norm" 23 "github.com/cockroachdb/cockroach/pkg/sql/opt/testutils" 24 "github.com/cockroachdb/cockroach/pkg/sql/opt/testutils/opttester" 25 "github.com/cockroachdb/cockroach/pkg/sql/opt/testutils/testcat" 26 "github.com/cockroachdb/cockroach/pkg/sql/opt/xform" 27 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 28 "github.com/cockroachdb/cockroach/pkg/sql/types" 29 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 30 "github.com/cockroachdb/datadriven" 31 ) 32 33 func TestDetachMemo(t *testing.T) { 34 defer leaktest.AfterTest(t)() 35 catalog := testcat.New() 36 if _, err := catalog.ExecuteDDL("CREATE TABLE abc (a INT PRIMARY KEY, b INT, c STRING, INDEX (c))"); err != nil { 37 t.Fatal(err) 38 } 39 40 var o xform.Optimizer 41 evalCtx := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) 42 testutils.BuildQuery(t, &o, catalog, &evalCtx, "SELECT * FROM abc WHERE c=$1") 43 44 before := o.DetachMemo() 45 46 if !o.Memo().IsEmpty() { 47 t.Error("memo expression should be reinitialized by DetachMemo") 48 } 49 50 testutils.BuildQuery(t, &o, catalog, &evalCtx, "SELECT a=$1 FROM abc") 51 52 after := o.Memo() 53 if after == before { 54 t.Error("after memo cannot be the same as the detached memo") 55 } 56 57 if !strings.Contains(after.RootExpr().String(), "variable: a:1 [type=int]") { 58 t.Error("after memo did not contain expected operator") 59 } 60 61 if after.RootExpr().(memo.RelExpr).Memo() != after { 62 t.Error("after memo expression does not reference the after memo") 63 } 64 65 if before == o.Memo() { 66 t.Error("detached memo should not be reused") 67 } 68 69 if before.RootExpr().(memo.RelExpr).Memo() != before { 70 t.Error("detached memo expression does not reference the detached memo") 71 } 72 73 if !strings.Contains(before.RootExpr().String(), "variable: c:3 [type=string]") { 74 t.Error("detached memo did not contain expected operator") 75 } 76 } 77 78 // TestDetachMemoRace reproduces the condition in #34904: a detached memo still 79 // aliases table annotations in the metadata. The problematic annotation is a 80 // statistics object. Construction of new expression can trigger calculation of 81 // new statistics. 82 func TestDetachMemoRace(t *testing.T) { 83 defer leaktest.AfterTest(t)() 84 85 catalog := testcat.New() 86 87 _, err := catalog.ExecuteDDL("CREATE TABLE abc (a INT, b INT, c INT, d INT)") 88 if err != nil { 89 t.Fatal(err) 90 } 91 var o xform.Optimizer 92 evalCtx := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) 93 testutils.BuildQuery(t, &o, catalog, &evalCtx, "SELECT * FROM abc WHERE a = $1") 94 mem := o.DetachMemo() 95 96 var wg sync.WaitGroup 97 for i := 0; i < 4; i++ { 98 col := opt.ColumnID(i + 1) 99 wg.Add(1) 100 go func() { 101 var o xform.Optimizer 102 evalCtx := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) 103 o.Init(&evalCtx, catalog) 104 f := o.Factory() 105 var replaceFn norm.ReplaceFunc 106 replaceFn = func(e opt.Expr) opt.Expr { 107 if sel, ok := e.(*memo.SelectExpr); ok { 108 return f.ConstructSelect( 109 f.CopyAndReplaceDefault(sel.Input, replaceFn).(memo.RelExpr), 110 memo.FiltersExpr{f.ConstructFiltersItem( 111 f.ConstructEq( 112 f.ConstructVariable(col), 113 f.ConstructConst(tree.NewDInt(10), types.Int), 114 ), 115 )}, 116 ) 117 } 118 return f.CopyAndReplaceDefault(e, replaceFn) 119 } 120 // Rewrite the filter to use a different column, which will trigger creation 121 // of new table statistics. If the statistics object is aliased, this will 122 // be racy. 123 f.CopyAndReplace(mem.RootExpr().(memo.RelExpr), mem.RootProps(), replaceFn) 124 wg.Done() 125 }() 126 } 127 wg.Wait() 128 } 129 130 // TestCoster files can be run separately like this: 131 // make test PKG=./pkg/sql/opt/xform TESTS="TestCoster/sort" 132 // make test PKG=./pkg/sql/opt/xform TESTS="TestCoster/scan" 133 // ... 134 func TestCoster(t *testing.T) { 135 defer leaktest.AfterTest(t)() 136 runDataDrivenTest( 137 t, "testdata/coster/", 138 memo.ExprFmtHideRuleProps|memo.ExprFmtHideQualifications|memo.ExprFmtHideScalars| 139 memo.ExprFmtHideTypes, 140 ) 141 } 142 143 // TestPhysicalProps files can be run separately like this: 144 // make test PKG=./pkg/sql/opt/xform TESTS="TestPhysicalPropsFactory/ordering" 145 // make test PKG=./pkg/sql/opt/xform TESTS="TestPhysicalPropsFactory/presentation" 146 // ... 147 func TestPhysicalProps(t *testing.T) { 148 defer leaktest.AfterTest(t)() 149 runDataDrivenTest( 150 t, "testdata/physprops/", 151 memo.ExprFmtHideConstraints| 152 memo.ExprFmtHideRuleProps| 153 memo.ExprFmtHideStats| 154 memo.ExprFmtHideCost| 155 memo.ExprFmtHideQualifications| 156 memo.ExprFmtHideScalars| 157 memo.ExprFmtHideTypes, 158 ) 159 } 160 161 // TestRuleProps files can be run separately like this: 162 // make test PKG=./pkg/sql/opt/xform TESTS="TestRuleProps/orderings" 163 // ... 164 func TestRuleProps(t *testing.T) { 165 defer leaktest.AfterTest(t)() 166 runDataDrivenTest( 167 t, 168 "testdata/ruleprops/", 169 memo.ExprFmtHideStats|memo.ExprFmtHideCost|memo.ExprFmtHideQualifications| 170 memo.ExprFmtHideScalars|memo.ExprFmtHideTypes, 171 ) 172 } 173 174 // TestRules files can be run separately like this: 175 // make test PKG=./pkg/sql/opt/xform TESTS="TestRules/scan" 176 // make test PKG=./pkg/sql/opt/xform TESTS="TestRules/select" 177 // ... 178 func TestRules(t *testing.T) { 179 defer leaktest.AfterTest(t)() 180 runDataDrivenTest( 181 t, 182 "testdata/rules/", 183 memo.ExprFmtHideStats|memo.ExprFmtHideCost|memo.ExprFmtHideRuleProps| 184 memo.ExprFmtHideQualifications|memo.ExprFmtHideScalars|memo.ExprFmtHideTypes, 185 ) 186 } 187 188 var externalTestData = flag.String( 189 "d", "testdata/external/", "test files directory for TestExternal", 190 ) 191 192 // TestExternal contains test cases from external customers and external 193 // benchmarks (like TPCH), so that changes in their query plans can be monitored 194 // over time. 195 // 196 // TestExternal files can be run separately like this: 197 // make test PKG=./pkg/sql/opt/xform TESTS="TestExternal/tpch" 198 // ... 199 // 200 // Test files from another location can be run using the -d flag: 201 // make test PKG=./pkg/sql/opt/xform TESTS=TestExternal TESTFLAGS='-d /some-dir' 202 // 203 func TestExternal(t *testing.T) { 204 defer leaktest.AfterTest(t)() 205 runDataDrivenTest( 206 t, 207 *externalTestData, 208 memo.ExprFmtHideStats|memo.ExprFmtHideCost|memo.ExprFmtHideRuleProps| 209 memo.ExprFmtHideQualifications|memo.ExprFmtHideScalars|memo.ExprFmtHideTypes, 210 ) 211 } 212 213 // runDataDrivenTest runs data-driven testcases of the form 214 // <command> 215 // <SQL statement> 216 // ---- 217 // <expected results> 218 // 219 // See OptTester.Handle for supported commands. 220 func runDataDrivenTest(t *testing.T, path string, fmtFlags memo.ExprFmtFlags) { 221 datadriven.Walk(t, path, func(t *testing.T, path string) { 222 catalog := testcat.New() 223 datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { 224 tester := opttester.New(catalog, d.Input) 225 tester.Flags.ExprFormat = fmtFlags 226 return tester.RunCommand(t, d) 227 }) 228 }) 229 }