github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/norm/factory_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 norm_test
    12  
    13  import (
    14  	"testing"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/opt/norm"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/opt/testutils"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/opt/testutils/testcat"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/opt/xform"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    25  )
    26  
    27  // TestSimplifyFilters tests factory.SimplifyFilters. It's hard to fully test
    28  // using SQL, as And operator rules simplify the expression before the Filters
    29  // operator is created.
    30  func TestSimplifyFilters(t *testing.T) {
    31  	evalCtx := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings())
    32  
    33  	cat := testcat.New()
    34  	if _, err := cat.ExecuteDDL("CREATE TABLE a (x INT PRIMARY KEY, y INT)"); err != nil {
    35  		t.Fatal(err)
    36  	}
    37  
    38  	var f norm.Factory
    39  	f.Init(&evalCtx, cat)
    40  
    41  	tn := tree.NewTableName("t", "a")
    42  	a := f.Metadata().AddTable(cat.Table(tn), tn)
    43  	ax := a.ColumnID(0)
    44  
    45  	variable := f.ConstructVariable(ax)
    46  	constant := f.ConstructConst(tree.NewDInt(1), types.Int)
    47  	eq := f.ConstructEq(variable, constant)
    48  
    49  	// Filters expression evaluates to False if any operand is False.
    50  	vals := f.ConstructValues(memo.ScalarListWithEmptyTuple, &memo.ValuesPrivate{
    51  		Cols: opt.ColList{},
    52  		ID:   f.Metadata().NextUniqueID(),
    53  	})
    54  	filters := memo.FiltersExpr{
    55  		f.ConstructFiltersItem(eq),
    56  		f.ConstructFiltersItem(memo.FalseSingleton),
    57  		f.ConstructFiltersItem(eq),
    58  	}
    59  	sel := f.ConstructSelect(vals, filters)
    60  	if sel.Relational().Cardinality.Max != 0 {
    61  		t.Fatalf("result should have been collapsed to zero cardinality rowset")
    62  	}
    63  
    64  	// Filters operator skips True operands.
    65  	filters = memo.FiltersExpr{f.ConstructFiltersItem(eq), f.ConstructFiltersItem(memo.TrueSingleton)}
    66  	sel = f.ConstructSelect(vals, filters)
    67  	if len(sel.(*memo.SelectExpr).Filters) != 1 {
    68  		t.Fatalf("filters result should have filtered True operator")
    69  	}
    70  }
    71  
    72  // Test CopyAndReplace on an already optimized join. Before CopyAndReplace is
    73  // called, the join has a placeholder that causes the optimizer to use a merge
    74  // join. After CopyAndReplace substitutes a constant for the placeholder, the
    75  // optimizer switches to a lookup join. A similar pattern is used by the
    76  // ApplyJoin execution operator which replaces variables with constants in an
    77  // already optimized tree. The CopyAndReplace code must take care to copy over
    78  // the normalized tree rather than the optimized tree by using the FirstExpr
    79  // method.
    80  func TestCopyAndReplace(t *testing.T) {
    81  	cat := testcat.New()
    82  	if _, err := cat.ExecuteDDL("CREATE TABLE ab (a INT PRIMARY KEY, b INT)"); err != nil {
    83  		t.Fatal(err)
    84  	}
    85  	if _, err := cat.ExecuteDDL("CREATE TABLE cde (c INT PRIMARY KEY, d INT, e INT, INDEX(d))"); err != nil {
    86  		t.Fatal(err)
    87  	}
    88  
    89  	evalCtx := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings())
    90  
    91  	var o xform.Optimizer
    92  	testutils.BuildQuery(t, &o, cat, &evalCtx, "SELECT * FROM ab INNER JOIN cde ON a=c AND d=$1")
    93  
    94  	if e, err := o.Optimize(); err != nil {
    95  		t.Fatal(err)
    96  	} else if e.Op() != opt.MergeJoinOp {
    97  		t.Errorf("expected optimizer to choose merge-join, not %v", e.Op())
    98  	}
    99  
   100  	m := o.Factory().DetachMemo()
   101  
   102  	o.Init(&evalCtx, cat)
   103  	var replaceFn norm.ReplaceFunc
   104  	replaceFn = func(e opt.Expr) opt.Expr {
   105  		if e.Op() == opt.PlaceholderOp {
   106  			return o.Factory().ConstructConstVal(tree.NewDInt(1), types.Int)
   107  		}
   108  		return o.Factory().CopyAndReplaceDefault(e, replaceFn)
   109  	}
   110  	o.Factory().CopyAndReplace(m.RootExpr().(memo.RelExpr), m.RootProps(), replaceFn)
   111  
   112  	if e, err := o.Optimize(); err != nil {
   113  		t.Fatal(err)
   114  	} else if e.Op() != opt.LookupJoinOp {
   115  		t.Errorf("expected optimizer to choose lookup-join, not %v", e.Op())
   116  	}
   117  }