github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/name_resolution.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
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"strings"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
    21  )
    22  
    23  // This file contains the two major components to name resolution:
    24  //
    25  // - classification algorithms. These are used when two different
    26  //   semantic constructs can appear in the same position in the SQL grammar.
    27  //   For example, table patterns and table names in GRANT.
    28  //
    29  // - resolution algorithms. These are used to map an unresolved name
    30  //   or pattern to something looked up from the database.
    31  //
    32  
    33  // classifyTablePattern distinguishes between a TableName (last name
    34  // part is a table name) and an AllTablesSelector.
    35  // Used e.g. for GRANT.
    36  func classifyTablePattern(n *UnresolvedName) (TablePattern, error) {
    37  	if n.NumParts < 1 || n.NumParts > 3 {
    38  		return nil, newInvTableNameError(n)
    39  	}
    40  	// Check that all the parts specified are not empty.
    41  	firstCheck := 0
    42  	if n.Star {
    43  		firstCheck = 1
    44  	}
    45  	// It's OK if the catalog name is empty.
    46  	// We allow this in e.g. `select * from "".crdb_internal.tables`.
    47  	lastCheck := n.NumParts
    48  	if lastCheck > 2 {
    49  		lastCheck = 2
    50  	}
    51  	for i := firstCheck; i < lastCheck; i++ {
    52  		if len(n.Parts[i]) == 0 {
    53  			return nil, newInvTableNameError(n)
    54  		}
    55  	}
    56  
    57  	// Construct the result.
    58  	if n.Star {
    59  		return &AllTablesSelector{makeObjectNamePrefixFromUnresolvedName(n)}, nil
    60  	}
    61  	tb := makeTableNameFromUnresolvedName(n)
    62  	return &tb, nil
    63  }
    64  
    65  // classifyColumnItem distinguishes between a ColumnItem (last name
    66  // part is a column name) and an AllColumnsSelector.
    67  //
    68  // Used e.g. in SELECT clauses.
    69  func classifyColumnItem(n *UnresolvedName) (VarName, error) {
    70  	if n.NumParts < 1 || n.NumParts > 4 {
    71  		return nil, newInvColRef(n)
    72  	}
    73  
    74  	// Check that all the parts specified are not empty.
    75  	firstCheck := 0
    76  	if n.Star {
    77  		firstCheck = 1
    78  	}
    79  	// It's OK if the catalog name is empty.
    80  	// We allow this in e.g.
    81  	// `select "".crdb_internal.tables.table_id from "".crdb_internal.tables`.
    82  	lastCheck := n.NumParts
    83  	if lastCheck > 3 {
    84  		lastCheck = 3
    85  	}
    86  	for i := firstCheck; i < lastCheck; i++ {
    87  		if len(n.Parts[i]) == 0 {
    88  			return nil, newInvColRef(n)
    89  		}
    90  	}
    91  
    92  	// Construct the result.
    93  	var tn *UnresolvedObjectName
    94  	if n.NumParts > 1 {
    95  		var err error
    96  		tn, err = NewUnresolvedObjectName(
    97  			n.NumParts-1,
    98  			[3]string{n.Parts[1], n.Parts[2], n.Parts[3]},
    99  			NoAnnotation,
   100  		)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  	}
   105  	if n.Star {
   106  		return &AllColumnsSelector{TableName: tn}, nil
   107  	}
   108  	return &ColumnItem{TableName: tn, ColumnName: Name(n.Parts[0])}, nil
   109  }
   110  
   111  // Resolution algorithms follow.
   112  
   113  const (
   114  	// PublicSchema is the name of the physical schema in every
   115  	// database/catalog.
   116  	PublicSchema string = sessiondata.PublicSchemaName
   117  	// PublicSchemaName is the same, typed as Name.
   118  	PublicSchemaName Name = Name(PublicSchema)
   119  )
   120  
   121  // NumResolutionResults represents the number of results in the lookup
   122  // of data sources matching a given prefix.
   123  type NumResolutionResults int
   124  
   125  const (
   126  	// NoResults for when there is no result.
   127  	NoResults NumResolutionResults = iota
   128  	// ExactlyOne indicates just one source matching the requested name.
   129  	ExactlyOne
   130  	// MoreThanOne signals an ambiguous match.
   131  	MoreThanOne
   132  )
   133  
   134  // ColumnItemResolver is the helper interface to resolve column items.
   135  type ColumnItemResolver interface {
   136  	// FindSourceMatchingName searches for a data source with name tn.
   137  	//
   138  	// This must error out with "ambiguous table name" if there is more
   139  	// than one data source matching tn. The srcMeta is subsequently
   140  	// passed to Resolve() if resolution succeeds. The prefix will not be
   141  	// modified.
   142  	FindSourceMatchingName(ctx context.Context, tn TableName) (res NumResolutionResults, prefix *TableName, srcMeta ColumnSourceMeta, err error)
   143  
   144  	// FindSourceProvidingColumn searches for a data source providing
   145  	// a column with the name given.
   146  	//
   147  	// This must error out with "ambiguous column name" if there is more
   148  	// than one data source matching tn, "column not found" if there is
   149  	// none. The srcMeta and colHints are subsequently passed to
   150  	// Resolve() if resolution succeeds. The prefix will not be
   151  	// modified.
   152  	FindSourceProvidingColumn(ctx context.Context, col Name) (prefix *TableName, srcMeta ColumnSourceMeta, colHint int, err error)
   153  
   154  	// Resolve() is called if resolution succeeds.
   155  	Resolve(ctx context.Context, prefix *TableName, srcMeta ColumnSourceMeta, colHint int, col Name) (ColumnResolutionResult, error)
   156  }
   157  
   158  // ColumnSourceMeta is an opaque reference passed through column item resolution.
   159  type ColumnSourceMeta interface {
   160  	// ColumnSourcMeta is the interface anchor.
   161  	ColumnSourceMeta()
   162  }
   163  
   164  // ColumnResolutionResult is an opaque reference returned by ColumnItemResolver.Resolve().
   165  type ColumnResolutionResult interface {
   166  	// ColumnResolutionResult is the interface anchor.
   167  	ColumnResolutionResult()
   168  }
   169  
   170  // Resolve performs name resolution for a qualified star using a resolver.
   171  func (a *AllColumnsSelector) Resolve(
   172  	ctx context.Context, r ColumnItemResolver,
   173  ) (srcName *TableName, srcMeta ColumnSourceMeta, err error) {
   174  	prefix := a.TableName.ToTableName()
   175  
   176  	// Is there a data source with this prefix?
   177  	var res NumResolutionResults
   178  	res, srcName, srcMeta, err = r.FindSourceMatchingName(ctx, prefix)
   179  	if err != nil {
   180  		return nil, nil, err
   181  	}
   182  	if res == NoResults && a.TableName.NumParts == 2 {
   183  		// No, but name of form db.tbl.*?
   184  		// Special rule for compatibility with CockroachDB v1.x:
   185  		// search name db.public.tbl.* instead.
   186  		prefix.ExplicitCatalog = true
   187  		prefix.CatalogName = prefix.SchemaName
   188  		prefix.SchemaName = PublicSchemaName
   189  		res, srcName, srcMeta, err = r.FindSourceMatchingName(ctx, prefix)
   190  		if err != nil {
   191  			return nil, nil, err
   192  		}
   193  	}
   194  	if res == NoResults {
   195  		return nil, nil, newSourceNotFoundError("no data source matches pattern: %s", a)
   196  	}
   197  	return srcName, srcMeta, nil
   198  }
   199  
   200  // Resolve performs name resolution for a column item using a resolver.
   201  func (c *ColumnItem) Resolve(
   202  	ctx context.Context, r ColumnItemResolver,
   203  ) (ColumnResolutionResult, error) {
   204  	colName := c.ColumnName
   205  	if c.TableName == nil {
   206  		// Naked column name: simple case.
   207  		srcName, srcMeta, cHint, err := r.FindSourceProvidingColumn(ctx, colName)
   208  		if err != nil {
   209  			return nil, err
   210  		}
   211  		return r.Resolve(ctx, srcName, srcMeta, cHint, colName)
   212  	}
   213  
   214  	// There is a prefix. We need to search for it.
   215  	prefix := c.TableName.ToTableName()
   216  
   217  	// Is there a data source with this prefix?
   218  	res, srcName, srcMeta, err := r.FindSourceMatchingName(ctx, prefix)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  	if res == NoResults && c.TableName.NumParts == 2 {
   223  		// No, but name of form db.tbl.x?
   224  		// Special rule for compatibility with CockroachDB v1.x:
   225  		// search name db.public.tbl.x instead.
   226  		prefix.ExplicitCatalog = true
   227  		prefix.CatalogName = prefix.SchemaName
   228  		prefix.SchemaName = PublicSchemaName
   229  		res, srcName, srcMeta, err = r.FindSourceMatchingName(ctx, prefix)
   230  		if err != nil {
   231  			return nil, err
   232  		}
   233  	}
   234  	if res == NoResults {
   235  		return nil, newSourceNotFoundError("no data source matches prefix: %s", c.TableName)
   236  	}
   237  	return r.Resolve(ctx, srcName, srcMeta, -1, colName)
   238  }
   239  
   240  // ObjectNameTargetResolver is the helper interface to resolve object
   241  // names when the object is not expected to exist.
   242  type ObjectNameTargetResolver interface {
   243  	LookupSchema(ctx context.Context, dbName, scName string) (found bool, scMeta SchemaMeta, err error)
   244  }
   245  
   246  // SchemaMeta is an opaque reference returned by LookupSchema().
   247  type SchemaMeta interface {
   248  	// SchemaMeta is the interface anchor.
   249  	SchemaMeta()
   250  }
   251  
   252  // ObjectNameExistingResolver is the helper interface to resolve table
   253  // names when the object is expected to exist already. The boolean passed
   254  // is used to specify if a MutableTableDescriptor is to be returned in the
   255  // result.
   256  type ObjectNameExistingResolver interface {
   257  	LookupObject(ctx context.Context, flags ObjectLookupFlags, dbName, scName, obName string) (
   258  		found bool, objMeta NameResolutionResult, err error,
   259  	)
   260  }
   261  
   262  // NameResolutionResult is an opaque reference returned by LookupObject().
   263  type NameResolutionResult interface {
   264  	// NameResolutionResult is the interface anchor.
   265  	NameResolutionResult()
   266  }
   267  
   268  // ResolveExisting performs name resolution for an object name when
   269  // the target object is expected to exist already. It does not
   270  // mutate the input name. It additionally returns the resolved
   271  // prefix qualification for the object. For example, if the unresolved
   272  // name was "a.b" and the name was resolved to "a.public.b", the
   273  // prefix "a.public" is returned.
   274  func ResolveExisting(
   275  	ctx context.Context,
   276  	u *UnresolvedObjectName,
   277  	r ObjectNameExistingResolver,
   278  	lookupFlags ObjectLookupFlags,
   279  	curDb string,
   280  	searchPath sessiondata.SearchPath,
   281  ) (bool, ObjectNamePrefix, NameResolutionResult, error) {
   282  	namePrefix := ObjectNamePrefix{
   283  		SchemaName:      Name(u.Schema()),
   284  		ExplicitSchema:  u.HasExplicitSchema(),
   285  		CatalogName:     Name(u.Catalog()),
   286  		ExplicitCatalog: u.HasExplicitCatalog(),
   287  	}
   288  	if u.HasExplicitSchema() {
   289  		// pg_temp can be used as an alias for the current sessions temporary schema.
   290  		// We must perform this resolution before looking up the object. This
   291  		// resolution only succeeds if the session already has a temporary schema.
   292  		scName, err := searchPath.MaybeResolveTemporarySchema(u.Schema())
   293  		if err != nil {
   294  			return false, namePrefix, nil, err
   295  		}
   296  		if u.HasExplicitCatalog() {
   297  			// Already 3 parts: nothing to search. Delegate to the resolver.
   298  			namePrefix.CatalogName = Name(u.Catalog())
   299  			namePrefix.SchemaName = Name(u.Schema())
   300  			found, result, err := r.LookupObject(ctx, lookupFlags, u.Catalog(), scName, u.Object())
   301  			return found, namePrefix, result, err
   302  		}
   303  		// Two parts: D.T.
   304  		// Try to use the current database, and be satisfied if it's sufficient to find the object.
   305  		//
   306  		// Note: we test this even if curDb == "", because CockroachDB
   307  		// supports querying virtual schemas even when the current
   308  		// database is not set. For example, `select * from
   309  		// pg_catalog.pg_tables` is meant to show all tables across all
   310  		// databases when there is no current database set.
   311  
   312  		if found, objMeta, err := r.LookupObject(ctx, lookupFlags, curDb, scName, u.Object()); found || err != nil {
   313  			if err == nil {
   314  				namePrefix.CatalogName = Name(curDb)
   315  			}
   316  			return found, namePrefix, objMeta, err
   317  		}
   318  		// No luck so far. Compatibility with CockroachDB v1.1: try D.public.T instead.
   319  		if found, objMeta, err := r.LookupObject(ctx, lookupFlags, u.Schema(), PublicSchema, u.Object()); found || err != nil {
   320  			if err == nil {
   321  				namePrefix.CatalogName = Name(u.Schema())
   322  				namePrefix.SchemaName = PublicSchemaName
   323  				namePrefix.ExplicitCatalog = true
   324  			}
   325  			return found, namePrefix, objMeta, err
   326  		}
   327  		// Welp, really haven't found anything.
   328  		return false, namePrefix, nil, nil
   329  	}
   330  
   331  	// This is a naked table name. Use the search path.
   332  	iter := searchPath.Iter()
   333  	for next, ok := iter.Next(); ok; next, ok = iter.Next() {
   334  		if found, objMeta, err := r.LookupObject(ctx, lookupFlags, curDb, next, u.Object()); found || err != nil {
   335  			if err == nil {
   336  				namePrefix.CatalogName = Name(curDb)
   337  				namePrefix.SchemaName = Name(next)
   338  			}
   339  			return found, namePrefix, objMeta, err
   340  		}
   341  	}
   342  	return false, namePrefix, nil, nil
   343  }
   344  
   345  // ResolveTarget performs name resolution for an object name when
   346  // the target object is not expected to exist already. It does not
   347  // mutate the input name. It additionally returns the resolved
   348  // prefix qualification for the object. For example, if the unresolved
   349  // name was "a.b" and the name was resolved to "a.public.b", the
   350  // prefix "a.public" is returned.
   351  func ResolveTarget(
   352  	ctx context.Context,
   353  	u *UnresolvedObjectName,
   354  	r ObjectNameTargetResolver,
   355  	curDb string,
   356  	searchPath sessiondata.SearchPath,
   357  ) (found bool, namePrefix ObjectNamePrefix, scMeta SchemaMeta, err error) {
   358  	namePrefix = ObjectNamePrefix{
   359  		SchemaName:      Name(u.Schema()),
   360  		ExplicitSchema:  u.HasExplicitSchema(),
   361  		CatalogName:     Name(u.Catalog()),
   362  		ExplicitCatalog: u.HasExplicitCatalog(),
   363  	}
   364  	if u.HasExplicitSchema() {
   365  		// pg_temp can be used as an alias for the current sessions temporary schema.
   366  		// We must perform this resolution before looking up the object. This
   367  		// resolution only succeeds if the session already has a temporary schema.
   368  		scName, err := searchPath.MaybeResolveTemporarySchema(u.Schema())
   369  		if err != nil {
   370  			return false, namePrefix, nil, err
   371  		}
   372  		if u.HasExplicitCatalog() {
   373  			// Already 3 parts: nothing to do.
   374  			found, scMeta, err = r.LookupSchema(ctx, u.Catalog(), scName)
   375  			return found, namePrefix, scMeta, err
   376  		}
   377  		// Two parts: D.T.
   378  		// Try to use the current database, and be satisfied if it's sufficient to find the object.
   379  		if found, scMeta, err = r.LookupSchema(ctx, curDb, scName); found || err != nil {
   380  			if err == nil {
   381  				namePrefix.CatalogName = Name(curDb)
   382  			}
   383  			return found, namePrefix, scMeta, err
   384  		}
   385  		// No luck so far. Compatibility with CockroachDB v1.1: use D.public.T instead.
   386  		if found, scMeta, err = r.LookupSchema(ctx, u.Schema(), PublicSchema); found || err != nil {
   387  			if err == nil {
   388  				namePrefix.CatalogName = Name(u.Schema())
   389  				namePrefix.SchemaName = PublicSchemaName
   390  				namePrefix.ExplicitCatalog = true
   391  			}
   392  			return found, namePrefix, scMeta, err
   393  		}
   394  		// Welp, really haven't found anything.
   395  		return false, namePrefix, nil, nil
   396  	}
   397  
   398  	// This is a naked table name. Use the current schema = the first
   399  	// valid item in the search path.
   400  	iter := searchPath.IterWithoutImplicitPGSchemas()
   401  	for scName, ok := iter.Next(); ok; scName, ok = iter.Next() {
   402  		if found, scMeta, err = r.LookupSchema(ctx, curDb, scName); found || err != nil {
   403  			if err == nil {
   404  				namePrefix.CatalogName = Name(curDb)
   405  				namePrefix.SchemaName = Name(scName)
   406  			}
   407  			break
   408  		}
   409  	}
   410  	return found, namePrefix, scMeta, err
   411  }
   412  
   413  // Resolve is used for table prefixes. This is adequate for table
   414  // patterns with stars, e.g. AllTablesSelector.
   415  func (tp *ObjectNamePrefix) Resolve(
   416  	ctx context.Context, r ObjectNameTargetResolver, curDb string, searchPath sessiondata.SearchPath,
   417  ) (found bool, scMeta SchemaMeta, err error) {
   418  	if tp.ExplicitSchema {
   419  		// pg_temp can be used as an alias for the current sessions temporary schema.
   420  		// We must perform this resolution before looking up the object. This
   421  		// resolution only succeeds if the session already has a temporary schema.
   422  		scName, err := searchPath.MaybeResolveTemporarySchema(tp.Schema())
   423  		if err != nil {
   424  			return false, nil, err
   425  		}
   426  		if tp.ExplicitCatalog {
   427  			// Catalog name is explicit; nothing to do.
   428  			return r.LookupSchema(ctx, tp.Catalog(), scName)
   429  		}
   430  		// Try with the current database. This may be empty, because
   431  		// virtual schemas exist even when the db name is empty
   432  		// (CockroachDB extension).
   433  		if found, scMeta, err = r.LookupSchema(ctx, curDb, scName); found || err != nil {
   434  			if err == nil {
   435  				tp.CatalogName = Name(curDb)
   436  			}
   437  			return found, scMeta, err
   438  		}
   439  		// No luck so far. Compatibility with CockroachDB v1.1: use D.public.T instead.
   440  		if found, scMeta, err = r.LookupSchema(ctx, tp.Schema(), PublicSchema); found || err != nil {
   441  			if err == nil {
   442  				tp.CatalogName = tp.SchemaName
   443  				tp.SchemaName = PublicSchemaName
   444  				tp.ExplicitCatalog = true
   445  			}
   446  			return found, scMeta, err
   447  		}
   448  		// No luck.
   449  		return false, nil, nil
   450  	}
   451  	// This is a naked table name. Use the current schema = the first
   452  	// valid item in the search path.
   453  	iter := searchPath.IterWithoutImplicitPGSchemas()
   454  	for scName, ok := iter.Next(); ok; scName, ok = iter.Next() {
   455  		if found, scMeta, err = r.LookupSchema(ctx, curDb, scName); found || err != nil {
   456  			if err == nil {
   457  				tp.CatalogName = Name(curDb)
   458  				tp.SchemaName = Name(scName)
   459  			}
   460  			break
   461  		}
   462  	}
   463  	return found, scMeta, err
   464  }
   465  
   466  // ResolveFunction transforms an UnresolvedName to a FunctionDefinition.
   467  //
   468  // Function resolution currently takes a "short path" using the
   469  // assumption that there are no stored functions in the database. That
   470  // is, only functions in the (virtual) global namespace and virtual
   471  // schemas can be used. This in turn implies that the current
   472  // database does not matter and no resolver is needed.
   473  //
   474  // TODO(whoever): this needs to be revisited when there can be stored functions.
   475  // When that is the case, function names must be first normalized to e.g.
   476  // TableName (or whatever an object name will be called by then)
   477  // and then undergo regular name resolution via ResolveExisting(). When
   478  // that happens, the following function can be removed.
   479  func (n *UnresolvedName) ResolveFunction(
   480  	searchPath sessiondata.SearchPath,
   481  ) (*FunctionDefinition, error) {
   482  	if n.NumParts > 3 || len(n.Parts[0]) == 0 || n.Star {
   483  		// The Star part of the condition is really an assertion. The
   484  		// parser should not have let this star propagate to a point where
   485  		// this method is called.
   486  		return nil, pgerror.Newf(pgcode.InvalidName,
   487  			"invalid function name: %s", n)
   488  	}
   489  
   490  	// We ignore the catalog part. Like explained above, we currently
   491  	// only support functions in virtual schemas, which always exist
   492  	// independently of the database/catalog prefix.
   493  	function, prefix := n.Parts[0], n.Parts[1]
   494  
   495  	if d, ok := FunDefs[function]; ok && prefix == "" {
   496  		// Fast path: return early.
   497  		return d, nil
   498  	}
   499  
   500  	fullName := function
   501  
   502  	if prefix == sessiondata.PgCatalogName {
   503  		// If the user specified e.g. `pg_catalog.max()` we want to find
   504  		// it in the global namespace.
   505  		prefix = ""
   506  	}
   507  
   508  	if prefix != "" {
   509  		fullName = prefix + "." + function
   510  	}
   511  	def, ok := FunDefs[fullName]
   512  	if !ok {
   513  		found := false
   514  		if prefix == "" {
   515  			// The function wasn't qualified, so we must search for it via
   516  			// the search path first.
   517  			iter := searchPath.Iter()
   518  			for alt, ok := iter.Next(); ok; alt, ok = iter.Next() {
   519  				fullName = alt + "." + function
   520  				if def, ok = FunDefs[fullName]; ok {
   521  					found = true
   522  					break
   523  				}
   524  			}
   525  		}
   526  		if !found {
   527  			extraMsg := ""
   528  			// Try a little harder.
   529  			if rdef, ok := FunDefs[strings.ToLower(function)]; ok {
   530  				extraMsg = fmt.Sprintf(", but %s() exists", rdef.Name)
   531  			}
   532  			return nil, pgerror.Newf(
   533  				pgcode.UndefinedFunction, "unknown function: %s()%s", ErrString(n), extraMsg)
   534  		}
   535  	}
   536  
   537  	return def, nil
   538  }
   539  
   540  func newInvColRef(n *UnresolvedName) error {
   541  	return pgerror.NewWithDepthf(1, pgcode.InvalidColumnReference,
   542  		"invalid column name: %s", n)
   543  }
   544  
   545  func newInvTableNameError(n fmt.Stringer) error {
   546  	return pgerror.NewWithDepthf(1, pgcode.InvalidName,
   547  		"invalid table name: %s", n)
   548  }
   549  
   550  func newSourceNotFoundError(fmt string, args ...interface{}) error {
   551  	return pgerror.NewWithDepthf(1, pgcode.UndefinedTable, fmt, args...)
   552  }
   553  
   554  // CommonLookupFlags is the common set of flags for the various accessor interfaces.
   555  type CommonLookupFlags struct {
   556  	// if required is set, lookup will return an error if the item is not found.
   557  	Required bool
   558  	// if AvoidCached is set, lookup will avoid the cache (if any).
   559  	AvoidCached bool
   560  }
   561  
   562  // DatabaseLookupFlags is the flag struct suitable for GetDatabaseDesc().
   563  type DatabaseLookupFlags = CommonLookupFlags
   564  
   565  // DatabaseListFlags is the flag struct suitable for GetObjectNames().
   566  type DatabaseListFlags struct {
   567  	CommonLookupFlags
   568  	// ExplicitPrefix, when set, will cause the returned table names to
   569  	// have an explicit schema and catalog part.
   570  	ExplicitPrefix bool
   571  }
   572  
   573  // DesiredObjectKind represents what kind of object is desired in a name
   574  // resolution attempt.
   575  type DesiredObjectKind int
   576  
   577  const (
   578  	// TableObject is used when a table-like object is desired from resolution.
   579  	TableObject DesiredObjectKind = iota
   580  	// TypeObject is used when a type-like object is desired from resolution.
   581  	TypeObject
   582  )
   583  
   584  // NewQualifiedObjectName returns an ObjectName of the corresponding kind.
   585  // It is used mainly for constructing appropriate error messages depending
   586  // on what kind of object was requested.
   587  func NewQualifiedObjectName(catalog, schema, object string, kind DesiredObjectKind) ObjectName {
   588  	switch kind {
   589  	case TableObject:
   590  		name := MakeTableNameWithSchema(Name(catalog), Name(schema), Name(object))
   591  		return &name
   592  	case TypeObject:
   593  		name := MakeNewQualifiedTypeName(catalog, schema, object)
   594  		return &name
   595  	}
   596  	return nil
   597  }
   598  
   599  // ObjectLookupFlags is the flag struct suitable for GetObjectDesc().
   600  type ObjectLookupFlags struct {
   601  	CommonLookupFlags
   602  	// return a MutableTableDescriptor
   603  	RequireMutable         bool
   604  	IncludeOffline         bool
   605  	AllowWithoutPrimaryKey bool
   606  	// Control what type of object is being requested.
   607  	DesiredObjectKind DesiredObjectKind
   608  }
   609  
   610  // ObjectLookupFlagsWithRequired returns a default ObjectLookupFlags object
   611  // with just the Required flag true. This is a common configuration of the
   612  // flags.
   613  func ObjectLookupFlagsWithRequired() ObjectLookupFlags {
   614  	return ObjectLookupFlags{
   615  		CommonLookupFlags: CommonLookupFlags{Required: true},
   616  	}
   617  }