github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/props/func_dep_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 props_test
    12  
    13  import (
    14  	"testing"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func TestFuncDeps_ConstCols(t *testing.T) {
    22  	fd := &props.FuncDepSet{}
    23  	require.Equal(t, "()", fd.ConstantCols().String())
    24  	fd.AddConstants(c(1, 2))
    25  	require.Equal(t, "(1,2)", fd.ConstantCols().String())
    26  
    27  	fd2 := makeAbcdeFD(t)
    28  	require.Equal(t, "()", fd2.ConstantCols().String())
    29  	fd2.AddConstants(c(1, 2))
    30  	require.Equal(t, "(1,2)", fd.ConstantCols().String())
    31  }
    32  
    33  // Other tests also exercise the ColsAreKey methods.
    34  func TestFuncDeps_ColsAreKey(t *testing.T) {
    35  	// CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
    36  	// CREATE UNIQUE INDEX ON abcde (b, c)
    37  	// CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
    38  	// This case wouldn't actually happen with a real world query.
    39  	var loj props.FuncDepSet
    40  	preservedCols := c(1, 2, 3, 4, 5)
    41  	nullExtendedCols := c(10, 11, 12, 13, 14)
    42  	abcde := makeAbcdeFD(t)
    43  	mnpq := makeMnpqFD(t)
    44  	mnpq.AddSynthesizedCol(c(12, 13), 14)
    45  	loj.CopyFrom(abcde)
    46  	loj.MakeProduct(mnpq)
    47  	loj.AddConstants(c(3))
    48  	loj.MakeLeftOuter(abcde, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1, 10, 11))
    49  	loj.AddEquivalency(1, 10)
    50  	verifyFD(t, &loj, "key(10,11); ()-->(3), (1)-->(2,4,5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (12,13)~~>(14), (1,10,11)-->(14), (1)==(10), (10)==(1)")
    51  
    52  	testcases := []struct {
    53  		cols   opt.ColSet
    54  		strict bool
    55  		lax    bool
    56  	}{
    57  		{cols: c(1, 2, 3, 4, 5, 10, 11, 12, 13, 14), strict: true, lax: true},
    58  		{cols: c(1, 2, 3, 4, 5, 10, 12, 13, 14), strict: false, lax: false},
    59  		{cols: c(1, 11), strict: true, lax: true},
    60  		{cols: c(10, 11), strict: true, lax: true},
    61  		{cols: c(1), strict: false, lax: false},
    62  		{cols: c(10), strict: false, lax: false},
    63  		{cols: c(11), strict: false, lax: false},
    64  		{cols: c(), strict: false, lax: false},
    65  
    66  		// This case is interesting: if we take into account that 3 is a constant,
    67  		// we could put 2 and 3 together and use (2,3)~~>(1,4,5) and (1)==(10) to
    68  		// prove that (2,3) is a lax key. But this is only true when that constant
    69  		// value for 3 is not NULL. We would have to pass non-null information to
    70  		// the check. See #42731.
    71  		{cols: c(2, 11), strict: false, lax: false},
    72  	}
    73  
    74  	for _, tc := range testcases {
    75  		testColsAreStrictKey(t, &loj, tc.cols, tc.strict)
    76  		testColsAreLaxKey(t, &loj, tc.cols, tc.lax)
    77  	}
    78  }
    79  
    80  func TestFuncDeps_ComputeClosure(t *testing.T) {
    81  	// (a)-->(b,c,d)
    82  	// (b,c,e)-->(f)
    83  	// (d)==(e)
    84  	// (e)==(d)
    85  	fd1 := &props.FuncDepSet{}
    86  	fd1.AddSynthesizedCol(c(1), 2)
    87  	fd1.AddSynthesizedCol(c(1), 3)
    88  	fd1.AddSynthesizedCol(c(1), 4)
    89  	fd1.AddSynthesizedCol(c(2, 3, 5), 6)
    90  	fd1.AddEquivalency(4, 5)
    91  	verifyFD(t, fd1, "(1)-->(2-4), (2,3,5)-->(6), (4)==(5), (5)==(4)")
    92  
    93  	// (a)~~>(d)
    94  	// ()-->(b)
    95  	// (b)==(c)
    96  	// (c)==(b)
    97  	// (d)-->(e)
    98  	fd2 := &props.FuncDepSet{}
    99  	// This isn't intended to create a real lax key; just a lax dependency.
   100  	fd2.AddLaxKey(c(1), c(1, 4))
   101  	fd2.AddConstants(c(2))
   102  	fd2.AddEquivalency(2, 3)
   103  	fd2.AddSynthesizedCol(c(4), 5)
   104  	verifyFD(t, fd2, "lax-key(1); ()-->(2,3), (1)~~>(4), (2)==(3), (3)==(2), (4)-->(5)")
   105  
   106  	testcases := []struct {
   107  		fd       *props.FuncDepSet
   108  		in       opt.ColSet
   109  		expected opt.ColSet
   110  	}{
   111  		{fd: fd1, in: c(), expected: c()},
   112  		{fd: fd1, in: c(1), expected: c(1, 2, 3, 4, 5, 6)},
   113  		{fd: fd1, in: c(2), expected: c(2)},
   114  		{fd: fd1, in: c(2, 3, 4), expected: c(2, 3, 4, 5, 6)},
   115  		{fd: fd1, in: c(4), expected: c(4, 5)},
   116  
   117  		{fd: fd2, in: c(), expected: c(2, 3)},
   118  		{fd: fd2, in: c(1), expected: c(1, 2, 3)},
   119  		{fd: fd2, in: c(1, 4), expected: c(1, 2, 3, 4, 5)},
   120  	}
   121  
   122  	for _, tc := range testcases {
   123  		closure := tc.fd.ComputeClosure(tc.in)
   124  		if !closure.Equals(tc.expected) {
   125  			t.Errorf("in: %s, expected: %s, actual: %s", tc.in, tc.expected, closure)
   126  		}
   127  	}
   128  }
   129  
   130  func TestFuncDeps_InClosureOf(t *testing.T) {
   131  	// (a)~~>(d)
   132  	// ()-->(b)
   133  	// (b)==(c)
   134  	// (c)==(b)
   135  	// (d)-->(e)
   136  	fd := &props.FuncDepSet{}
   137  	fd.AddConstants(c(2))
   138  	// This isn't intended to create a real lax key; just a lax dependency.
   139  	fd.AddLaxKey(c(1), c(1, 4))
   140  	fd.AddEquivalency(2, 3)
   141  	fd.AddSynthesizedCol(c(4), 5)
   142  	verifyFD(t, fd, "lax-key(1); ()-->(2,3), (1)~~>(4), (2)==(3), (3)==(2), (4)-->(5)")
   143  
   144  	testcases := []struct {
   145  		cols     []opt.ColumnID
   146  		in       []opt.ColumnID
   147  		expected bool
   148  	}{
   149  		{cols: []opt.ColumnID{}, in: []opt.ColumnID{}, expected: true},
   150  		{cols: []opt.ColumnID{}, in: []opt.ColumnID{1}, expected: true},
   151  		{cols: []opt.ColumnID{2, 3}, in: []opt.ColumnID{}, expected: true},
   152  		{cols: []opt.ColumnID{2}, in: []opt.ColumnID{3}, expected: true},
   153  		{cols: []opt.ColumnID{3}, in: []opt.ColumnID{2}, expected: true},
   154  		{cols: []opt.ColumnID{3, 5}, in: []opt.ColumnID{2, 4}, expected: true},
   155  
   156  		{cols: []opt.ColumnID{1}, in: []opt.ColumnID{}, expected: false},
   157  		{cols: []opt.ColumnID{4}, in: []opt.ColumnID{5}, expected: false},
   158  		{cols: []opt.ColumnID{2, 3, 4}, in: []opt.ColumnID{1, 2, 3}, expected: false},
   159  	}
   160  
   161  	for _, tc := range testcases {
   162  		cols := c(tc.cols...)
   163  		in := c(tc.in...)
   164  		actual := fd.InClosureOf(cols, in)
   165  		if actual != tc.expected {
   166  			if tc.expected {
   167  				t.Errorf("expected %s to be in closure of %s", cols, in)
   168  			} else {
   169  				t.Errorf("expected %s to not be in closure of %s", cols, in)
   170  			}
   171  		}
   172  	}
   173  }
   174  
   175  func TestFuncDeps_ComputeEquivClosure(t *testing.T) {
   176  	// (a)==(b,d)
   177  	// (b)==(a,c)
   178  	// (c)==(b)
   179  	// (d)==(a)
   180  	// (a)~~>(e)
   181  	// (a)-->(f)
   182  	fd1 := &props.FuncDepSet{}
   183  	// This isn't intended to create a real lax key; just a lax dependency.
   184  	fd1.AddLaxKey(c(1), c(1, 5))
   185  	fd1.AddSynthesizedCol(c(1), 6)
   186  	fd1.AddEquivalency(1, 2)
   187  	fd1.AddEquivalency(2, 3)
   188  	fd1.AddEquivalency(1, 4)
   189  	verifyFD(t, fd1, "lax-key(1); (1)~~>(5), (1)-->(6), (1)==(2-4), (2)==(1,3,4), (3)==(1,2,4), (4)==(1-3)")
   190  
   191  	testcases := []struct {
   192  		fd       *props.FuncDepSet
   193  		in       opt.ColSet
   194  		expected opt.ColSet
   195  	}{
   196  		{fd: fd1, in: c(), expected: c()},
   197  		{fd: fd1, in: c(1), expected: c(1, 2, 3, 4)},
   198  		{fd: fd1, in: c(2), expected: c(1, 2, 3, 4)},
   199  		{fd: fd1, in: c(3), expected: c(1, 2, 3, 4)},
   200  		{fd: fd1, in: c(4), expected: c(1, 2, 3, 4)},
   201  		{fd: fd1, in: c(5, 6), expected: c(5, 6)},
   202  	}
   203  
   204  	for _, tc := range testcases {
   205  		closure := tc.fd.ComputeEquivClosure(tc.in)
   206  		if !closure.Equals(tc.expected) {
   207  			t.Errorf("in: %s, expected: %s, actual: %s", tc.in, tc.expected, closure)
   208  		}
   209  	}
   210  }
   211  
   212  func TestFuncDeps_EquivReps(t *testing.T) {
   213  	// (a)==(b,d)
   214  	// (b)==(a,c)
   215  	// (c)==(b)
   216  	// (a)~~>(e)
   217  	// (a)-->(f)
   218  	fd1 := &props.FuncDepSet{}
   219  	// This isn't intended to create a real lax key; just a lax dependency.
   220  	fd1.AddLaxKey(c(1), c(1, 5))
   221  	fd1.AddSynthesizedCol(c(1), 6)
   222  	fd1.AddEquivalency(1, 2)
   223  	fd1.AddEquivalency(2, 3)
   224  	verifyFD(t, fd1, "lax-key(1); (1)~~>(5), (1)-->(6), (1)==(2,3), (2)==(1,3), (3)==(1,2)")
   225  
   226  	// (a)==(b,d)
   227  	// (b)==(a,c)
   228  	// (c)==(b)
   229  	// (d)==(a)
   230  	// (a)~~>(e)
   231  	// (a)-->(f)
   232  	fd2 := &props.FuncDepSet{}
   233  	fd2.CopyFrom(fd1)
   234  	fd2.AddEquivalency(1, 4)
   235  	verifyFD(t, fd2, "lax-key(1); (1)~~>(5), (1)-->(6), (1)==(2-4), (2)==(1,3,4), (3)==(1,2,4), (4)==(1-3)")
   236  
   237  	// (a)==(b,d)
   238  	// (b)==(a,c)
   239  	// (c)==(b)
   240  	// (d)==(e)
   241  	// (a)~~>(e)
   242  	// (a)-->(f)
   243  	fd3 := &props.FuncDepSet{}
   244  	fd3.CopyFrom(fd1)
   245  	fd3.AddEquivalency(4, 5)
   246  	verifyFD(t, fd3, "lax-key(1); (1)~~>(5), (1)-->(6), (1)==(2,3), (2)==(1,3), (3)==(1,2), (4)==(5), (5)==(4)")
   247  
   248  	testcases := []struct {
   249  		fd       *props.FuncDepSet
   250  		expected opt.ColSet
   251  	}{
   252  		{fd: fd1, expected: c(1)},
   253  		{fd: fd2, expected: c(1)},
   254  		{fd: fd3, expected: c(1, 4)},
   255  	}
   256  
   257  	for _, tc := range testcases {
   258  		closure := tc.fd.EquivReps()
   259  		if !closure.Equals(tc.expected) {
   260  			t.Errorf("fd: %s, expected: %s, actual: %s", tc.fd, tc.expected, closure)
   261  		}
   262  	}
   263  }
   264  
   265  func TestFuncDeps_AddStrictKey(t *testing.T) {
   266  	// CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   267  	// SELECT DISTINCT ON (p) m, n, p, q FROM mnpq
   268  	mnpq := makeMnpqFD(t)
   269  	allCols := c(10, 11, 12, 13)
   270  	mnpq.AddStrictKey(c(12), allCols)
   271  	verifyFD(t, mnpq, "key(12); (10,11)-->(12,13), (12)-->(10,11,13)")
   272  	testColsAreStrictKey(t, mnpq, c(12), true)
   273  	testColsAreStrictKey(t, mnpq, c(13), false)
   274  	testColsAreStrictKey(t, mnpq, c(10, 11), true)
   275  
   276  	// SELECT DISTINCT ON (m, n, p) m, n, p, q FROM mnpq
   277  	mnpq = makeMnpqFD(t)
   278  	mnpq.AddStrictKey(c(10, 11, 12), allCols)
   279  	verifyFD(t, mnpq, "key(10,11); (10,11)-->(12,13)")
   280  	testColsAreStrictKey(t, mnpq, c(10, 11), true)
   281  	testColsAreStrictKey(t, mnpq, c(11, 12), false)
   282  
   283  	// SELECT DISTINCT ON (n, p, q) m, n, p, q FROM mnpq
   284  	mnpq = makeMnpqFD(t)
   285  	mnpq.AddStrictKey(c(11, 12, 13), allCols)
   286  	verifyFD(t, mnpq, "key(10,11); (10,11)-->(12,13), (11-13)-->(10)")
   287  	testColsAreStrictKey(t, mnpq, c(11, 12, 13), true)
   288  	testColsAreStrictKey(t, mnpq, c(11, 12), false)
   289  	testColsAreStrictKey(t, mnpq, c(10, 11), true)
   290  
   291  	// All columns together form a key.
   292  	//   CREATE TABLE ab (a INT, b INT, PRIMARY KEY (a, b))
   293  	allCols = c(1, 2)
   294  	ab := &props.FuncDepSet{}
   295  	ab.AddStrictKey(allCols, allCols)
   296  	verifyFD(t, ab, "key(1,2)")
   297  	testColsAreStrictKey(t, ab, c(1, 2), true)
   298  	testColsAreStrictKey(t, ab, c(1), false)
   299  
   300  	// Empty key.
   301  	empty := &props.FuncDepSet{}
   302  	empty.AddStrictKey(opt.ColSet{}, c(1))
   303  	verifyFD(t, empty, "key(); ()-->(1)")
   304  	testColsAreStrictKey(t, empty, c(), true)
   305  	testColsAreStrictKey(t, empty, c(1), true)
   306  }
   307  
   308  func TestFuncDeps_AddLaxKey(t *testing.T) {
   309  	// CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   310  	// CREATE UNIQUE INDEX idx ON mnpq (p)
   311  	mnpq := makeMnpqFD(t)
   312  	allCols := c(10, 11, 12, 13)
   313  	mnpq.AddLaxKey(c(12), allCols)
   314  	verifyFD(t, mnpq, "key(10,11); (10,11)-->(12,13), (12)~~>(10,11,13)")
   315  	testColsAreStrictKey(t, mnpq, c(12), false)
   316  	testColsAreLaxKey(t, mnpq, c(12), true)
   317  	testColsAreLaxKey(t, mnpq, c(10, 11), true)
   318  
   319  	// CREATE UNIQUE INDEX idx ON mnpq (m, n, p)
   320  	mnpq = makeMnpqFD(t)
   321  	mnpq.AddLaxKey(c(10, 11, 12), allCols)
   322  	verifyFD(t, mnpq, "key(10,11); (10,11)-->(12,13)")
   323  	testColsAreStrictKey(t, mnpq, c(10, 11), true)
   324  	testColsAreLaxKey(t, mnpq, c(10, 11), true)
   325  	testColsAreLaxKey(t, mnpq, c(10, 11, 12), true)
   326  
   327  	// Verify that a shorter lax key overwrites a longer lax key (but not
   328  	// vice-versa).
   329  	abcde := &props.FuncDepSet{}
   330  	abcde.AddLaxKey(c(2, 3), c(1, 2, 3, 4, 5))
   331  	verifyFD(t, abcde, "lax-key(2,3); (2,3)~~>(1,4,5)")
   332  	abcde.AddLaxKey(c(1), c(1, 2, 3, 4, 5))
   333  	verifyFD(t, abcde, "lax-key(1); (2,3)~~>(1,4,5), (1)~~>(2-5)")
   334  	abcde.AddLaxKey(c(4, 5), c(1, 2, 3, 4, 5))
   335  	verifyFD(t, abcde, "lax-key(1); (2,3)~~>(1,4,5), (1)~~>(2-5), (4,5)~~>(1-3)")
   336  }
   337  
   338  func TestFuncDeps_MakeMax1Row(t *testing.T) {
   339  	// CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   340  	// CREATE UNIQUE INDEX ON abcde (b, c)
   341  	// SELECT * FROM abcde LIMIT 1
   342  	abcde := makeAbcdeFD(t)
   343  	abcde.MakeMax1Row(c(1, 2, 3, 4, 5))
   344  	verifyFD(t, abcde, "key(); ()-->(1-5)")
   345  	testColsAreStrictKey(t, abcde, c(), true)
   346  
   347  	// No columns.
   348  	abcde = makeAbcdeFD(t)
   349  	abcde.MakeMax1Row(opt.ColSet{})
   350  	verifyFD(t, abcde, "key()")
   351  	testColsAreStrictKey(t, abcde, c(), true)
   352  }
   353  
   354  func TestFuncDeps_MakeNotNull(t *testing.T) {
   355  	// CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   356  	// CREATE UNIQUE INDEX ON abcde (b, c)
   357  	// SELECT * FROM abcde WHERE b IS NOT NULL
   358  	abcde := makeAbcdeFD(t)
   359  	abcde.MakeNotNull(c(2))
   360  	verifyFD(t, abcde, "key(1); (1)-->(2-5), (2,3)~~>(1,4,5)")
   361  
   362  	// SELECT * FROM abcde WHERE b IS NOT NULL AND c IS NOT NULL
   363  	abcde.MakeNotNull(c(2, 3))
   364  	verifyFD(t, abcde, "key(1); (1)-->(2-5), (2,3)-->(1,4,5)")
   365  
   366  	// CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   367  	// CREATE UNIQUE INDEX ON abcde (b, c)
   368  	// CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   369  	// SELECT * FROM (SELECT * FROM abcde WHERE a=1 AND b=1)
   370  	//   LEFT OUTER JOIN (SELECT * FROM mnpq WHERE m=1 AND p=1) ON True
   371  	//   WHERE p IS NOT NULL
   372  	preservedCols := c(1, 2, 3, 4, 5)
   373  	nullExtendedCols := c(10, 11, 12, 13)
   374  	loj := makeProductFD(t)
   375  	loj.AddConstants(c(1, 2, 10, 12))
   376  	verifyFD(t, loj, "key(11); ()-->(1-5,10,12), (11)-->(13)")
   377  	loj.MakeLeftOuter(abcde, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1, 2, 10, 11, 12))
   378  	verifyFD(t, loj, "key(11); ()-->(1-5), (11)-->(10,12,13)")
   379  	loj.MakeNotNull(c(1, 2, 12))
   380  	verifyFD(t, loj, "key(11); ()-->(1-5), (11)-->(10,12,13)")
   381  
   382  	// Test MakeNotNull triggering key reduction.
   383  	//   SELECT * FROM (SELECT DISTINCT b, c, d, e FROM abcde) WHERE b IS NOT NULL AND c IS NOT NULL
   384  	allCols := c(2, 3, 4, 5)
   385  	abcde = makeAbcdeFD(t)
   386  	abcde.ProjectCols(allCols)
   387  	abcde.AddStrictKey(allCols, allCols)
   388  	verifyFD(t, abcde, "key(2-5); (2,3)~~>(4,5)")
   389  	abcde.MakeNotNull(c(2, 3))
   390  	verifyFD(t, abcde, "key(2,3); (2,3)-->(4,5)")
   391  
   392  	// Test lax key to strong key conversion.
   393  	abc := &props.FuncDepSet{}
   394  	abc.AddLaxKey(c(2, 3), c(1, 2, 3))
   395  	verifyFD(t, abc, "lax-key(2,3); (2,3)~~>(1)")
   396  	abc.MakeNotNull(c(2))
   397  	verifyFD(t, abc, "lax-key(2,3); (2,3)~~>(1)")
   398  	abc.MakeNotNull(c(2, 3))
   399  	verifyFD(t, abc, "key(2,3); (2,3)-->(1)")
   400  }
   401  
   402  func TestFuncDeps_AddEquivalency(t *testing.T) {
   403  	// CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   404  	// CREATE UNIQUE INDEX ON abcde (b, c)
   405  	// CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   406  	// SELECT * FROM abcde, mnpq
   407  	product := makeProductFD(t)
   408  
   409  	// Multiple equivalencies.
   410  	//   SELECT * FROM abcde, mnpq WHERE b=m AND c=n AND d=d
   411  	var bmcn props.FuncDepSet
   412  	bmcn.CopyFrom(product)
   413  	bmcn.AddEquivalency(2, 10)
   414  	bmcn.AddEquivalency(3, 11)
   415  	bmcn.AddEquivalency(4, 4)
   416  	verifyFD(t, &bmcn, "key(1); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (2)==(10), (10)==(2), (3)==(11), (11)==(3)")
   417  	testColsAreStrictKey(t, &bmcn, c(2, 3, 4, 5, 10, 11, 12, 13), false)
   418  
   419  	// SELECT * FROM abcde, mnpq WHERE a=m AND a=n
   420  	var amn props.FuncDepSet
   421  	amn.CopyFrom(product)
   422  	amn.AddEquivalency(1, 10)
   423  	amn.AddEquivalency(1, 11)
   424  	verifyFD(t, &amn, "key(11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (1)==(10,11), (10)==(1,11), (11)==(1,10)")
   425  	testColsAreStrictKey(t, &amn, c(1), true)
   426  	testColsAreStrictKey(t, &amn, c(10), true)
   427  	testColsAreStrictKey(t, &amn, c(11), true)
   428  
   429  	// Override weaker dependencies with equivalency.
   430  	//   CREATE TABLE ab (a INT PRIMARY KEY, b INT, UNIQUE(b))
   431  	//   SELECT * FROM ab WHERE a=b
   432  	allCols := c(1, 2)
   433  	ab := &props.FuncDepSet{}
   434  	ab.AddStrictKey(c(1), allCols)
   435  	ab.AddLaxKey(c(2), allCols)
   436  	verifyFD(t, ab, "key(1); (1)-->(2), (2)~~>(1)")
   437  	ab.AddEquivalency(1, 2)
   438  	verifyFD(t, ab, "key(1); (1)==(2), (2)==(1)")
   439  	testColsAreStrictKey(t, ab, c(2), true)
   440  
   441  	// Multiple equivalencies + constant.
   442  	//   SELECT * FROM abcde, mnpq ON a=m WHERE m=n AND n=1
   443  	cnst := makeJoinFD(t)
   444  	cnst.AddEquivalency(10, 11)
   445  	cnst.AddConstants(c(11))
   446  	verifyFD(t, cnst, "key(); ()-->(1-5,10-13), (1)==(10,11), (10)==(1,11), (11)==(1,10)")
   447  }
   448  
   449  func TestFuncDeps_AddConstants(t *testing.T) {
   450  	// CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   451  	// CREATE UNIQUE INDEX ON abcde (b, c)
   452  	// SELECT * FROM abcde WHERE c>2
   453  	abcde := makeAbcdeFD(t)
   454  	abcde.AddConstants(c(2))
   455  	verifyFD(t, abcde, "key(1); ()-->(2), (1)-->(3-5), (2,3)~~>(1,4,5)")
   456  	abcde.MakeNotNull(c(2, 3))
   457  	verifyFD(t, abcde, "key(1); ()-->(2), (1)-->(3-5), (2,3)-->(1,4,5)")
   458  	testColsAreStrictKey(t, abcde, c(3), true)
   459  
   460  	// CREATE TABLE wxyz (w INT, x INT, y INT, z INT, PRIMARY KEY(w, x, y, z))
   461  	// SELECT * FROM wxyz WHERE x IS NULL AND y IS NULL
   462  	allCols := c(1, 2, 3, 4)
   463  	xyz := &props.FuncDepSet{}
   464  	xyz.AddStrictKey(allCols, allCols)
   465  	xyz.AddConstants(c(2, 3))
   466  	verifyFD(t, xyz, "key(1,4); ()-->(2,3)")
   467  	testColsAreStrictKey(t, xyz, c(2, 3), false)
   468  
   469  	// SELECT * FROM (SELECT * FROM wxyz WHERE x=1) WHERE y=2
   470  	allCols = c(1, 2, 3, 4)
   471  	xyz = &props.FuncDepSet{}
   472  	xyz.AddStrictKey(allCols, allCols)
   473  	xyz.AddConstants(c(2))
   474  	xyz.MakeNotNull(c(2))
   475  	xyz.AddConstants(c(3))
   476  	xyz.MakeNotNull(c(2, 3))
   477  	verifyFD(t, xyz, "key(1,4); ()-->(2,3)")
   478  
   479  	// SELECT * FROM (SELECT * FROM abcde WHERE b IS NOT NULL AND c IS NOT NULL) WHERE b=1
   480  	abcde = makeAbcdeFD(t)
   481  	abcde.MakeNotNull(c(2, 3))
   482  	verifyFD(t, abcde, "key(1); (1)-->(2-5), (2,3)-->(1,4,5)")
   483  	abcde.AddConstants(c(2))
   484  	verifyFD(t, abcde, "key(1); ()-->(2), (1)-->(3-5), (3)-->(1,4,5)")
   485  
   486  	// SELECT * FROM (SELECT * FROM abcde WHERE b IS NOT NULL AND c IS NOT NULL) WHERE b=1 AND c=2
   487  	abcde = makeAbcdeFD(t)
   488  	abcde.MakeNotNull(c(2, 3))
   489  	abcde.AddConstants(c(2, 3))
   490  	verifyFD(t, abcde, "key(); ()-->(1-5)")
   491  
   492  	// CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   493  	// SELECT a, m, n FROM abcde, mnpq WHERE a=m AND n IS NULL
   494  	var am props.FuncDepSet
   495  	am.CopyFrom(makeJoinFD(t))
   496  	am.AddConstants(c(11))
   497  	verifyFD(t, &am, "key(10); ()-->(11), (1)-->(2-5), (2,3)~~>(1,4,5), (10)-->(12,13), (1)==(10), (10)==(1)")
   498  	am.ProjectCols(c(1, 10, 11))
   499  	verifyFD(t, &am, "key(10); ()-->(11), (1)==(10), (10)==(1)")
   500  	testColsAreStrictKey(t, &am, c(1), true)
   501  	testColsAreStrictKey(t, &am, c(1, 10), true)
   502  
   503  	// Equivalency, with one of equivalent columns set to constant.
   504  	//   SELECT * FROM abcde, mnpq WHERE a=m AND m=5
   505  	var eqConst props.FuncDepSet
   506  	eqConst.CopyFrom(makeJoinFD(t))
   507  	eqConst.AddConstants(c(10))
   508  	eqConst.MakeNotNull(c(10))
   509  	verifyFD(t, &eqConst, "key(11); ()-->(1-5,10), (11)-->(12,13), (1)==(10), (10)==(1)")
   510  	testColsAreStrictKey(t, &eqConst, c(1, 2, 3, 10, 12), false)
   511  }
   512  
   513  // Figure, page references are from this paper:
   514  // Norman Paulley, Glenn. (2000).
   515  // Exploiting Functional Dependence in Query Optimization.
   516  // https://cs.uwaterloo.ca/research/tr/2000/11/CS-2000-11.thesis.pdf
   517  func TestFuncDeps_AddSynthesizedCol(t *testing.T) {
   518  	// CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   519  	// CREATE UNIQUE INDEX ON abcde (b, c)
   520  	abcde := makeAbcdeFD(t)
   521  
   522  	// Construct FD from figure 3.4, page 119:
   523  	//   SELECT a, b, d, e, func(b, c) AS f FROM abcde
   524  	var abdef props.FuncDepSet
   525  	abdef.CopyFrom(abcde)
   526  	abdef.AddSynthesizedCol(c(2, 3), 6)
   527  	verifyFD(t, &abdef, "key(1); (1)-->(2-5), (2,3)~~>(1,4,5), (2,3)-->(6)")
   528  	abdef.ProjectCols(c(1, 2, 4, 5, 6))
   529  	verifyFD(t, &abdef, "key(1); (1)-->(2,4-6)")
   530  
   531  	// Add another synthesized column, based on the first synthesized column.
   532  	abdef.AddSynthesizedCol(c(6), 7)
   533  	verifyFD(t, &abdef, "key(1); (1)-->(2,4-6), (6)-->(7)")
   534  	testColsAreStrictKey(t, &abdef, c(2, 3), false)
   535  
   536  	// Add a constant synthesized column, not based on any other column.
   537  	abdef.AddSynthesizedCol(opt.ColSet{}, 8)
   538  	verifyFD(t, &abdef, "key(1); ()-->(8), (1)-->(2,4-6), (6)-->(7)")
   539  	testColsAreStrictKey(t, &abdef, c(2, 3, 4, 5, 6, 7, 8), false)
   540  
   541  	// Remove columns and add computed column.
   542  	//   CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   543  	//   SELECT * FROM abcde, mnpq WHERE a=m
   544  	//   SELECT a, n, b+1 FROM abcde, mnpq WHERE a=m
   545  	var anb1 props.FuncDepSet
   546  	anb1.CopyFrom(makeJoinFD(t))
   547  	anb1.AddSynthesizedCol(c(2), 100)
   548  	verifyFD(t, &anb1, "key(10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (1)==(10), (10)==(1), (2)-->(100)")
   549  	anb1.ProjectCols(c(1, 11, 100))
   550  	verifyFD(t, &anb1, "key(1,11); (1)-->(100)")
   551  	testColsAreStrictKey(t, &anb1, c(1, 11, 100), true)
   552  }
   553  
   554  func TestFuncDeps_ProjectCols(t *testing.T) {
   555  	foo := &props.FuncDepSet{}
   556  	all := c(1, 2, 3, 4)
   557  	foo.AddStrictKey(c(1), all)
   558  	foo.AddLaxKey(c(2, 3), all)
   559  	foo.AddLaxKey(c(4), all)
   560  	verifyFD(t, foo, "key(1); (1)-->(2-4), (2,3)~~>(1,4), (4)~~>(1-3)")
   561  	foo.ProjectCols(c(2, 3, 4))
   562  	verifyFD(t, foo, "lax-key(2-4); (2,3)~~>(4), (4)~~>(2,3)")
   563  	foo.MakeNotNull(c(2, 3, 4))
   564  	verifyFD(t, foo, "key(4); (2,3)-->(4), (4)-->(2,3)")
   565  
   566  	x := makeAbcdeFD(t)
   567  	x.ProjectCols(c(2, 3))
   568  	verifyFD(t, x, "lax-key(2,3)")
   569  
   570  	x = makeAbcdeFD(t)
   571  	x.MakeNotNull(c(2, 3))
   572  	x.ProjectCols(c(2, 3))
   573  	verifyFD(t, x, "key(2,3)")
   574  
   575  	// Remove column from lax dependency.
   576  	//   CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   577  	//   CREATE UNIQUE INDEX ON abcde (b, c)
   578  	//   SELECT a, c, d, e FROM abcde
   579  	abde := makeAbcdeFD(t)
   580  	abde.ProjectCols(c(1, 3, 4, 5))
   581  	verifyFD(t, abde, "key(1); (1)-->(3-5)")
   582  
   583  	// Try removing columns that are only dependants (i.e. never determinants).
   584  	//   CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   585  	//   SELECT * FROM abcde, mnpq WHERE a=m
   586  	//   SELECT a, b, c, m, n FROM abcde, mnpq WHERE a=m
   587  	var abcmn props.FuncDepSet
   588  	abcmn.CopyFrom(makeJoinFD(t))
   589  	abcmn.ProjectCols(c(1, 2, 3, 10, 11))
   590  	verifyFD(t, &abcmn, "key(10,11); (1)-->(2,3), (2,3)~~>(1,10), (1)==(10), (10)==(1)")
   591  	testColsAreStrictKey(t, &abcmn, c(1, 11), true)
   592  	testColsAreStrictKey(t, &abcmn, c(2, 3), false)
   593  
   594  	// Remove column that is constant and part of multi-column determinant.
   595  	//   SELECT a, c, d, e FROM abcde WHERE b=1
   596  	abcde := makeAbcdeFD(t)
   597  	abcde.AddConstants(c(2))
   598  	abcde.MakeNotNull(c(2, 3))
   599  	verifyFD(t, abcde, "key(1); ()-->(2), (1)-->(3-5), (2,3)-->(1,4,5)")
   600  	abcde.ProjectCols(c(1, 3, 4, 5))
   601  	verifyFD(t, abcde, "key(1); (1)-->(3-5), (3)-->(1,4,5)")
   602  
   603  	// Remove key columns, but expect another key to be found.
   604  	//   SELECT b, c, n FROM abcde, mnpq WHERE a=m AND b IS NOT NULL AND c IS NOT NULL
   605  	switchKey := makeJoinFD(t)
   606  	switchKey.MakeNotNull(c(2, 3))
   607  	verifyFD(t, switchKey, "key(10,11); (1)-->(2-5), (2,3)-->(1,4,5), (10,11)-->(12,13), (1)==(10), (10)==(1)")
   608  	switchKey.ProjectCols(c(2, 3, 11))
   609  	verifyFD(t, switchKey, "key(2,3,11)")
   610  
   611  	// Remove column from every determinant and ensure that all FDs go away.
   612  	//   SELECT d FROM abcde, mnpq WHERE a=m AND 1=1 AND n=2
   613  	noKey := makeJoinFD(t)
   614  	verifyFD(t, noKey, "key(10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (1)==(10), (10)==(1)")
   615  	noKey.ProjectCols(c(2, 11))
   616  	verifyFD(t, noKey, "")
   617  	testColsAreStrictKey(t, noKey, c(), false)
   618  
   619  	// Remove columns so that there is no longer a key.
   620  	//   SELECT b, c, d, e, n, p, q FROM abcde, mnpq WHERE a=m
   621  	var bcden props.FuncDepSet
   622  	bcden.CopyFrom(makeJoinFD(t))
   623  	bcden.ProjectCols(c(2, 3, 4, 5, 11, 12, 13))
   624  	verifyFD(t, &bcden, "lax-key(2-5,11-13); (2,3)~~>(4,5)")
   625  	testColsAreStrictKey(t, &bcden, c(2, 3, 4, 5, 11, 12, 13), false)
   626  	testColsAreLaxKey(t, &bcden, c(2, 3, 4, 5, 11, 12, 13), true)
   627  
   628  	// Remove remainder of columns (N rows, 0 cols projected).
   629  	bcden.ProjectCols(opt.ColSet{})
   630  	verifyFD(t, &bcden, "")
   631  
   632  	// Project single column.
   633  	//   SELECT d FROM abcde, mnpq WHERE a=m AND a=1 AND n=1
   634  	oneRow := makeJoinFD(t)
   635  	oneRow.AddConstants(c(1, 11))
   636  	verifyFD(t, oneRow, "key(); ()-->(1-5,10-13), (1)==(10), (10)==(1)")
   637  	oneRow.ProjectCols(c(4))
   638  	verifyFD(t, oneRow, "key(); ()-->(4)")
   639  
   640  	// Remove column that has equivalent substitute.
   641  	//   SELECT e, one FROM (SELECT *, d+1 AS one FROM abcde) WHERE d=e
   642  	abcde = makeAbcdeFD(t)
   643  	abcde.AddSynthesizedCol(c(4), 6)
   644  	abcde.AddEquivalency(4, 5)
   645  	verifyFD(t, abcde, "key(1); (1)-->(2-5), (2,3)~~>(1,4,5), (4)-->(6), (4)==(5), (5)==(4)")
   646  	abcde.ProjectCols(c(5, 6))
   647  	verifyFD(t, abcde, "(5)-->(6)")
   648  
   649  	// Remove column that has equivalent substitute and is part of composite
   650  	// determinant.
   651  	//   SELECT d, e FROM abcde WHERE b=d AND c=e
   652  	abcde = makeAbcdeFD(t)
   653  	abcde.AddEquivalency(2, 4)
   654  	verifyFD(t, abcde, "key(1); (1)-->(2-5), (2,3)~~>(1,4,5), (2)==(4), (4)==(2)")
   655  	abcde.ProjectCols(c(3, 4, 5))
   656  	verifyFD(t, abcde, "lax-key(3-5); (3,4)~~>(5)")
   657  
   658  	// Equivalent substitution results in (4,5)~~>(4,5), which is eliminated.
   659  	//   SELECT d, e FROM abcde WHERE b=d AND c=e
   660  	abcde = makeAbcdeFD(t)
   661  	abcde.AddEquivalency(2, 4)
   662  	abcde.AddEquivalency(3, 5)
   663  	verifyFD(t, abcde, "key(1); (1)-->(2-5), (2,3)~~>(1,4,5), (2)==(4), (4)==(2), (3)==(5), (5)==(3)")
   664  	abcde.ProjectCols(c(4, 5))
   665  	verifyFD(t, abcde, "lax-key(4,5)")
   666  
   667  	// Use ProjectCols to add columns (make sure key is extended).
   668  	//   SELECT d, e FROM abcde WHERE b=d AND c=e
   669  	abcde = makeAbcdeFD(t)
   670  	abcde.ProjectCols(c(1, 2, 3, 4, 5, 6, 7))
   671  	verifyFD(t, abcde, "key(1); (1)-->(2-7), (2,3)~~>(1,4,5)")
   672  
   673  	// Verify lax keys are retained (and can later become keys) when the key is
   674  	// projected away.
   675  	abcde = &props.FuncDepSet{}
   676  	abcde.AddStrictKey(c(1), c(1, 2, 3, 4, 5))
   677  	abcde.AddLaxKey(c(2), c(1, 2, 3, 4, 5))
   678  	abcde.AddLaxKey(c(3, 4), c(1, 2, 3, 4, 5))
   679  	verifyFD(t, abcde, "key(1); (1)-->(2-5), (2)~~>(1,3-5), (3,4)~~>(1,2,5)")
   680  	abcde.ProjectCols(c(2, 3, 4, 5))
   681  	verifyFD(t, abcde, "lax-key(2-5); (2)~~>(3-5), (3,4)~~>(2,5)")
   682  	// 2 on its own is not necessarily a lax key: even if it determines the other
   683  	// columns, any of them can still be NULL.
   684  	testColsAreLaxKey(t, abcde, c(2), false)
   685  	testColsAreLaxKey(t, abcde, c(3, 4), false)
   686  
   687  	copy := &props.FuncDepSet{}
   688  	copy.CopyFrom(abcde)
   689  
   690  	// Verify that lax keys convert to strong keys.
   691  	abcde.MakeNotNull(c(2, 3, 4, 5))
   692  	verifyFD(t, abcde, "key(3,4); (2)-->(3-5), (3,4)-->(2,5)")
   693  }
   694  
   695  func TestFuncDeps_AddFrom(t *testing.T) {
   696  	// Remove lax dependency, then add it back.
   697  	//   CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   698  	//   CREATE UNIQUE INDEX ON abcde (b, c)
   699  	abcde := makeAbcdeFD(t)
   700  	abcde.ProjectCols(c(1, 2, 4))
   701  	verifyFD(t, abcde, "key(1); (1)-->(2,4)")
   702  	abcde.AddFrom(makeAbcdeFD(t))
   703  	abcde.AddStrictKey(c(1), c(1, 2, 3, 4, 5))
   704  	verifyFD(t, abcde, "key(1); (1)-->(2-5), (2,3)~~>(1,4,5)")
   705  	testColsAreStrictKey(t, abcde, c(1), true)
   706  
   707  	// Remove strict dependency, then add it back.
   708  	abcde = makeAbcdeFD(t)
   709  	abcde.MakeNotNull(c(2, 3))
   710  	abcde.ProjectCols(c(2, 3))
   711  	verifyFD(t, abcde, "key(2,3)")
   712  	abcde.AddFrom(makeAbcdeFD(t))
   713  	abcde.AddStrictKey(c(2, 3), c(1, 2, 3, 4, 5))
   714  	verifyFD(t, abcde, "key(2,3); (1)-->(2-5), (2,3)-->(1,4,5)")
   715  	testColsAreStrictKey(t, abcde, c(1), true)
   716  }
   717  
   718  func TestFuncDeps_AddEquivFrom(t *testing.T) {
   719  	// CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   720  	// CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   721  	// SELECT * FROM abcde, mnpq WHERE a=m AND b=n
   722  	product := makeAbcdeFD(t)
   723  	mnpq := makeMnpqFD(t)
   724  	product.MakeProduct(mnpq)
   725  	product.AddEquivalency(1, 10)
   726  	verifyFD(t, product, "key(10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (1)==(10), (10)==(1)")
   727  
   728  	var equiv props.FuncDepSet
   729  	equiv.AddEquivFrom(product)
   730  	verifyFD(t, &equiv, "(1)==(10), (10)==(1)")
   731  
   732  	product.AddEquivalency(2, 11)
   733  	equiv.ProjectCols(opt.ColSet{})
   734  	equiv.AddEquivFrom(product)
   735  	verifyFD(t, &equiv, "(1)==(10), (10)==(1), (2)==(11), (11)==(2)")
   736  }
   737  
   738  func TestFuncDeps_MakeProduct(t *testing.T) {
   739  	// Union dependencies and removed columns and keys:
   740  	//   CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   741  	//   CREATE UNIQUE INDEX ON abcde (b, c)
   742  	//   CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   743  	//   SELECT * FROM (SELECT a, b, c FROM abcde WHERE d=e), (SELECT m, n FROM mnpq WHERE p=q)
   744  	product := makeAbcdeFD(t)
   745  	product.AddEquivalency(4, 5)
   746  	product.ProjectCols(c(1, 2, 3))
   747  	mnpq := makeMnpqFD(t)
   748  	mnpq.AddEquivalency(12, 13)
   749  	mnpq.ProjectCols(c(10, 11))
   750  	product.MakeProduct(mnpq)
   751  	verifyFD(t, product, "key(1,10,11); (1)-->(2,3), (2,3)~~>(1)")
   752  
   753  	// Constants on both sides.
   754  	//   SELECT * FROM (SELECT * FROM abcde b=1), (SELECT * FROM mnpq WHERE p=1)
   755  	product = makeAbcdeFD(t)
   756  	product.AddConstants(c(2))
   757  	mnpq = makeMnpqFD(t)
   758  	mnpq.AddConstants(c(12))
   759  	product.MakeProduct(mnpq)
   760  	verifyFD(t, product, "key(1,10,11); ()-->(2,12), (1)-->(3-5), (2,3)~~>(1,4,5), (10,11)-->(13)")
   761  
   762  	// Strict key on left side, no key on right side:
   763  	//   SELECT * FROM abcde, (SELECT p, q FROM mnpq)
   764  	product = makeAbcdeFD(t)
   765  	mnpq = makeMnpqFD(t)
   766  	mnpq.ProjectCols(c(12, 13))
   767  	product.MakeProduct(mnpq)
   768  	verifyFD(t, product, "(1)-->(2-5), (2,3)~~>(1,4,5)")
   769  	testColsAreStrictKey(t, product, c(1, 2, 3, 4, 5, 12, 13), false)
   770  	testColsAreLaxKey(t, product, c(1, 2, 3, 4, 5, 12, 13), false)
   771  
   772  	// No key on left side, Strict key on right side.
   773  	//   SELECT * FROM (SELECT d, e FROM abcde), mnpq
   774  	product = makeAbcdeFD(t)
   775  	product.ProjectCols(c(4, 5))
   776  	product.MakeProduct(makeMnpqFD(t))
   777  	verifyFD(t, product, "(10,11)-->(12,13)")
   778  	testColsAreStrictKey(t, product, c(4, 5, 10, 11, 12, 13), false)
   779  	testColsAreLaxKey(t, product, c(1, 2, 3, 4, 5, 12, 13), false)
   780  
   781  	// Strict key on left side, lax key on right side:
   782  	//   CREATE UNIQUE INDEX ON mnpq (p)
   783  	//   SELECT * FROM abcde, (SELECT p, q FROM mnpq)
   784  	product = makeAbcdeFD(t)
   785  	mnpq = makeMnpqFD(t)
   786  	mnpq.AddLaxKey(c(12), c(10, 11, 12, 13))
   787  	mnpq.ProjectCols(c(12, 13))
   788  	product.MakeProduct(mnpq)
   789  	verifyFD(t, product, "lax-key(1,12,13); (1)-->(2-5), (2,3)~~>(1,4,5), (12)~~>(13)")
   790  	testColsAreStrictKey(t, product, c(1, 2, 3, 4, 5, 12, 13), false)
   791  	testColsAreLaxKey(t, product, c(1, 12, 13), true)
   792  
   793  	// Lax key on left side, strict key on right side:
   794  	//   SELECT * FROM (SELECT b, c, d, e FROM abcde), mnpq
   795  	product = makeAbcdeFD(t)
   796  	product.ProjectCols(c(2, 3, 4, 5))
   797  	mnpq = makeMnpqFD(t)
   798  	product.MakeProduct(mnpq)
   799  	verifyFD(t, product, "lax-key(2-5,10,11); (2,3)~~>(4,5), (10,11)-->(12,13)")
   800  	testColsAreStrictKey(t, product, c(1, 2, 3, 4, 5, 10, 11, 12, 13), false)
   801  	testColsAreLaxKey(t, product, c(2, 3, 4, 5, 10, 11), true)
   802  
   803  	// Lax key on left side, lax key on right side:
   804  	//   CREATE UNIQUE INDEX ON mnpq (p)
   805  	//   SELECT * FROM (SELECT b, c, d, e FROM abcde), (SELECT p, q FROM mnpq)
   806  	product = makeAbcdeFD(t)
   807  	product.ProjectCols(c(2, 3, 4, 5))
   808  	mnpq = makeMnpqFD(t)
   809  	mnpq.AddLaxKey(c(12), c(10, 11, 12, 13))
   810  	mnpq.ProjectCols(c(12, 13))
   811  	product.MakeProduct(mnpq)
   812  	verifyFD(t, product, "lax-key(2-5,12,13); (2,3)~~>(4,5), (12)~~>(13)")
   813  	testColsAreStrictKey(t, product, c(2, 3, 4, 5, 12, 13), false)
   814  
   815  	// Lax key on left side, no key on right side:
   816  	//   SELECT * FROM (SELECT b, c, d, e FROM abcde), (SELECT p, q FROM mnpq)
   817  	product = makeAbcdeFD(t)
   818  	product.ProjectCols(c(2, 3, 4, 5))
   819  	mnpq = makeMnpqFD(t)
   820  	mnpq.ProjectCols(c(12, 13))
   821  	product.MakeProduct(mnpq)
   822  	verifyFD(t, product, "(2,3)~~>(4,5)")
   823  	testColsAreStrictKey(t, product, c(2, 3, 4, 5, 12, 13), false)
   824  	testColsAreLaxKey(t, product, c(2, 3, 4, 5, 12, 13), false)
   825  
   826  	// No key on left side, lax key on right side:
   827  	//   CREATE UNIQUE INDEX ON mnpq (p)
   828  	//   SELECT * FROM (SELECT d, e FROM abcde), (SELECT p, q FROM mnpq)
   829  	product = makeAbcdeFD(t)
   830  	product.ProjectCols(c(4, 5))
   831  	mnpq = makeMnpqFD(t)
   832  	mnpq.AddLaxKey(c(12), c(10, 11, 12, 13))
   833  	mnpq.ProjectCols(c(12, 13))
   834  	product.MakeProduct(mnpq)
   835  	verifyFD(t, product, "(12)~~>(13)")
   836  	testColsAreStrictKey(t, product, c(4, 5, 12, 13), false)
   837  	testColsAreLaxKey(t, product, c(4, 5, 12, 13), false)
   838  }
   839  
   840  func TestFuncDeps_MakeApply(t *testing.T) {
   841  	// CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   842  	// CREATE UNIQUE INDEX ON abcde (b, c)
   843  	// CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   844  	//   SELECT *
   845  	//   FROM abcde
   846  	//   INNER JOIN LATERAL (SELECT * FROM mnpq WHERE m=a LIMIT 1)
   847  	//   ON True
   848  	abcde := makeAbcdeFD(t)
   849  	mnpq := makeMnpqFD(t)
   850  	mnpq.MakeMax1Row(c(10, 11, 12, 13))
   851  	verifyFD(t, mnpq, "key(); ()-->(10-13)")
   852  	abcde.MakeApply(mnpq)
   853  	verifyFD(t, abcde, "key(1); (1)-->(2-5,10-13), (2,3)~~>(1,4,5)")
   854  
   855  	// SELECT *
   856  	// FROM abcde
   857  	// INNER JOIN LATERAL (SELECT * FROM mnpq WHERE m=a AND p=1)
   858  	// ON True
   859  	abcde = makeAbcdeFD(t)
   860  	mnpq = makeMnpqFD(t)
   861  	mnpq.AddConstants(c(10, 12))
   862  	verifyFD(t, mnpq, "key(11); ()-->(10,12), (11)-->(13)")
   863  	abcde.MakeApply(mnpq)
   864  	verifyFD(t, abcde, "key(1,11); (1)-->(2-5), (2,3)~~>(1,4,5), (1,11)-->(10,12,13)")
   865  
   866  	// SELECT *
   867  	// FROM abcde
   868  	// INNER JOIN LATERAL (SELECT * FROM mnpq WHERE m=a AND p=q)
   869  	// ON True
   870  	abcde = makeAbcdeFD(t)
   871  	mnpq = makeMnpqFD(t)
   872  	mnpq.AddConstants(c(10))
   873  	mnpq.AddEquivalency(12, 13)
   874  	verifyFD(t, mnpq, "key(11); ()-->(10), (11)-->(12,13), (12)==(13), (13)==(12)")
   875  	abcde.MakeApply(mnpq)
   876  	verifyFD(t, abcde, "key(1,11); (1)-->(2-5), (2,3)~~>(1,4,5), (1,11)-->(10,12,13), (12)==(13), (13)==(12)")
   877  
   878  	// No key in outer relation.
   879  	//   SELECT *
   880  	//   FROM (SELECT b, c, d, e FROM abcde)
   881  	//   INNER JOIN LATERAL (SELECT * FROM mnpq WHERE p=q AND n=1)
   882  	//   ON True
   883  	abcde = makeAbcdeFD(t)
   884  	abcde.ProjectCols(c(2, 3, 4, 5))
   885  	mnpq = makeMnpqFD(t)
   886  	mnpq.AddConstants(c(11))
   887  	mnpq.AddEquivalency(12, 13)
   888  	verifyFD(t, mnpq, "key(10); ()-->(11), (10)-->(12,13), (12)==(13), (13)==(12)")
   889  	abcde.MakeApply(mnpq)
   890  	verifyFD(t, abcde, "(2,3)~~>(4,5), (12)==(13), (13)==(12)")
   891  
   892  	// No key in inner relation.
   893  	//   SELECT *
   894  	//   FROM abcde
   895  	//   INNER JOIN LATERAL (SELECT n, p, q FROM mnpq WHERE n=a AND p=1)
   896  	//   ON True
   897  	abcde = makeAbcdeFD(t)
   898  	mnpq = makeMnpqFD(t)
   899  	mnpq.AddConstants(c(11, 12))
   900  	mnpq.ProjectCols(c(11, 12, 13))
   901  	verifyFD(t, mnpq, "()-->(11,12)")
   902  	abcde.MakeApply(mnpq)
   903  	verifyFD(t, abcde, "(1)-->(2-5), (2,3)~~>(1,4,5)")
   904  }
   905  
   906  func TestFuncDeps_MakeLeftOuter(t *testing.T) {
   907  	// All determinant columns in null-extended side are nullable.
   908  	//   CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
   909  	//   CREATE UNIQUE INDEX ON abcde (b, c)
   910  	//   CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
   911  	//   SELECT * FROM abcde LEFT OUTER JOIN (SELECT *, p+q FROM mnpq) ON True
   912  	var loj props.FuncDepSet
   913  	preservedCols := c(1, 2, 3, 4, 5)
   914  	nullExtendedCols := c(10, 11, 12, 13, 14)
   915  	abcde := makeAbcdeFD(t)
   916  	mnpq := makeMnpqFD(t)
   917  	mnpq.AddSynthesizedCol(c(12, 13), 14)
   918  	loj.CopyFrom(abcde)
   919  	loj.MakeProduct(mnpq)
   920  	verifyFD(t, &loj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (12,13)-->(14)")
   921  	loj.MakeLeftOuter(abcde, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1, 10, 11))
   922  	verifyFD(t, &loj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (12,13)~~>(14), (1,10,11)-->(14)")
   923  
   924  	// One determinant column in null-extended side is not null.
   925  	//   SELECT * FROM abcde LEFT OUTER JOIN (SELECT *, m+q FROM mnpq) ON True
   926  	preservedCols = c(1, 2, 3, 4, 5)
   927  	nullExtendedCols = c(10, 11, 12, 13, 14)
   928  	abcde = makeAbcdeFD(t)
   929  	mnpq = makeMnpqFD(t)
   930  	mnpq.AddSynthesizedCol(c(10, 13), 14)
   931  	loj.CopyFrom(abcde)
   932  	loj.MakeProduct(mnpq)
   933  	verifyFD(t, &loj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (10,13)-->(14)")
   934  	loj.MakeLeftOuter(abcde, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1, 10, 11))
   935  	verifyFD(t, &loj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (10,13)-->(14)")
   936  
   937  	// Inputs have constant columns. Constant columns on the row-supplying side
   938  	// stay constant, while constant columns on the null-supplying side are not
   939  	// constant after null-extension.
   940  	var roj props.FuncDepSet
   941  	preservedCols = c(10, 11, 12, 13)
   942  	nullExtendedCols = c(1, 2, 3, 4, 5)
   943  	abcde = makeAbcdeFD(t)
   944  	roj.CopyFrom(abcde)
   945  	roj.MakeProduct(makeMnpqFD(t))
   946  	roj.AddConstants(c(2, 3, 12))
   947  	roj.MakeNotNull(c(2, 3, 12))
   948  	verifyFD(t, &roj, "key(10,11); ()-->(2,3,12), (1)-->(4,5), (2,3)-->(1,4,5), (10,11)-->(13)")
   949  	roj.MakeLeftOuter(mnpq, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1, 2, 3, 10, 11, 12))
   950  	verifyFD(t, &roj, "key(10,11); ()-->(12), (1)-->(4,5), (2,3)-->(1,4,5), (10,11)-->(1-5,13)")
   951  
   952  	// Add constants on both sides of outer join. None of the resulting columns
   953  	// are constant, because rows are added back after filtering on the
   954  	// row-supplying side, and the null-supplying side is null-extended.
   955  	//   SELECT * FROM abcde RIGHT OUTER JOIN mnpq ON b=1 AND c=1 AND p=1
   956  	filters := props.FuncDepSet{}
   957  	preservedCols = c(10, 11, 12, 13)
   958  	nullExtendedCols = c(1, 2, 3, 4, 5)
   959  	abcde = makeAbcdeFD(t)
   960  	roj.CopyFrom(abcde)
   961  	roj.MakeProduct(makeMnpqFD(t))
   962  	filters.AddConstants(c(2, 3, 12))
   963  	filters.MakeNotNull(c(2, 3, 12))
   964  	verifyFD(t, &roj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13)")
   965  	roj.MakeLeftOuter(mnpq, &filters, preservedCols, nullExtendedCols, c(1, 2, 3, 10, 11, 12))
   966  	verifyFD(t, &roj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13)")
   967  
   968  	// Test equivalency on both sides of outer join.
   969  	preservedCols = c(10, 11, 12, 13)
   970  	nullExtendedCols = c(1, 2, 3, 4, 5)
   971  	abcde = makeAbcdeFD(t)
   972  	roj.CopyFrom(abcde)
   973  	roj.MakeProduct(makeMnpqFD(t))
   974  	roj.AddEquivalency(2, 3)
   975  	roj.AddEquivalency(3, 4)
   976  	roj.AddEquivalency(10, 12)
   977  	roj.AddEquivalency(10, 13)
   978  	verifyFD(t, &roj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,5), (10,11)-->(12,13), (2)==(3,4), (3)==(2,4), (4)==(2,3), (10)==(12,13), (12)==(10,13), (13)==(10,12)")
   979  	roj.MakeLeftOuter(mnpq, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1, 2, 3, 10, 11, 13))
   980  	verifyFD(t, &roj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,5), (10,11)-->(12,13), (2)==(3,4), (3)==(2,4), (4)==(2,3), (10)==(12,13), (12)==(10,13), (13)==(10,12)")
   981  
   982  	// Test equivalencies on both sides of outer join in filters.
   983  	//   SELECT * FROM abcde RIGHT OUTER JOIN mnpq ON b=c AND c=d AND m=p AND m=q
   984  	filters = props.FuncDepSet{}
   985  	preservedCols = c(10, 11, 12, 13)
   986  	nullExtendedCols = c(1, 2, 3, 4, 5)
   987  	abcde = makeAbcdeFD(t)
   988  	roj.CopyFrom(abcde)
   989  	roj.MakeProduct(makeMnpqFD(t))
   990  	filters.AddEquivalency(2, 3)
   991  	filters.AddEquivalency(3, 4)
   992  	filters.AddEquivalency(10, 12)
   993  	filters.AddEquivalency(10, 13)
   994  	verifyFD(t, &roj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13)")
   995  	roj.MakeLeftOuter(mnpq, &filters, preservedCols, nullExtendedCols, c(1, 2, 3, 10, 11, 13))
   996  	verifyFD(t, &roj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13)")
   997  
   998  	// Test equivalency that crosses join boundary.
   999  	preservedCols = c(10, 11, 12, 13)
  1000  	nullExtendedCols = c(1, 2, 3, 4, 5)
  1001  	abcde = makeAbcdeFD(t)
  1002  	roj.CopyFrom(abcde)
  1003  	roj.MakeProduct(makeMnpqFD(t))
  1004  	roj.AddEquivalency(1, 10)
  1005  	verifyFD(t, &roj, "key(10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (1)==(10), (10)==(1)")
  1006  	roj.MakeLeftOuter(mnpq, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1, 10, 11))
  1007  	verifyFD(t, &roj, "key(10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(1-5,12,13), (1)~~>(10)")
  1008  
  1009  	// Test filter equivalency that crosses join boundary.
  1010  	//   SELECT * FROM abcde RIGHT OUTER JOIN mnpq ON a=m
  1011  	filters = props.FuncDepSet{}
  1012  	preservedCols = c(10, 11, 12, 13)
  1013  	nullExtendedCols = c(1, 2, 3, 4, 5)
  1014  	abcde = makeAbcdeFD(t)
  1015  	roj.CopyFrom(abcde)
  1016  	roj.MakeProduct(makeMnpqFD(t))
  1017  	filters.AddEquivalency(1, 10)
  1018  	verifyFD(t, &roj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13)")
  1019  	roj.MakeLeftOuter(mnpq, &filters, preservedCols, nullExtendedCols, c(1, 10, 11))
  1020  	verifyFD(t, &roj, "key(10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(1-5,12,13)")
  1021  
  1022  	// Test equivalency that includes columns from both sides of join boundary.
  1023  	preservedCols = c(10, 11, 12, 13)
  1024  	nullExtendedCols = c(1, 2, 3, 4, 5)
  1025  	abcde = makeAbcdeFD(t)
  1026  	roj.CopyFrom(abcde)
  1027  	roj.MakeProduct(makeMnpqFD(t))
  1028  	roj.AddEquivalency(1, 10)
  1029  	roj.AddEquivalency(1, 2)
  1030  	verifyFD(t, &roj, "key(10,11); (1)-->(3-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (1)==(2,10), (10)==(1,2), (2)==(1,10)")
  1031  	roj.MakeLeftOuter(mnpq, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1, 2, 10, 11))
  1032  	verifyFD(t, &roj, "key(10,11); (1)-->(3-5), (2,3)~~>(1,4,5), (10,11)-->(1-5,12,13), (1)==(2), (2)==(1), (1)~~>(10), (2)~~>(10)")
  1033  
  1034  	// Test filter equivalency that includes columns from both sides of join
  1035  	// boundary.
  1036  	//   SELECT * FROM abcde RIGHT OUTER JOIN mnpq ON a=m AND a=b
  1037  	filters = props.FuncDepSet{}
  1038  	preservedCols = c(10, 11, 12, 13)
  1039  	nullExtendedCols = c(1, 2, 3, 4, 5)
  1040  	abcde = makeAbcdeFD(t)
  1041  	roj.CopyFrom(abcde)
  1042  	roj.MakeProduct(makeMnpqFD(t))
  1043  	filters.AddEquivalency(1, 10)
  1044  	filters.AddEquivalency(1, 2)
  1045  	verifyFD(t, &roj, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13)")
  1046  	roj.MakeLeftOuter(mnpq, &filters, preservedCols, nullExtendedCols, c(1, 2, 10, 11))
  1047  	verifyFD(t, &roj, "key(10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(1-5,12,13)")
  1048  
  1049  	// Test multiple calls to MakeLeftOuter, where the first creates determinant with
  1050  	// columns from both sides of join.
  1051  	//   SELECT * FROM (SELECT * FROM abcde WHERE b=1) FULL JOIN mnpq ON True
  1052  	preservedCols = c(10, 11, 12, 13)
  1053  	preservedCols2 := c(1, 2, 3, 4, 5)
  1054  	nullExtendedCols = c(1, 2, 3, 4, 5)
  1055  	nullExtendedCols2 := c(10, 11, 12, 13)
  1056  	abcde = makeAbcdeFD(t)
  1057  	roj.CopyFrom(abcde)
  1058  	roj.AddConstants(c(2))
  1059  	roj.MakeProduct(makeMnpqFD(t))
  1060  	verifyFD(t, &roj, "key(1,10,11); ()-->(2), (1)-->(3-5), (2,3)~~>(1,4,5), (10,11)-->(12,13)")
  1061  	roj.MakeLeftOuter(mnpq, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1, 2, 10, 11))
  1062  	verifyFD(t, &roj, "key(1,10,11); (1)-->(3-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (1,10,11)-->(2)")
  1063  	roj.MakeLeftOuter(abcde, &props.FuncDepSet{}, preservedCols2, nullExtendedCols2, c(1, 2, 10, 11))
  1064  	verifyFD(t, &roj, "key(1,10,11); (1)-->(3-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (1,10,11)-->(2)")
  1065  
  1066  	// Join keyless relations with nullable columns.
  1067  	//   SELECT * FROM (SELECT d, e, d+e FROM abcde) LEFT JOIN (SELECT p, q, p+q FROM mnpq) ON True
  1068  	preservedCols = c(4, 5, 6)
  1069  	nullExtendedCols = c(12, 13, 14)
  1070  	abcde = makeAbcdeFD(t)
  1071  	mnpq = makeMnpqFD(t)
  1072  	mnpq.AddSynthesizedCol(c(12, 13), 14)
  1073  	mnpq.ProjectCols(c(12, 13, 14))
  1074  	loj.CopyFrom(abcde)
  1075  	loj.AddSynthesizedCol(c(4, 5), 6)
  1076  	loj.ProjectCols(c(4, 5, 6))
  1077  	loj.MakeProduct(mnpq)
  1078  	verifyFD(t, &loj, "(4,5)-->(6), (12,13)-->(14)")
  1079  	loj.MakeLeftOuter(abcde, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c())
  1080  	verifyFD(t, &loj, "(4,5)-->(6), (12,13)~~>(14)")
  1081  	testColsAreStrictKey(t, &loj, c(4, 5, 6, 12, 13, 14), false)
  1082  
  1083  	// Join keyless relations with not-null columns.
  1084  	//   SELECT * FROM (SELECT d, e, d+e FROM abcde WHERE d>e) LEFT JOIN (SELECT p, q, p+q FROM mnpq WHERE p>q) ON True
  1085  	preservedCols = c(4, 5, 6)
  1086  	nullExtendedCols = c(12, 13, 14)
  1087  	abcde = makeAbcdeFD(t)
  1088  	mnpq = makeMnpqFD(t)
  1089  	mnpq.AddSynthesizedCol(c(12, 13), 14)
  1090  	mnpq.ProjectCols(c(12, 13, 14))
  1091  	loj.CopyFrom(abcde)
  1092  	loj.AddSynthesizedCol(c(4, 5), 6)
  1093  	loj.ProjectCols(c(4, 5, 6))
  1094  	loj.MakeProduct(mnpq)
  1095  	verifyFD(t, &loj, "(4,5)-->(6), (12,13)-->(14)")
  1096  	loj.MakeLeftOuter(abcde, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(4, 5, 12, 13))
  1097  	verifyFD(t, &loj, "(4,5)-->(6), (12,13)-->(14)")
  1098  	testColsAreStrictKey(t, &loj, c(4, 5, 6, 12, 13, 14), false)
  1099  
  1100  	// SELECT * FROM abcde LEFT JOIN LATERAL (SELECT p, q, p+q FROM mnpq) ON True
  1101  	preservedCols = c(1, 2, 3, 4, 5)
  1102  	nullExtendedCols = c(12, 13, 14)
  1103  	abcde = makeAbcdeFD(t)
  1104  	mnpq = makeMnpqFD(t)
  1105  	mnpq.AddSynthesizedCol(c(12, 13), 14)
  1106  	mnpq.ProjectCols(c(12, 13, 14))
  1107  	loj.CopyFrom(abcde)
  1108  	verifyFD(t, mnpq, "(12,13)-->(14)")
  1109  	loj.MakeApply(mnpq)
  1110  	verifyFD(t, &loj, "(1)-->(2-5), (2,3)~~>(1,4,5), (1,12,13)-->(14)")
  1111  	loj.MakeLeftOuter(abcde, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1))
  1112  	verifyFD(t, &loj, "(1)-->(2-5), (2,3)~~>(1,4,5)")
  1113  }
  1114  
  1115  func TestFuncDeps_MakeFullOuter(t *testing.T) {
  1116  	mk := func(left, right *props.FuncDepSet, notNullInputCols opt.ColSet) *props.FuncDepSet {
  1117  		var outer props.FuncDepSet
  1118  		outer.CopyFrom(left)
  1119  		outer.MakeProduct(right)
  1120  		outer.MakeFullOuter(left.ColSet(), right.ColSet(), notNullInputCols)
  1121  		return &outer
  1122  	}
  1123  
  1124  	abcde := makeAbcdeFD(t)
  1125  	mnpq := makeMnpqFD(t)
  1126  
  1127  	outer := mk(abcde, mnpq, c(1, 10, 11))
  1128  	verifyFD(t, outer, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13)")
  1129  
  1130  	// With partial null column info, some FDs become lax.
  1131  	outer = mk(abcde, mnpq, c(1))
  1132  	verifyFD(t, outer, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)~~>(12,13), (1,10,11)-->(12,13)")
  1133  
  1134  	// Without null column info, the key becomes lax.
  1135  	outer = mk(abcde, mnpq, c())
  1136  	verifyFD(t, outer, "lax-key(1,10,11); (2,3)~~>(1,4,5), (1)~~>(2-5), (10,11)~~>(12,13), (1,10,11)~~>(2-5,12,13)")
  1137  
  1138  	// Test case with empty key on both sides; the result should not have a key.
  1139  	abcde.MakeMax1Row(abcde.ColSet())
  1140  	mnpq.MakeMax1Row(mnpq.ColSet())
  1141  	outer = mk(abcde, mnpq, c())
  1142  	verifyFD(t, outer, "")
  1143  }
  1144  
  1145  // Construct base table FD from figure 3.3, page 114:
  1146  //   CREATE TABLE abcde (a INT PRIMARY KEY, b INT, c INT, d INT, e INT)
  1147  //   CREATE UNIQUE INDEX ON abcde (b, c)
  1148  func makeAbcdeFD(t *testing.T) *props.FuncDepSet {
  1149  	// Set Key to all cols to start, and ensure it's overridden in AddStrictKey.
  1150  	allCols := c(1, 2, 3, 4, 5)
  1151  	abcde := &props.FuncDepSet{}
  1152  	abcde.AddStrictKey(c(1), allCols)
  1153  	verifyFD(t, abcde, "key(1); (1)-->(2-5)")
  1154  	abcde.AddLaxKey(c(2, 3), allCols)
  1155  	verifyFD(t, abcde, "key(1); (1)-->(2-5), (2,3)~~>(1,4,5)")
  1156  	testColsAreStrictKey(t, abcde, c(1), true)
  1157  	testColsAreStrictKey(t, abcde, c(2, 3), false)
  1158  	testColsAreStrictKey(t, abcde, c(1, 2), true)
  1159  	testColsAreStrictKey(t, abcde, c(1, 2, 3, 4, 5), true)
  1160  	testColsAreStrictKey(t, abcde, c(4, 5), false)
  1161  	testColsAreLaxKey(t, abcde, c(2, 3), true)
  1162  	return abcde
  1163  }
  1164  
  1165  // Construct base table FD from figure 3.3, page 114:
  1166  //   CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
  1167  func makeMnpqFD(t *testing.T) *props.FuncDepSet {
  1168  	allCols := c(10, 11, 12, 13)
  1169  	mnpq := &props.FuncDepSet{}
  1170  	mnpq.AddStrictKey(c(10, 11), allCols)
  1171  	mnpq.MakeNotNull(c(10, 11))
  1172  	verifyFD(t, mnpq, "key(10,11); (10,11)-->(12,13)")
  1173  	testColsAreStrictKey(t, mnpq, c(10), false)
  1174  	testColsAreStrictKey(t, mnpq, c(10, 11), true)
  1175  	testColsAreStrictKey(t, mnpq, c(10, 11, 12), true)
  1176  	return mnpq
  1177  }
  1178  
  1179  // Construct cartesian product FD from figure 3.6, page 122:
  1180  //   CREATE TABLE mnpq (m INT, n INT, p INT, q INT, PRIMARY KEY (m, n))
  1181  //   SELECT * FROM abcde, mnpq
  1182  func makeProductFD(t *testing.T) *props.FuncDepSet {
  1183  	product := makeAbcdeFD(t)
  1184  	product.MakeProduct(makeMnpqFD(t))
  1185  	verifyFD(t, product, "key(1,10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13)")
  1186  	testColsAreStrictKey(t, product, c(1), false)
  1187  	testColsAreStrictKey(t, product, c(10, 11), false)
  1188  	testColsAreStrictKey(t, product, c(1, 10, 11), true)
  1189  	testColsAreStrictKey(t, product, c(1, 2, 3, 10, 11, 12), true)
  1190  	testColsAreStrictKey(t, product, c(2, 3, 10, 11), false)
  1191  	return product
  1192  }
  1193  
  1194  // Construct inner join FD:
  1195  //   SELECT * FROM abcde, mnpq WHERE a=m
  1196  func makeJoinFD(t *testing.T) *props.FuncDepSet {
  1197  	// Start with cartesian product FD and add equivalency to it.
  1198  	join := makeProductFD(t)
  1199  	join.AddEquivalency(1, 10)
  1200  	verifyFD(t, join, "key(10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (1)==(10), (10)==(1)")
  1201  	join.ProjectCols(c(1, 2, 3, 4, 5, 10, 11, 12, 13))
  1202  	verifyFD(t, join, "key(10,11); (1)-->(2-5), (2,3)~~>(1,4,5), (10,11)-->(12,13), (1)==(10), (10)==(1)")
  1203  	testColsAreStrictKey(t, join, c(1, 11), true)
  1204  	testColsAreStrictKey(t, join, c(1, 10), false)
  1205  	testColsAreStrictKey(t, join, c(1, 10, 11), true)
  1206  	testColsAreStrictKey(t, join, c(1), false)
  1207  	testColsAreStrictKey(t, join, c(10, 11), true)
  1208  	testColsAreStrictKey(t, join, c(2, 3, 11), false)
  1209  	testColsAreLaxKey(t, join, c(2, 3, 11), true)
  1210  	return join
  1211  }
  1212  
  1213  func verifyFD(t *testing.T, f *props.FuncDepSet, expected string) {
  1214  	t.Helper()
  1215  	actual := f.String()
  1216  	if actual != expected {
  1217  		t.Errorf("\nexpected: %s\nactual  : %s", expected, actual)
  1218  	}
  1219  
  1220  	f.Verify()
  1221  
  1222  	if key, ok := f.StrictKey(); ok {
  1223  		testColsAreStrictKey(t, f, key, true)
  1224  		if !key.Empty() {
  1225  			testColsAreStrictKey(t, f, opt.ColSet{}, false)
  1226  		}
  1227  		closure := f.ComputeClosure(key)
  1228  		testColsAreStrictKey(t, f, closure, true)
  1229  	} else if key, ok := f.LaxKey(); ok {
  1230  		testColsAreLaxKey(t, f, key, true)
  1231  		if !key.Empty() {
  1232  			testColsAreLaxKey(t, f, opt.ColSet{}, false)
  1233  		}
  1234  		closure := f.ComputeClosure(key)
  1235  		testColsAreLaxKey(t, f, closure, true)
  1236  	} else {
  1237  		testColsAreStrictKey(t, f, opt.ColSet{}, false)
  1238  	}
  1239  }
  1240  
  1241  func testColsAreStrictKey(t *testing.T, f *props.FuncDepSet, cols opt.ColSet, expected bool) {
  1242  	t.Helper()
  1243  	actual := f.ColsAreStrictKey(cols)
  1244  	if actual != expected {
  1245  		if expected {
  1246  			t.Errorf("%s is not a strict key for %s", cols, f)
  1247  		} else {
  1248  			t.Errorf("%s is a strict key for %s", cols, f)
  1249  		}
  1250  	}
  1251  }
  1252  
  1253  func testColsAreLaxKey(t *testing.T, f *props.FuncDepSet, cols opt.ColSet, expected bool) {
  1254  	t.Helper()
  1255  	actual := f.ColsAreLaxKey(cols)
  1256  	if actual != expected {
  1257  		if expected {
  1258  			t.Errorf("%s is not a lax key for %s", cols, f)
  1259  		} else {
  1260  			t.Errorf("%s is a lax key for %s", cols, f)
  1261  		}
  1262  	}
  1263  }
  1264  
  1265  func c(cols ...opt.ColumnID) opt.ColSet {
  1266  	return opt.MakeColSet(cols...)
  1267  }