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 }