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  }