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  }