github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/name_resolution_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 tree_test 12 13 import ( 14 "bytes" 15 "context" 16 "fmt" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/sql/parser" 20 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 21 "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" 22 "github.com/cockroachdb/cockroach/pkg/testutils" 23 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 24 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 25 ) 26 27 func TestClassifyTablePattern(t *testing.T) { 28 defer leaktest.AfterTest(t)() 29 testCases := []struct { 30 in, out string 31 expanded string 32 err string 33 }{ 34 {`a`, `a`, `""."".a`, ``}, 35 {`a.b`, `a.b`, `"".a.b`, ``}, 36 {`a.b.c`, `a.b.c`, `a.b.c`, ``}, 37 {`a.b.c.d`, ``, ``, `at or near "\.": syntax error`}, 38 {`a.""`, ``, ``, `invalid table name: a\.""`}, 39 {`a.b.""`, ``, ``, `invalid table name: a\.b\.""`}, 40 {`a.b.c.""`, ``, ``, `at or near "\.": syntax error`}, 41 {`a."".c`, ``, ``, `invalid table name: a\.""\.c`}, 42 // CockroachDB extension: empty catalog name. 43 {`"".b.c`, `"".b.c`, `"".b.c`, ``}, 44 45 // Check keywords: disallowed in first position, ok afterwards. 46 {`user.x.y`, ``, ``, `syntax error`}, 47 {`"user".x.y`, `"user".x.y`, `"user".x.y`, ``}, 48 {`x.user.y`, `x."user".y`, `x."user".y`, ``}, 49 {`x.user`, `x."user"`, `"".x."user"`, ``}, 50 51 {`*`, `*`, `""."".*`, ``}, 52 {`a.*`, `a.*`, `"".a.*`, ``}, 53 {`a.b.*`, `a.b.*`, `a.b.*`, ``}, 54 {`a.b.c.*`, ``, ``, `at or near "\.": syntax error`}, 55 {`a.b.*.c`, ``, ``, `at or near "\.": syntax error`}, 56 {`a.*.b`, ``, ``, `at or near "\.": syntax error`}, 57 {`*.b`, ``, ``, `at or near "\.": syntax error`}, 58 {`"".*`, ``, ``, `invalid table name: "".\*`}, 59 {`a."".*`, ``, ``, `invalid table name: a\.""\.\*`}, 60 {`a.b."".*`, ``, ``, `invalid table name: a.b.""`}, 61 // CockroachDB extension: empty catalog name. 62 {`"".b.*`, `"".b.*`, `"".b.*`, ``}, 63 64 // Check keywords: disallowed in first position, ok afterwards. 65 {`user.x.*`, ``, ``, `syntax error`}, 66 {`"user".x.*`, `"user".x.*`, `"user".x.*`, ``}, 67 {`x.user.*`, `x."user".*`, `x."user".*`, ``}, 68 69 {`foo@bar`, ``, ``, `at or near "@": syntax error`}, 70 } 71 72 for _, tc := range testCases { 73 t.Run(tc.in, func(t *testing.T) { 74 tp, err := func() (tree.TablePattern, error) { 75 stmt, err := parser.ParseOne(fmt.Sprintf("GRANT SELECT ON %s TO foo", tc.in)) 76 if err != nil { 77 return nil, err 78 } 79 tp, err := stmt.AST.(*tree.Grant).Targets.Tables[0].NormalizeTablePattern() 80 if err != nil { 81 return nil, err 82 } 83 return tp, nil 84 }() 85 if !testutils.IsError(err, tc.err) { 86 t.Fatalf("%s: expected %s, but found %v", tc.in, tc.err, err) 87 } 88 if tc.err != "" { 89 return 90 } 91 if out := tp.String(); tc.out != out { 92 t.Fatalf("%s: expected %s, but found %s", tc.in, tc.out, out) 93 } 94 switch tpv := tp.(type) { 95 case *tree.AllTablesSelector: 96 tpv.ExplicitSchema = true 97 tpv.ExplicitCatalog = true 98 case *tree.TableName: 99 tpv.ExplicitSchema = true 100 tpv.ExplicitCatalog = true 101 default: 102 t.Fatalf("%s: unknown pattern type: %T", tc.in, tp) 103 } 104 105 if out := tp.String(); tc.expanded != out { 106 t.Fatalf("%s: expected full %s, but found %s", tc.in, tc.expanded, out) 107 } 108 }) 109 } 110 } 111 112 func TestClassifyColumnName(t *testing.T) { 113 defer leaktest.AfterTest(t)() 114 testCases := []struct { 115 in, out string 116 err string 117 }{ 118 {`a`, `a`, ``}, 119 {`a.b`, `a.b`, ``}, 120 {`a.b.c`, `a.b.c`, ``}, 121 {`a.b.c.d`, `a.b.c.d`, ``}, 122 {`a.b.c.d.e`, ``, `at or near "\.": syntax error`}, 123 {`""`, ``, `invalid column name: ""`}, 124 {`a.""`, ``, `invalid column name: a\.""`}, 125 {`a.b.""`, ``, `invalid column name: a\.b\.""`}, 126 {`a.b.c.""`, ``, `invalid column name: a\.b\.c\.""`}, 127 {`a.b.c.d.""`, ``, `at or near "\.": syntax error`}, 128 {`"".a`, ``, `invalid column name: ""\.a`}, 129 {`"".a.b`, ``, `invalid column name: ""\.a\.b`}, 130 // CockroachDB extension: empty catalog name. 131 {`"".a.b.c`, `"".a.b.c`, ``}, 132 133 {`a.b."".d`, ``, `invalid column name: a\.b\.""\.d`}, 134 {`a."".c.d`, ``, `invalid column name: a\.""\.c\.d`}, 135 136 // Check keywords: disallowed in first position, ok afterwards. 137 {`user.x.y`, ``, `syntax error`}, 138 {`"user".x.y`, `"user".x.y`, ``}, 139 {`x.user.y`, `x.user.y`, ``}, 140 {`x.user`, `x."user"`, ``}, 141 142 {`*`, `*`, ``}, 143 {`a.*`, `a.*`, ``}, 144 {`a.b.*`, `a.b.*`, ``}, 145 {`a.b.c.*`, `a.b.c.*`, ``}, 146 {`a.b.c.d.*`, ``, `at or near "\.": syntax error`}, 147 {`a.b.*.c`, ``, `at or near "\.": syntax error`}, 148 {`a.*.b`, ``, `at or near "\.": syntax error`}, 149 {`*.b`, ``, `at or near "\.": syntax error`}, 150 {`"".*`, ``, `invalid column name: "".\*`}, 151 {`a."".*`, ``, `invalid column name: a\.""\.\*`}, 152 {`a.b."".*`, ``, `invalid column name: a\.b\.""\.\*`}, 153 {`a.b.c."".*`, ``, `at or near "\.": syntax error`}, 154 155 {`"".a.*`, ``, `invalid column name: ""\.a.*`}, 156 // CockroachDB extension: empty catalog name. 157 {`"".a.b.*`, `"".a.b.*`, ``}, 158 159 {`a."".c.*`, ``, `invalid column name: a\.""\.c\.*`}, 160 161 // Check keywords: disallowed in first position, ok afterwards. 162 {`user.x.*`, ``, `syntax error`}, 163 {`"user".x.*`, `"user".x.*`, ``}, 164 {`x.user.*`, `x.user.*`, ``}, 165 166 {`foo@bar`, ``, `at or near "@": syntax error`}, 167 } 168 169 for _, tc := range testCases { 170 t.Run(tc.in, func(t *testing.T) { 171 v, err := func() (tree.VarName, error) { 172 stmt, err := parser.ParseOne(fmt.Sprintf("SELECT %s", tc.in)) 173 if err != nil { 174 return nil, err 175 } 176 v := stmt.AST.(*tree.Select).Select.(*tree.SelectClause).Exprs[0].Expr.(tree.VarName) 177 return v.NormalizeVarName() 178 }() 179 if !testutils.IsError(err, tc.err) { 180 t.Fatalf("%s: expected %s, but found %v", tc.in, tc.err, err) 181 } 182 if tc.err != "" { 183 return 184 } 185 if out := v.String(); tc.out != out { 186 t.Fatalf("%s: expected %s, but found %s", tc.in, tc.out, out) 187 } 188 switch v.(type) { 189 case *tree.AllColumnsSelector: 190 case tree.UnqualifiedStar: 191 case *tree.ColumnItem: 192 default: 193 t.Fatalf("%s: unknown var type: %T", tc.in, v) 194 } 195 }) 196 } 197 } 198 199 // fakeSource represents a fake column resolution environment for tests. 200 type fakeSource struct { 201 t *testing.T 202 knownTables []knownTable 203 } 204 205 type knownTable struct { 206 srcName tree.TableName 207 columns []tree.Name 208 } 209 210 type colsRes tree.NameList 211 212 func (c colsRes) ColumnSourceMeta() {} 213 214 // FindSourceMatchingName is part of the ColumnItemResolver interface. 215 func (f *fakeSource) FindSourceMatchingName( 216 _ context.Context, tn tree.TableName, 217 ) ( 218 res tree.NumResolutionResults, 219 prefix *tree.TableName, 220 srcMeta tree.ColumnSourceMeta, 221 err error, 222 ) { 223 defer func() { 224 f.t.Logf("FindSourceMatchingName(%s) -> res %d prefix %s meta %v err %v", 225 &tn, res, prefix, srcMeta, err) 226 }() 227 found := false 228 var columns colsRes 229 for i := range f.knownTables { 230 t := &f.knownTables[i] 231 if t.srcName.ObjectName != tn.ObjectName { 232 continue 233 } 234 if tn.ExplicitSchema { 235 if !t.srcName.ExplicitSchema || t.srcName.SchemaName != tn.SchemaName { 236 continue 237 } 238 if tn.ExplicitCatalog { 239 if !t.srcName.ExplicitCatalog || t.srcName.CatalogName != tn.CatalogName { 240 continue 241 } 242 } 243 } 244 if found { 245 return tree.MoreThanOne, nil, nil, fmt.Errorf("ambiguous source name: %q", &tn) 246 } 247 found = true 248 prefix = &t.srcName 249 columns = colsRes(t.columns) 250 } 251 if !found { 252 return tree.NoResults, nil, nil, nil 253 } 254 return tree.ExactlyOne, prefix, columns, nil 255 } 256 257 // FindSourceProvidingColumn is part of the ColumnItemResolver interface. 258 func (f *fakeSource) FindSourceProvidingColumn( 259 _ context.Context, col tree.Name, 260 ) (prefix *tree.TableName, srcMeta tree.ColumnSourceMeta, colHint int, err error) { 261 defer func() { 262 f.t.Logf("FindSourceProvidingColumn(%s) -> prefix %s meta %v hint %d err %v", 263 col, prefix, srcMeta, colHint, err) 264 }() 265 found := false 266 var columns colsRes 267 for i := range f.knownTables { 268 t := &f.knownTables[i] 269 for c, cn := range t.columns { 270 if cn != col { 271 continue 272 } 273 if found { 274 return nil, nil, -1, f.ambiguousColumnErr(col) 275 } 276 found = true 277 colHint = c 278 columns = colsRes(t.columns) 279 prefix = &t.srcName 280 break 281 } 282 } 283 if !found { 284 return nil, nil, -1, fmt.Errorf("column %q does not exist", &col) 285 } 286 return prefix, columns, colHint, nil 287 } 288 289 func (f *fakeSource) ambiguousColumnErr(col tree.Name) error { 290 var candidates bytes.Buffer 291 sep := "" 292 for i := range f.knownTables { 293 t := &f.knownTables[i] 294 for _, cn := range t.columns { 295 if cn == col { 296 fmt.Fprintf(&candidates, "%s%s.%s", sep, tree.ErrString(&t.srcName), cn) 297 sep = ", " 298 } 299 } 300 } 301 return fmt.Errorf("column reference %q is ambiguous (candidates: %s)", &col, candidates.String()) 302 } 303 304 type colRes string 305 306 func (c colRes) ColumnResolutionResult() {} 307 308 // Resolve is part of the ColumnItemResolver interface. 309 func (f *fakeSource) Resolve( 310 _ context.Context, 311 prefix *tree.TableName, 312 srcMeta tree.ColumnSourceMeta, 313 colHint int, 314 col tree.Name, 315 ) (tree.ColumnResolutionResult, error) { 316 f.t.Logf("in Resolve: prefix %s meta %v colHint %d col %s", 317 prefix, srcMeta, colHint, col) 318 columns, ok := srcMeta.(colsRes) 319 if !ok { 320 return nil, fmt.Errorf("programming error: srcMeta invalid") 321 } 322 if colHint >= 0 { 323 // Resolution succeeded. Let's do some sanity checking. 324 if columns[colHint] != col { 325 return nil, fmt.Errorf("programming error: invalid colHint %d", colHint) 326 } 327 return colRes(fmt.Sprintf("%s.%s", prefix, col)), nil 328 } 329 for _, cn := range columns { 330 if col == cn { 331 // Resolution succeeded. 332 return colRes(fmt.Sprintf("%s.%s", prefix, col)), nil 333 } 334 } 335 return nil, fmt.Errorf("unknown column name: %s", &col) 336 } 337 338 var _ sqlutils.ColumnItemResolverTester = &fakeSource{} 339 340 // GetColumnItemResolver is part of the sqlutils.ColumnItemResolverTester 341 // interface. 342 func (f *fakeSource) GetColumnItemResolver() tree.ColumnItemResolver { 343 return f 344 } 345 346 // AddTable is part of the sqlutils.ColumnItemResolverTester interface. 347 func (f *fakeSource) AddTable(tabName tree.TableName, colNames []tree.Name) { 348 f.knownTables = append(f.knownTables, knownTable{srcName: tabName, columns: colNames}) 349 } 350 351 // ResolveQualifiedStarTestResults is part of the 352 // sqlutils.ColumnItemResolverTester interface. 353 func (f *fakeSource) ResolveQualifiedStarTestResults( 354 srcName *tree.TableName, srcMeta tree.ColumnSourceMeta, 355 ) (string, string, error) { 356 cs, ok := srcMeta.(colsRes) 357 if !ok { 358 return "", "", fmt.Errorf("fake resolver did not return colsRes, found %T instead", srcMeta) 359 } 360 nl := tree.NameList(cs) 361 return srcName.String(), nl.String(), nil 362 } 363 364 // ResolveColumnItemTestResults is part of the 365 // sqlutils.ColumnItemResolverTester interface. 366 func (f *fakeSource) ResolveColumnItemTestResults(res tree.ColumnResolutionResult) (string, error) { 367 c, ok := res.(colRes) 368 if !ok { 369 return "", fmt.Errorf("fake resolver did not return colRes, found %T instead", res) 370 } 371 return string(c), nil 372 } 373 374 func TestResolveQualifiedStar(t *testing.T) { 375 defer leaktest.AfterTest(t)() 376 f := &fakeSource{t: t} 377 sqlutils.RunResolveQualifiedStarTest(t, f) 378 } 379 380 func TestResolveColumnItem(t *testing.T) { 381 defer leaktest.AfterTest(t)() 382 f := &fakeSource{t: t} 383 sqlutils.RunResolveColumnItemTest(t, f) 384 } 385 386 // fakeMetadata represents a fake table resolution environment for tests. 387 type fakeMetadata struct { 388 t *testing.T 389 knownVSchemas []knownSchema 390 knownCatalogs []knownCatalog 391 } 392 393 type knownSchema struct { 394 scName tree.Name 395 tables []tree.Name 396 } 397 398 func (*knownSchema) SchemaMeta() {} 399 400 type knownCatalog struct { 401 ctName tree.Name 402 schemas []knownSchema 403 } 404 405 // LookupSchema implements the TableNameResolver interface. 406 func (f *fakeMetadata) LookupSchema( 407 _ context.Context, dbName, scName string, 408 ) (found bool, scMeta tree.SchemaMeta, err error) { 409 defer func() { 410 f.t.Logf("LookupSchema(%s, %s) -> found %v meta %v err %v", 411 dbName, scName, found, scMeta, err) 412 }() 413 for i := range f.knownVSchemas { 414 v := &f.knownVSchemas[i] 415 if scName == string(v.scName) { 416 // Virtual schema found, check that the db exists. 417 // The empty database is valid. 418 if dbName == "" { 419 return true, v, nil 420 } 421 for j := range f.knownCatalogs { 422 c := &f.knownCatalogs[j] 423 if dbName == string(c.ctName) { 424 return true, v, nil 425 } 426 } 427 // No valid database, schema is invalid. 428 return false, nil, nil 429 } 430 } 431 for i := range f.knownCatalogs { 432 c := &f.knownCatalogs[i] 433 if dbName == string(c.ctName) { 434 for j := range c.schemas { 435 s := &c.schemas[j] 436 if scName == string(s.scName) { 437 return true, s, nil 438 } 439 } 440 break 441 } 442 } 443 return false, nil, nil 444 } 445 446 type fakeResResult int 447 448 func (fakeResResult) NameResolutionResult() {} 449 450 // LookupObject implements the TableNameResolver interface. 451 func (f *fakeMetadata) LookupObject( 452 _ context.Context, lookupFlags tree.ObjectLookupFlags, dbName, scName, tbName string, 453 ) (found bool, obMeta tree.NameResolutionResult, err error) { 454 defer func() { 455 f.t.Logf("LookupObject(%s, %s, %s) -> found %v meta %v err %v", 456 dbName, scName, tbName, found, obMeta, err) 457 }() 458 foundV := false 459 for i := range f.knownVSchemas { 460 v := &f.knownVSchemas[i] 461 if scName == string(v.scName) { 462 // Virtual schema found, check that the db exists. 463 // The empty database is valid. 464 if dbName != "" { 465 hasDb := false 466 for j := range f.knownCatalogs { 467 c := &f.knownCatalogs[j] 468 if dbName == string(c.ctName) { 469 hasDb = true 470 break 471 } 472 } 473 if !hasDb { 474 return false, nil, nil 475 } 476 } 477 // Db valid, check the table name. 478 for tbIdx, tb := range v.tables { 479 if tbName == string(tb) { 480 return true, fakeResResult(tbIdx), nil 481 } 482 } 483 foundV = true 484 break 485 } 486 } 487 if foundV { 488 // Virtual schema matched, but there was no table. Fail. 489 return false, nil, nil 490 } 491 492 for i := range f.knownCatalogs { 493 c := &f.knownCatalogs[i] 494 if dbName == string(c.ctName) { 495 for j := range c.schemas { 496 s := &c.schemas[j] 497 if scName == string(s.scName) { 498 for tbIdx, tb := range s.tables { 499 if tbName == string(tb) { 500 return true, fakeResResult(tbIdx), nil 501 } 502 } 503 break 504 } 505 } 506 break 507 } 508 } 509 return false, nil, nil 510 } 511 512 func newFakeMetadata() *fakeMetadata { 513 return &fakeMetadata{ 514 knownVSchemas: []knownSchema{ 515 {"pg_catalog", []tree.Name{"pg_tables"}}, 516 }, 517 knownCatalogs: []knownCatalog{ 518 {"db1", []knownSchema{{"public", []tree.Name{"foo", "kv"}}}}, 519 {"db2", []knownSchema{ 520 {"public", []tree.Name{"foo"}}, 521 {"extended", []tree.Name{"bar", "pg_tables"}}, 522 }}, 523 {"db3", []knownSchema{ 524 {"public", []tree.Name{"foo", "bar"}}, 525 {"pg_temp_123", []tree.Name{"foo", "baz"}}, 526 }}, 527 }, 528 } 529 } 530 531 func TestResolveTablePatternOrName(t *testing.T) { 532 defer leaktest.AfterTest(t)() 533 type spath = sessiondata.SearchPath 534 535 var mpath = func(args ...string) spath { 536 return sessiondata.MakeSearchPath(args) 537 } 538 539 var tpath = func(tempSchemaName string, args ...string) spath { 540 return sessiondata.MakeSearchPath(args).WithTemporarySchemaName(tempSchemaName) 541 } 542 543 testCases := []struct { 544 // Test inputs. 545 in string // The table name or pattern. 546 curDb string // The current database. 547 searchPath spath // The current search path. 548 expected bool // If non-star, whether the object is expected to exist already. 549 // Expected outputs. 550 out string // The prefix after resolution. 551 expanded string // The prefix after resolution, with hidden fields revealed. 552 scName string // The schema name after resolution. 553 err string // Error, if expected. 554 }{ 555 // 556 // Tests for table names. 557 // 558 559 // Names of length 1. 560 561 {`kv`, `db1`, mpath("public", "pg_catalog"), true, `kv`, `db1.public.kv`, `db1.public[1]`, ``}, 562 {`foo`, `db1`, mpath("public", "pg_catalog"), true, `foo`, `db1.public.foo`, `db1.public[0]`, ``}, 563 {`blix`, `db1`, mpath("public", "pg_catalog"), true, ``, ``, ``, `prefix or object not found`}, 564 {`pg_tables`, `db1`, mpath("public", "pg_catalog"), true, `pg_tables`, `db1.pg_catalog.pg_tables`, `db1.pg_catalog[0]`, ``}, 565 566 {`blix`, `db1`, mpath("public", "pg_catalog"), false, `blix`, `db1.public.blix`, `db1.public`, ``}, 567 568 // A valid table is invisible if "public" is not in the search path. 569 {`kv`, `db1`, mpath(), true, ``, ``, ``, `prefix or object not found`}, 570 571 // But pg_catalog is magic and "always there". 572 {`pg_tables`, `db1`, mpath(), true, `pg_tables`, `db1.pg_catalog.pg_tables`, `db1.pg_catalog[0]`, ``}, 573 {`blix`, `db1`, mpath(), false, ``, ``, ``, `prefix or object not found`}, 574 575 // If there's a table with the same name as a pg_catalog table, then search path order matters. 576 {`pg_tables`, `db2`, mpath("extended", "pg_catalog"), true, `pg_tables`, `db2.extended.pg_tables`, `db2.extended[1]`, ``}, 577 {`pg_tables`, `db2`, mpath("pg_catalog", "extended"), true, `pg_tables`, `db2.pg_catalog.pg_tables`, `db2.pg_catalog[0]`, ``}, 578 // When pg_catalog is not explicitly mentioned in the search path, it is searched first. 579 {`pg_tables`, `db2`, mpath("foo"), true, `pg_tables`, `db2.pg_catalog.pg_tables`, `db2.pg_catalog[0]`, ``}, 580 581 // Names of length 2. 582 583 {`public.kv`, `db1`, mpath("public", "pg_catalog"), true, `public.kv`, `db1.public.kv`, `db1.public[1]`, ``}, 584 {`public.foo`, `db1`, mpath("public", "pg_catalog"), true, `public.foo`, `db1.public.foo`, `db1.public[0]`, ``}, 585 {`public.blix`, `db1`, mpath("public", "pg_catalog"), true, ``, ``, ``, `prefix or object not found`}, 586 {`public.pg_tables`, `db1`, mpath("public", "pg_catalog"), true, ``, ``, ``, `prefix or object not found`}, 587 {`extended.pg_tables`, `db2`, mpath("public", "pg_catalog"), true, `extended.pg_tables`, `db2.extended.pg_tables`, `db2.extended[1]`, ``}, 588 {`pg_catalog.pg_tables`, `db1`, mpath("public", "pg_catalog"), true, `pg_catalog.pg_tables`, `db1.pg_catalog.pg_tables`, `db1.pg_catalog[0]`, ``}, 589 590 {`public.blix`, `db1`, mpath("public", "pg_catalog"), false, `public.blix`, `db1.public.blix`, `db1.public`, ``}, 591 592 // Compat with CockroachDB v1.x. 593 {`db1.kv`, `db1`, mpath("public", "pg_catalog"), true, `db1.public.kv`, `db1.public.kv`, `db1.public[1]`, ``}, 594 595 {`blix.foo`, `db1`, mpath("public", "pg_catalog"), true, ``, ``, ``, `prefix or object not found`}, 596 {`blix.pg_tables`, `db1`, mpath("public", "pg_catalog"), true, ``, ``, ``, `prefix or object not found`}, 597 598 // Names of length 3. 599 600 {`db1.public.foo`, `db1`, mpath("public", "pg_catalog"), true, `db1.public.foo`, `db1.public.foo`, `db1.public[0]`, ``}, 601 {`db1.public.kv`, `db1`, mpath(), true, `db1.public.kv`, `db1.public.kv`, `db1.public[1]`, ``}, 602 {`db1.public.blix`, `db1`, mpath(), false, `db1.public.blix`, `db1.public.blix`, `db1.public`, ``}, 603 604 {`blix.public.foo`, `db1`, mpath("public"), true, ``, ``, ``, `prefix or object not found`}, 605 {`blix.public.foo`, `db1`, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 606 607 // Beware: vtables only exist in valid databases and the empty database name. 608 {`db1.pg_catalog.pg_tables`, `db1`, mpath(), true, `db1.pg_catalog.pg_tables`, `db1.pg_catalog.pg_tables`, `db1.pg_catalog[0]`, ``}, 609 {`"".pg_catalog.pg_tables`, `db1`, mpath(), true, `"".pg_catalog.pg_tables`, `"".pg_catalog.pg_tables`, `.pg_catalog[0]`, ``}, 610 {`blix.pg_catalog.pg_tables`, `db1`, mpath("public"), true, ``, ``, ``, `prefix or object not found`}, 611 {`blix.pg_catalog.pg_tables`, `db1`, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 612 {`"".pg_catalog.blix`, `db1`, mpath(), false, `"".pg_catalog.blix`, `"".pg_catalog.blix`, `.pg_catalog`, ``}, 613 614 // 615 // Tests for table names with no current database. 616 // 617 618 {`kv`, ``, mpath("public", "pg_catalog"), true, ``, ``, ``, `prefix or object not found`}, 619 {`pg_tables`, ``, mpath("public", "pg_catalog"), true, `pg_tables`, `"".pg_catalog.pg_tables`, `.pg_catalog[0]`, ``}, 620 {`pg_tables`, ``, mpath(), true, `pg_tables`, `"".pg_catalog.pg_tables`, `.pg_catalog[0]`, ``}, 621 622 {`blix`, ``, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 623 {`blix`, ``, mpath("public", "pg_catalog"), false, `blix`, `"".pg_catalog.blix`, `.pg_catalog`, ``}, 624 625 // Names of length 2. 626 627 {`public.kv`, ``, mpath("public", "pg_catalog"), true, ``, ``, ``, `prefix or object not found`}, 628 {`pg_catalog.pg_tables`, ``, mpath("public", "pg_catalog"), true, `pg_catalog.pg_tables`, `"".pg_catalog.pg_tables`, `.pg_catalog[0]`, ``}, 629 630 // Compat with CockroachDB v1.x. 631 {`db1.kv`, ``, mpath("public", "pg_catalog"), true, `db1.public.kv`, `db1.public.kv`, `db1.public[1]`, ``}, 632 {`db1.blix`, ``, mpath("public", "pg_catalog"), true, ``, ``, ``, `prefix or object not found`}, 633 634 {`blix.pg_tables`, ``, mpath("public", "pg_catalog"), true, ``, ``, ``, `prefix or object not found`}, 635 636 // Names of length 3. 637 638 {`db1.public.foo`, ``, mpath("public", "pg_catalog"), true, `db1.public.foo`, `db1.public.foo`, `db1.public[0]`, ``}, 639 {`db1.public.kv`, ``, mpath(), true, `db1.public.kv`, `db1.public.kv`, `db1.public[1]`, ``}, 640 {`db1.public.blix`, ``, mpath(), false, `db1.public.blix`, `db1.public.blix`, `db1.public`, ``}, 641 642 {`blix.public.foo`, ``, mpath("public"), true, ``, ``, ``, `prefix or object not found`}, 643 {`blix.public.foo`, ``, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 644 645 // Beware: vtables only exist in valid databases and the empty database name. 646 {`db1.pg_catalog.pg_tables`, ``, mpath(), true, `db1.pg_catalog.pg_tables`, `db1.pg_catalog.pg_tables`, `db1.pg_catalog[0]`, ``}, 647 {`"".pg_catalog.pg_tables`, ``, mpath(), true, `"".pg_catalog.pg_tables`, `"".pg_catalog.pg_tables`, `.pg_catalog[0]`, ``}, 648 {`blix.pg_catalog.pg_tables`, ``, mpath("public"), true, ``, ``, ``, `prefix or object not found`}, 649 {`blix.pg_catalog.pg_tables`, ``, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 650 {`"".pg_catalog.blix`, ``, mpath(), false, `"".pg_catalog.blix`, `"".pg_catalog.blix`, `.pg_catalog`, ``}, 651 652 // 653 // Tests for table patterns. 654 // 655 656 // Patterns of length 1. 657 658 {`*`, `db1`, mpath("public", "pg_catalog"), false, `*`, `db1.public.*`, `db1.public`, ``}, 659 660 // Patterns of length 2. 661 {`public.*`, `db1`, mpath("public"), false, `public.*`, `db1.public.*`, `db1.public`, ``}, 662 {`public.*`, `db1`, mpath("public", "pg_catalog"), false, `public.*`, `db1.public.*`, `db1.public`, ``}, 663 {`public.*`, `db1`, mpath(), false, `public.*`, `db1.public.*`, `db1.public`, ``}, 664 665 {`blix.*`, `db1`, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 666 667 {`pg_catalog.*`, `db1`, mpath("public"), false, `pg_catalog.*`, `db1.pg_catalog.*`, `db1.pg_catalog`, ``}, 668 {`pg_catalog.*`, `db1`, mpath("public", "pg_catalog"), false, `pg_catalog.*`, `db1.pg_catalog.*`, `db1.pg_catalog`, ``}, 669 {`pg_catalog.*`, `db1`, mpath(), false, `pg_catalog.*`, `db1.pg_catalog.*`, `db1.pg_catalog`, ``}, 670 671 // 672 // Tests for table patterns with no current database. 673 // 674 675 // Patterns of length 1. 676 677 {`*`, ``, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 678 {`*`, ``, mpath("public", "pg_catalog"), false, `*`, `"".pg_catalog.*`, `.pg_catalog`, ``}, 679 680 // Patterns of length 2. 681 682 {`public.*`, ``, mpath("public", "pg_catalog"), false, ``, ``, ``, `prefix or object not found`}, 683 // vtables exist also in the empty database. 684 {`pg_catalog.*`, ``, mpath("public", "pg_catalog"), false, `pg_catalog.*`, `"".pg_catalog.*`, `.pg_catalog`, ``}, 685 {`pg_catalog.*`, ``, mpath(), false, `pg_catalog.*`, `"".pg_catalog.*`, `.pg_catalog`, ``}, 686 687 // Compat with CockroachDB v1.x. 688 {`db1.*`, ``, mpath("public", "pg_catalog"), false, `db1.public.*`, `db1.public.*`, `db1.public`, ``}, 689 690 {`blix.*`, ``, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 691 {`blix.*`, ``, mpath("public", "pg_catalog"), false, ``, ``, ``, `prefix or object not found`}, 692 {`blix.*`, ``, mpath(), false, ``, ``, ``, `prefix or object not found`}, 693 694 // Patterns of length 3. 695 696 {`db1.public.*`, ``, mpath("public", "pg_catalog"), false, `db1.public.*`, `db1.public.*`, `db1.public`, ``}, 697 {`db1.public.*`, ``, mpath(), false, `db1.public.*`, `db1.public.*`, `db1.public`, ``}, 698 699 {`blix.public.*`, ``, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 700 {`blix.public.*`, ``, mpath("public", "pg_catalog"), false, ``, ``, ``, `prefix or object not found`}, 701 702 // Beware: vtables only exist in valid databases and the empty database name. 703 {`db1.pg_catalog.*`, ``, mpath(), false, `db1.pg_catalog.*`, `db1.pg_catalog.*`, `db1.pg_catalog`, ``}, 704 {`"".pg_catalog.*`, ``, mpath(), false, `"".pg_catalog.*`, `"".pg_catalog.*`, `.pg_catalog`, ``}, 705 {`blix.pg_catalog.*`, ``, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 706 707 // 708 // Tests for temporary table resolution 709 // 710 711 // Names of length 1 712 713 {`foo`, `db3`, tpath("pg_temp_123", "public"), true, `foo`, `db3.pg_temp_123.foo`, `db3.pg_temp_123[0]`, ``}, 714 {`foo`, `db3`, tpath("pg_temp_123", "public", "pg_temp"), true, `foo`, `db3.public.foo`, `db3.public[0]`, ``}, 715 {`baz`, `db3`, tpath("pg_temp_123", "public"), true, `baz`, `db3.pg_temp_123.baz`, `db3.pg_temp_123[1]`, ``}, 716 {`bar`, `db3`, tpath("pg_temp_123", "public"), true, `bar`, `db3.public.bar`, `db3.public[1]`, ``}, 717 {`bar`, `db3`, tpath("pg_temp_123", "public", "pg_temp"), true, `bar`, `db3.public.bar`, `db3.public[1]`, ``}, 718 719 // Names of length 2 720 721 {`public.foo`, `db3`, tpath("pg_temp_123", "public"), true, `public.foo`, `db3.public.foo`, `db3.public[0]`, ``}, 722 {`pg_temp.foo`, `db3`, tpath("pg_temp_123", "public"), true, `pg_temp.foo`, `db3.pg_temp.foo`, `db3.pg_temp[0]`, ``}, 723 {`pg_temp_123.foo`, `db3`, tpath("pg_temp_123", "public"), true, `pg_temp_123.foo`, `db3.pg_temp_123.foo`, `db3.pg_temp_123[0]`, ``}, 724 725 // Wrongly qualifying a TT/PT as a PT/TT results in an error. 726 {`pg_temp.bar`, `db3`, tpath("pg_temp_123", "public"), true, ``, ``, ``, `prefix or object not found`}, 727 {`public.baz`, `db3`, tpath("pg_temp_123", "public"), true, ``, ``, ``, `prefix or object not found`}, 728 729 // Cases where a session tries to access a temporary table of another session. 730 {`pg_temp_111.foo`, `db3`, tpath("pg_temp_123", "public"), true, ``, ``, ``, `cannot access temporary tables of other sessions`}, 731 {`pg_temp_111.foo`, `db3`, tpath("pg_temp_123", "public"), false, ``, ``, ``, `cannot access temporary tables of other sessions`}, 732 733 // Case where the temporary table being created has the same name as an 734 // existing persistent table. 735 {`pg_temp.bar`, `db3`, tpath("pg_temp_123", "public"), false, `pg_temp.bar`, `db3.pg_temp.bar`, `db3.pg_temp_123`, ``}, 736 737 // Case where the persistent table being created has the same name as an 738 // existing temporary table. 739 {`public.baz`, `db3`, tpath("pg_temp_123", "public"), false, `public.baz`, `db3.public.baz`, `db3.public`, ``}, 740 741 // Cases where the temporary schema has not been created yet 742 {`pg_temp.foo`, `db3`, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 743 744 // Names of length 3 745 746 {`db3.public.foo`, `db3`, tpath("pg_temp_123", "public"), true, `db3.public.foo`, `db3.public.foo`, `db3.public[0]`, ``}, 747 {`db3.pg_temp.foo`, `db3`, tpath("pg_temp_123", "public"), true, `db3.pg_temp.foo`, `db3.pg_temp.foo`, `db3.pg_temp[0]`, ``}, 748 {`db3.pg_temp_123.foo`, `db3`, tpath("pg_temp_123", "public"), true, `db3.pg_temp_123.foo`, `db3.pg_temp_123.foo`, `db3.pg_temp_123[0]`, ``}, 749 750 // Wrongly qualifying a TT/PT as a PT/TT results in an error. 751 {`db3.pg_temp.bar`, `db3`, tpath("pg_temp_123", "public"), true, ``, ``, ``, `prefix or object not found`}, 752 {`db3.public.baz`, `db3`, tpath("pg_temp_123", "public"), true, ``, ``, ``, `prefix or object not found`}, 753 754 // Cases where a session tries to access a temporary table of another session. 755 {`db3.pg_temp_111.foo`, `db3`, tpath("pg_temp_123", "public"), true, ``, ``, ``, `cannot access temporary tables of other sessions`}, 756 {`db3.pg_temp_111.foo`, `db3`, tpath("pg_temp_123", "public"), false, ``, ``, ``, `cannot access temporary tables of other sessions`}, 757 758 // Case where the temporary table being created has the same name as an 759 // existing persistent table. 760 {`db3.pg_temp.bar`, `db3`, tpath("pg_temp_123", "public"), false, `db3.pg_temp.bar`, `db3.pg_temp.bar`, `db3.pg_temp_123`, ``}, 761 762 // Case where the persistent table being created has the same name as an 763 // existing temporary table. 764 {`db3.public.baz`, `db3`, tpath("pg_temp_123", "public"), false, `db3.public.baz`, `db3.public.baz`, `db3.public`, ``}, 765 766 // Cases where the temporary schema has not been created yet 767 {`db3.pg_temp.foo`, `db3`, mpath("public"), false, ``, ``, ``, `prefix or object not found`}, 768 } 769 770 fakeResolver := newFakeMetadata() 771 for _, tc := range testCases { 772 t.Run(fmt.Sprintf("%s/%s/%s/%v", tc.in, tc.curDb, tc.searchPath, tc.expected), func(t *testing.T) { 773 fakeResolver.t = t 774 tp, sc, err := func() (tree.TablePattern, string, error) { 775 stmt, err := parser.ParseOne(fmt.Sprintf("GRANT SELECT ON TABLE %s TO foo", tc.in)) 776 if err != nil { 777 return nil, "", err 778 } 779 tp, err := stmt.AST.(*tree.Grant).Targets.Tables[0].NormalizeTablePattern() 780 if err != nil { 781 return nil, "", err 782 } 783 784 var found bool 785 var scPrefix, ctPrefix string 786 var scMeta interface{} 787 var obMeta interface{} 788 ctx := context.Background() 789 switch tpv := tp.(type) { 790 case *tree.AllTablesSelector: 791 found, scMeta, err = tpv.ObjectNamePrefix.Resolve(ctx, fakeResolver, tc.curDb, tc.searchPath) 792 scPrefix = tpv.Schema() 793 ctPrefix = tpv.Catalog() 794 case *tree.TableName: 795 var prefix tree.ObjectNamePrefix 796 if tc.expected { 797 flags := tree.ObjectLookupFlags{} 798 // TODO: As part of work for #34240, we should be operating on 799 // UnresolvedObjectNames here, rather than TableNames. 800 un := tpv.ToUnresolvedObjectName() 801 found, prefix, obMeta, err = tree.ResolveExisting(ctx, un, fakeResolver, flags, tc.curDb, tc.searchPath) 802 } else { 803 // TODO: As part of work for #34240, we should be operating on 804 // UnresolvedObjectNames here, rather than TableNames. 805 un := tpv.ToUnresolvedObjectName() 806 found, prefix, scMeta, err = tree.ResolveTarget(ctx, un, fakeResolver, tc.curDb, tc.searchPath) 807 } 808 tpv.ObjectNamePrefix = prefix 809 scPrefix = tpv.Schema() 810 ctPrefix = tpv.Catalog() 811 default: 812 t.Fatalf("%s: unknown pattern type: %T", t.Name(), tp) 813 } 814 if err != nil { 815 return nil, "", err 816 } 817 818 var scRes string 819 if scMeta != nil { 820 sc, ok := scMeta.(*knownSchema) 821 if !ok { 822 t.Fatalf("%s: scMeta not of correct type: %v", t.Name(), scMeta) 823 } 824 scRes = fmt.Sprintf("%s.%s", ctPrefix, sc.scName) 825 } 826 if obMeta != nil { 827 obIdx, ok := obMeta.(fakeResResult) 828 if !ok { 829 t.Fatalf("%s: obMeta not of correct type: %v", t.Name(), obMeta) 830 } 831 scRes = fmt.Sprintf("%s.%s[%d]", ctPrefix, scPrefix, obIdx) 832 } 833 834 if !found { 835 return nil, "", fmt.Errorf("prefix or object not found") 836 } 837 return tp, scRes, nil 838 }() 839 840 if !testutils.IsError(err, tc.err) { 841 t.Fatalf("%s: expected %s, but found %v", t.Name(), tc.err, err) 842 } 843 if tc.err != "" { 844 return 845 } 846 if out := tp.String(); tc.out != out { 847 t.Errorf("%s: expected %s, but found %s", t.Name(), tc.out, out) 848 } 849 switch tpv := tp.(type) { 850 case *tree.AllTablesSelector: 851 tpv.ObjectNamePrefix.ExplicitCatalog = true 852 tpv.ObjectNamePrefix.ExplicitSchema = true 853 case *tree.TableName: 854 tpv.ObjectNamePrefix.ExplicitCatalog = true 855 tpv.ObjectNamePrefix.ExplicitSchema = true 856 } 857 if out := tp.String(); tc.expanded != out { 858 t.Errorf("%s: expected full %s, but found %s", t.Name(), tc.expanded, out) 859 } 860 if tc.scName != sc { 861 t.Errorf("%s: expected schema %s, but found %s", t.Name(), tc.scName, sc) 862 } 863 }) 864 } 865 }