github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/ordering/scan_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 ordering
    12  
    13  import (
    14  	"fmt"
    15  	"testing"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/opt/constraint"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/opt/norm"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/opt/testutils/testcat"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    25  )
    26  
    27  func TestScan(t *testing.T) {
    28  	tc := testcat.New()
    29  	if _, err := tc.ExecuteDDL(
    30  		"CREATE TABLE t (c1 INT, c2 INT, c3 INT, c4 INT, PRIMARY KEY(c1, c2), INDEX(c3 DESC, c4))",
    31  	); err != nil {
    32  		t.Fatal(err)
    33  	}
    34  	evalCtx := tree.NewTestingEvalContext(nil /* st */)
    35  	var f norm.Factory
    36  	f.Init(evalCtx, tc)
    37  	md := f.Metadata()
    38  	tn := tree.NewUnqualifiedTableName("t")
    39  	tab := md.AddTable(tc.Table(tn), tn)
    40  
    41  	if c1 := tab.ColumnID(0); c1 != 1 {
    42  		t.Fatalf("unexpected ID for column c1: %d\n", c1)
    43  	}
    44  
    45  	// Make a constraint for the index.
    46  	var columns constraint.Columns
    47  	columns.Init([]opt.OrderingColumn{-3, +4, +1, +2})
    48  	var c constraint.Constraint
    49  	keyCtx := constraint.MakeKeyContext(&columns, evalCtx)
    50  	var span constraint.Span
    51  	span.Init(
    52  		constraint.MakeCompositeKey(tree.NewDInt(1), tree.NewDInt(10)),
    53  		constraint.IncludeBoundary,
    54  		constraint.MakeCompositeKey(tree.NewDInt(1), tree.NewDInt(20)),
    55  		constraint.IncludeBoundary,
    56  	)
    57  	c.InitSingleSpan(&keyCtx, &span)
    58  
    59  	// We have groups of test cases for various ScanPrivates.
    60  	type testCase struct {
    61  		req  string // required ordering
    62  		exp  string // "no", "fwd", or "rev"
    63  		prov string // provided ordering
    64  	}
    65  
    66  	type testGroup struct {
    67  		p     memo.ScanPrivate
    68  		cases []testCase
    69  	}
    70  
    71  	tests := []testGroup{
    72  		{ // group 1: primary index scan.
    73  			p: memo.ScanPrivate{
    74  				Table: tab,
    75  				Index: cat.PrimaryIndex,
    76  				Cols:  opt.MakeColSet(1, 2, 3, 4),
    77  			},
    78  			cases: []testCase{
    79  				{req: "", exp: "fwd", prov: ""},               // case 1
    80  				{req: "+1", exp: "fwd", prov: "+1"},           // case 2
    81  				{req: "-1", exp: "rev", prov: "-1"},           // case 3
    82  				{req: "+2", exp: "no"},                        // case 4
    83  				{req: "+1,+2", exp: "fwd", prov: "+1,+2"},     // case 5
    84  				{req: "-1,-2", exp: "rev", prov: "-1,-2"},     // case 6
    85  				{req: "+1,-2", exp: "no", prov: "+1,-2"},      // case 7
    86  				{req: "+(1|2)", exp: "fwd", prov: "+1"},       // case 8
    87  				{req: "+2 opt(1)", exp: "fwd", prov: "+1,+2"}, // case 9
    88  				{req: "-2 opt(1)", exp: "rev", prov: "-1,-2"}, // case 10
    89  			},
    90  		},
    91  		{ // group 2: secondary index scan.
    92  			p: memo.ScanPrivate{
    93  				Table: tab,
    94  				Index: 1,
    95  				Cols:  opt.MakeColSet(1, 2, 3, 4),
    96  			},
    97  			cases: []testCase{
    98  				{req: "", exp: "fwd", prov: ""},                          // case 1
    99  				{req: "-3", exp: "fwd", prov: "-3"},                      // case 2
   100  				{req: "+3", exp: "rev", prov: "+3"},                      // case 3
   101  				{req: "-3,+4", exp: "fwd", prov: "-3,+4"},                // case 4
   102  				{req: "+3,-4", exp: "rev", prov: "+3,-4"},                // case 5
   103  				{req: "+3,+4", exp: "no"},                                // case 6
   104  				{req: "-3,+4,+1", exp: "fwd", prov: "-3,+4,+1"},          // case 7
   105  				{req: "-3,+4,+1,+2", exp: "fwd", prov: "-3,+4,+1,+2"},    // case 8
   106  				{req: "-3,+2 opt(1,4)", exp: "fwd", prov: "-3,+4,+1,+2"}, // case 9
   107  				{req: "+3,-2 opt(1,4)", exp: "rev", prov: "+3,-4,-1,-2"}, // case 10
   108  			},
   109  		},
   110  		{ // group 3: scan with limit (forces forward scan).
   111  			p: memo.ScanPrivate{
   112  				Table:     tab,
   113  				Index:     cat.PrimaryIndex,
   114  				Cols:      opt.MakeColSet(1, 2, 3, 4),
   115  				HardLimit: +10,
   116  			},
   117  			cases: []testCase{
   118  				{req: "", exp: "fwd", prov: ""},               // case 1
   119  				{req: "+1", exp: "fwd", prov: "+1"},           // case 2
   120  				{req: "-1", exp: "no"},                        // case 3
   121  				{req: "+2", exp: "no"},                        // case 4
   122  				{req: "+1,+2", exp: "fwd", prov: "+1,+2"},     // case 5
   123  				{req: "-1,-2", exp: "no"},                     // case 6
   124  				{req: "+1,-2", exp: "no"},                     // case 7
   125  				{req: "+(1|2)", exp: "fwd", prov: "+1"},       // case 8
   126  				{req: "+2 opt(1)", exp: "fwd", prov: "+1,+2"}, // case 9
   127  				{req: "-2 opt(1)", exp: "no"},                 // case 10
   128  			},
   129  		},
   130  		{ // group 4: scan with reverse limit.
   131  			p: memo.ScanPrivate{
   132  				Table:     tab,
   133  				Index:     cat.PrimaryIndex,
   134  				Cols:      opt.MakeColSet(1, 2, 3, 4),
   135  				HardLimit: -10,
   136  			},
   137  			cases: []testCase{
   138  				{req: "", exp: "rev", prov: ""},               // case 1
   139  				{req: "+1", exp: "no"},                        // case 2
   140  				{req: "-1", exp: "rev", prov: "-1"},           // case 3
   141  				{req: "+2", exp: "no"},                        // case 4
   142  				{req: "+1,+2", exp: "no"},                     // case 5
   143  				{req: "-1,-2", exp: "rev", prov: "-1,-2"},     // case 6
   144  				{req: "+1,-2", exp: "no"},                     // case 7
   145  				{req: "+(1|2)", exp: "no"},                    // case 8
   146  				{req: "+2 opt(1)", exp: "no"},                 // case 9
   147  				{req: "-2 opt(1)", exp: "rev", prov: "-1,-2"}, // case 10
   148  			},
   149  		},
   150  		{ // group 5: scan with constraint.
   151  			p: memo.ScanPrivate{
   152  				Table:      tab,
   153  				Index:      1,
   154  				Cols:       opt.MakeColSet(1, 2, 3, 4),
   155  				Constraint: &c,
   156  			},
   157  			cases: []testCase{
   158  				{req: "-3", exp: "fwd", prov: ""},                   // case 1
   159  				{req: "-3,+4", exp: "fwd", prov: "+4"},              // case 2
   160  				{req: "-1 opt(3,4)", exp: "rev", prov: "-4,-1"},     // case 3
   161  				{req: "+1 opt(3)", exp: "no"},                       // case 4
   162  				{req: "-(1|2) opt(3,4)", exp: "rev", prov: "-4,-1"}, // case 5
   163  			},
   164  		},
   165  	}
   166  
   167  	for gIdx, g := range tests {
   168  		t.Run(fmt.Sprintf("group%d", gIdx+1), func(t *testing.T) {
   169  			for tcIdx, tc := range g.cases {
   170  				t.Run(fmt.Sprintf("case%d", tcIdx+1), func(t *testing.T) {
   171  					req := physical.ParseOrderingChoice(tc.req)
   172  					ok, rev := ScanPrivateCanProvide(md, &g.p, &req)
   173  					res := "no"
   174  					if ok {
   175  						if rev {
   176  							res = "rev"
   177  						} else {
   178  							res = "fwd"
   179  						}
   180  					}
   181  					if res != tc.exp {
   182  						t.Errorf("expected %s, got %s", tc.exp, res)
   183  					}
   184  					if ok {
   185  						scan := f.ConstructScan(&g.p)
   186  						prov := scanBuildProvided(scan, &req).String()
   187  						if prov != tc.prov {
   188  							t.Errorf("expected provided '%s', got '%s'", tc.prov, prov)
   189  						}
   190  					}
   191  				})
   192  			}
   193  		})
   194  	}
   195  }