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 }