github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/optbuilder/util.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 optbuilder 12 13 import ( 14 "github.com/cockroachdb/cockroach/pkg/sql/opt" 15 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 16 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 17 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 18 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 19 "github.com/cockroachdb/cockroach/pkg/sql/privilege" 20 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 21 "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" 22 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 23 "github.com/cockroachdb/cockroach/pkg/sql/types" 24 "github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented" 25 "github.com/cockroachdb/errors" 26 ) 27 28 // windowAggregateFrame() returns a frame that any aggregate built as a window 29 // can use. 30 func windowAggregateFrame() memo.WindowFrame { 31 return memo.WindowFrame{ 32 StartBoundType: unboundedStartBound.BoundType, 33 EndBoundType: unboundedEndBound.BoundType, 34 } 35 } 36 37 // getTypedExprs casts the exprs into TypedExps and returns them. 38 func getTypedExprs(exprs []tree.Expr) []tree.TypedExpr { 39 argExprs := make([]tree.TypedExpr, len(exprs)) 40 for i, expr := range exprs { 41 argExprs[i] = expr.(tree.TypedExpr) 42 } 43 return argExprs 44 } 45 46 // expandStar expands expr into a list of columns if expr 47 // corresponds to a "*", "<table>.*" or "(Expr).*". 48 func (b *Builder) expandStar( 49 expr tree.Expr, inScope *scope, 50 ) (aliases []string, exprs []tree.TypedExpr) { 51 if b.insideViewDef { 52 panic(unimplemented.NewWithIssue(10028, "views do not currently support * expressions")) 53 } 54 switch t := expr.(type) { 55 case *tree.TupleStar: 56 texpr := inScope.resolveType(t.Expr, types.Any) 57 typ := texpr.ResolvedType() 58 if typ.Family() != types.TupleFamily { 59 panic(tree.NewTypeIsNotCompositeError(typ)) 60 } 61 62 // If the sub-expression is a tuple constructor, we'll de-tuplify below. 63 // Otherwise we'll re-evaluate the expression multiple times. 64 // 65 // The following query generates a tuple constructor: 66 // SELECT (kv.*).* FROM kv 67 // -- the inner star expansion (scope.VisitPre) first expands to 68 // SELECT (((kv.k, kv.v) as k,v)).* FROM kv 69 // -- then the inner tuple constructor detuplifies here to: 70 // SELECT kv.k, kv.v FROM kv 71 // 72 // The following query generates a scalar var with tuple type that 73 // is not a tuple constructor: 74 // 75 // SELECT (SELECT pg_get_keywords() AS x LIMIT 1).* 76 // -- does not detuplify, one gets instead: 77 // SELECT (SELECT pg_get_keywords() AS x LIMIT 1).word, 78 // (SELECT pg_get_keywords() AS x LIMIT 1).catcode, 79 // (SELECT pg_get_keywords() AS x LIMIT 1).catdesc 80 // -- (and we hope a later opt will merge the subqueries) 81 tTuple, isTuple := texpr.(*tree.Tuple) 82 83 aliases = typ.TupleLabels() 84 exprs = make([]tree.TypedExpr, len(typ.TupleContents())) 85 for i := range typ.TupleContents() { 86 if isTuple { 87 // De-tuplify: ((a,b,c)).* -> a, b, c 88 exprs[i] = tTuple.Exprs[i].(tree.TypedExpr) 89 } else { 90 // Can't de-tuplify: 91 // either (Expr).* -> (Expr).a, (Expr).b, (Expr).c if there are enough 92 // labels, or (Expr).* -> (Expr).@1, (Expr).@2, (Expr).@3 if labels are 93 // missing. 94 // 95 // We keep the labels if available so that the column name 96 // generation still produces column label "x" for, e.g. (E).x. 97 colName := "" 98 if i < len(aliases) { 99 colName = aliases[i] 100 } 101 // NewTypedColumnAccessExpr expects colName to be empty if the tuple 102 // should be accessed by index. 103 exprs[i] = tree.NewTypedColumnAccessExpr(texpr, colName, i) 104 } 105 } 106 for i := len(aliases); i < len(typ.TupleContents()); i++ { 107 // Add aliases for all the non-named columns in the tuple. 108 aliases = append(aliases, "?column?") 109 } 110 111 case *tree.AllColumnsSelector: 112 src, srcMeta, err := t.Resolve(b.ctx, inScope) 113 if err != nil { 114 panic(err) 115 } 116 refScope := srcMeta.(*scope) 117 exprs = make([]tree.TypedExpr, 0, len(refScope.cols)) 118 aliases = make([]string, 0, len(refScope.cols)) 119 for i := range refScope.cols { 120 col := &refScope.cols[i] 121 if col.table == *src && !col.hidden { 122 exprs = append(exprs, col) 123 aliases = append(aliases, string(col.name)) 124 } 125 } 126 127 case tree.UnqualifiedStar: 128 if len(inScope.cols) == 0 { 129 panic(pgerror.Newf(pgcode.InvalidName, 130 "cannot use %q without a FROM clause", tree.ErrString(expr))) 131 } 132 exprs = make([]tree.TypedExpr, 0, len(inScope.cols)) 133 aliases = make([]string, 0, len(inScope.cols)) 134 for i := range inScope.cols { 135 col := &inScope.cols[i] 136 if !col.hidden { 137 exprs = append(exprs, col) 138 aliases = append(aliases, string(col.name)) 139 } 140 } 141 142 default: 143 panic(errors.AssertionFailedf("unhandled type: %T", expr)) 144 } 145 146 return aliases, exprs 147 } 148 149 // expandStarAndResolveType expands expr into a list of columns if 150 // expr corresponds to a "*", "<table>.*" or "(Expr).*". Otherwise, 151 // expandStarAndResolveType resolves the type of expr and returns it 152 // as a []TypedExpr. 153 func (b *Builder) expandStarAndResolveType( 154 expr tree.Expr, inScope *scope, 155 ) (exprs []tree.TypedExpr) { 156 switch t := expr.(type) { 157 case *tree.AllColumnsSelector, tree.UnqualifiedStar, *tree.TupleStar: 158 _, exprs = b.expandStar(expr, inScope) 159 160 case *tree.UnresolvedName: 161 vn, err := t.NormalizeVarName() 162 if err != nil { 163 panic(err) 164 } 165 return b.expandStarAndResolveType(vn, inScope) 166 167 default: 168 texpr := inScope.resolveType(t, types.Any) 169 exprs = []tree.TypedExpr{texpr} 170 } 171 172 return exprs 173 } 174 175 // synthesizeColumn is used to synthesize new columns. This is needed for 176 // operations such as projection of scalar expressions and aggregations. For 177 // example, the query `SELECT (x + 1) AS "x_incr" FROM t` has a projection with 178 // a synthesized column "x_incr". 179 // 180 // scope The scope is passed in so it can can be updated with the newly bound 181 // variable. 182 // alias This is an optional alias for the new column (e.g., if specified with 183 // the AS keyword). 184 // typ The type of the column. 185 // expr The expression this column refers to (if any). 186 // scalar The scalar expression associated with this column (if any). 187 // 188 // The new column is returned as a scopeColumn object. 189 func (b *Builder) synthesizeColumn( 190 scope *scope, alias string, typ *types.T, expr tree.TypedExpr, scalar opt.ScalarExpr, 191 ) *scopeColumn { 192 name := tree.Name(alias) 193 colID := b.factory.Metadata().AddColumn(alias, typ) 194 scope.cols = append(scope.cols, scopeColumn{ 195 name: name, 196 typ: typ, 197 id: colID, 198 expr: expr, 199 scalar: scalar, 200 }) 201 return &scope.cols[len(scope.cols)-1] 202 } 203 204 // populateSynthesizedColumn is similar to synthesizeColumn, but it fills in 205 // the given existing column rather than allocating a new one. 206 func (b *Builder) populateSynthesizedColumn(col *scopeColumn, scalar opt.ScalarExpr) { 207 colID := b.factory.Metadata().AddColumn(string(col.name), col.typ) 208 col.id = colID 209 col.scalar = scalar 210 } 211 212 // projectColumn projects src by copying its column ID to dst. projectColumn 213 // also copies src.name to dst if an alias is not already set in dst. No other 214 // fields are copied, for the following reasons: 215 // - We don't copy group, as dst becomes a pass-through column in the new 216 // scope. dst already has group=0, so keep it as-is. 217 // - We don't copy hidden, because projecting a column makes it visible. 218 // dst already has hidden=false, so keep it as-is. 219 // - We don't copy table, since the table becomes anonymous in the new scope. 220 // - We don't copy descending, since we don't want to overwrite dst.descending 221 // if dst is an ORDER BY column. 222 // - expr, exprStr and typ in dst already correspond to the expression and type 223 // of the src column. 224 func (b *Builder) projectColumn(dst *scopeColumn, src *scopeColumn) { 225 if dst.name == "" { 226 dst.name = src.name 227 } 228 dst.id = src.id 229 } 230 231 // shouldCreateDefaultColumn decides if we need to create a default 232 // column and default label for a function expression. 233 // Returns true if the function's return type is not an empty tuple and 234 // doesn't declare any tuple labels. 235 func (b *Builder) shouldCreateDefaultColumn(texpr tree.TypedExpr) bool { 236 if texpr.ResolvedType() == types.EmptyTuple { 237 // This is only to support crdb_internal.unary_table(). 238 return false 239 } 240 241 // We need to create a default column with a default name when 242 // the function return type doesn't declare any return labels. 243 return len(texpr.ResolvedType().TupleLabels()) == 0 244 } 245 246 // addColumn adds a column to scope with the given alias, type, and 247 // expression. It returns a pointer to the new column. The column ID and group 248 // are left empty so they can be filled in later. 249 func (b *Builder) addColumn(scope *scope, alias string, expr tree.TypedExpr) *scopeColumn { 250 name := tree.Name(alias) 251 scope.cols = append(scope.cols, scopeColumn{ 252 name: name, 253 typ: expr.ResolvedType(), 254 expr: expr, 255 }) 256 return &scope.cols[len(scope.cols)-1] 257 } 258 259 func (b *Builder) synthesizeResultColumns(scope *scope, cols sqlbase.ResultColumns) { 260 for i := range cols { 261 c := b.synthesizeColumn(scope, cols[i].Name, cols[i].Typ, nil /* expr */, nil /* scalar */) 262 if cols[i].Hidden { 263 c.hidden = true 264 } 265 } 266 } 267 268 // colIndex takes an expression that refers to a column using an integer, 269 // verifies it refers to a valid target in the SELECT list, and returns the 270 // corresponding column index. For example: 271 // SELECT a from T ORDER by 1 272 // Here "1" refers to the first item in the SELECT list, "a". The returned 273 // index is 0. 274 func colIndex(numOriginalCols int, expr tree.Expr, context string) int { 275 ord := int64(-1) 276 switch i := expr.(type) { 277 case *tree.NumVal: 278 if i.ShouldBeInt64() { 279 val, err := i.AsInt64() 280 if err != nil { 281 panic(err) 282 } 283 ord = val 284 } else { 285 panic(pgerror.Newf( 286 pgcode.Syntax, 287 "non-integer constant in %s: %s", context, expr, 288 )) 289 } 290 case *tree.DInt: 291 if *i >= 0 { 292 ord = int64(*i) 293 } 294 case *tree.StrVal: 295 panic(pgerror.Newf( 296 pgcode.Syntax, "non-integer constant in %s: %s", context, expr, 297 )) 298 case tree.Datum: 299 panic(pgerror.Newf( 300 pgcode.Syntax, "non-integer constant in %s: %s", context, expr, 301 )) 302 } 303 if ord != -1 { 304 if ord < 1 || ord > int64(numOriginalCols) { 305 panic(pgerror.Newf( 306 pgcode.InvalidColumnReference, 307 "%s position %s is not in select list", context, expr, 308 )) 309 } 310 ord-- 311 } 312 return int(ord) 313 } 314 315 // colIdxByProjectionAlias returns the corresponding index in columns of an expression 316 // that may refer to a column alias. 317 // If there are no aliases in columns that expr refers to, then -1 is returned. 318 // This method is pertinent to ORDER BY and DISTINCT ON clauses that may refer 319 // to a column alias. 320 func colIdxByProjectionAlias(expr tree.Expr, op string, scope *scope) int { 321 index := -1 322 323 if vBase, ok := expr.(tree.VarName); ok { 324 v, err := vBase.NormalizeVarName() 325 if err != nil { 326 panic(err) 327 } 328 329 if c, ok := v.(*tree.ColumnItem); ok && c.TableName == nil { 330 // Look for an output column that matches the name. This 331 // handles cases like: 332 // 333 // SELECT a AS b FROM t ORDER BY b 334 // SELECT DISTINCT ON (b) a AS b FROM t 335 target := c.ColumnName 336 for j := range scope.cols { 337 col := &scope.cols[j] 338 if col.name != target { 339 continue 340 } 341 342 if col.mutation { 343 panic(makeBackfillError(col.name)) 344 } 345 346 if index != -1 { 347 // There is more than one projection alias that matches the clause. 348 // Here, SQL92 is specific as to what should be done: if the 349 // underlying expression is known and it is equivalent, then just 350 // accept that and ignore the ambiguity. This plays nice with 351 // `SELECT b, * FROM t ORDER BY b`. Otherwise, reject with an 352 // ambiguity error. 353 if scope.cols[j].getExprStr() != scope.cols[index].getExprStr() { 354 panic(pgerror.Newf(pgcode.AmbiguousAlias, 355 "%s \"%s\" is ambiguous", op, target)) 356 } 357 // Use the index of the first matching column. 358 continue 359 } 360 index = j 361 } 362 } 363 } 364 365 return index 366 } 367 368 // makeBackfillError returns an error indicating that the column of the given 369 // name is currently being backfilled and cannot be referenced. 370 func makeBackfillError(name tree.Name) error { 371 return pgerror.Newf(pgcode.InvalidColumnReference, 372 "column %q is being backfilled", tree.ErrString(&name)) 373 } 374 375 // flattenTuples extracts the members of tuples into a list of columns. 376 func flattenTuples(exprs []tree.TypedExpr) []tree.TypedExpr { 377 // We want to avoid allocating new slices unless strictly necessary. 378 var newExprs []tree.TypedExpr 379 for i, e := range exprs { 380 if t, ok := e.(*tree.Tuple); ok { 381 if newExprs == nil { 382 // All right, it was necessary to allocate the slices after all. 383 newExprs = make([]tree.TypedExpr, i, len(exprs)) 384 copy(newExprs, exprs[:i]) 385 } 386 387 newExprs = flattenTuple(t, newExprs) 388 } else if newExprs != nil { 389 newExprs = append(newExprs, e) 390 } 391 } 392 if newExprs != nil { 393 return newExprs 394 } 395 return exprs 396 } 397 398 // flattenTuple recursively extracts the members of a tuple into a list of 399 // expressions. 400 func flattenTuple(t *tree.Tuple, exprs []tree.TypedExpr) []tree.TypedExpr { 401 for _, e := range t.Exprs { 402 if eT, ok := e.(*tree.Tuple); ok { 403 exprs = flattenTuple(eT, exprs) 404 } else { 405 expr := e.(tree.TypedExpr) 406 exprs = append(exprs, expr) 407 } 408 } 409 return exprs 410 } 411 412 // symbolicExprStr returns a string representation of the expression using 413 // symbolic notation. Because the symbolic notation disambiguates columns, this 414 // string can be used to determine if two expressions are equivalent. 415 func symbolicExprStr(expr tree.Expr) string { 416 return tree.AsStringWithFlags(expr, tree.FmtCheckEquivalence) 417 } 418 419 func colsToColList(cols []scopeColumn) opt.ColList { 420 colList := make(opt.ColList, len(cols)) 421 for i := range cols { 422 colList[i] = cols[i].id 423 } 424 return colList 425 } 426 427 // resolveAndBuildScalar is used to build a scalar with a required type. 428 func (b *Builder) resolveAndBuildScalar( 429 expr tree.Expr, 430 requiredType *types.T, 431 context exprKind, 432 flags tree.SemaRejectFlags, 433 inScope *scope, 434 ) opt.ScalarExpr { 435 // We need to save and restore the previous value of the field in 436 // semaCtx in case we are recursively called within a subquery 437 // context. 438 defer b.semaCtx.Properties.Restore(b.semaCtx.Properties) 439 b.semaCtx.Properties.Require(context.String(), flags) 440 441 inScope.context = context 442 texpr := inScope.resolveAndRequireType(expr, requiredType) 443 return b.buildScalar(texpr, inScope, nil, nil, nil) 444 } 445 446 // In Postgres, qualifying an object name with pg_temp is equivalent to explicitly 447 // specifying TEMP/TEMPORARY in the CREATE syntax. resolveTemporaryStatus returns 448 // true if either(or both) of these conditions are true. 449 func resolveTemporaryStatus(name *tree.TableName, explicitTemp bool) bool { 450 // An explicit schema can only be provided in the CREATE TEMP TABLE statement 451 // iff it is pg_temp. 452 if explicitTemp && name.ExplicitSchema && name.SchemaName != sessiondata.PgTempSchemaName { 453 panic(pgerror.New(pgcode.InvalidTableDefinition, "cannot create temporary relation in non-temporary schema")) 454 } 455 return name.SchemaName == sessiondata.PgTempSchemaName || explicitTemp 456 } 457 458 // resolveSchemaForCreate returns the schema that will contain a newly created 459 // catalog object with the given name. If the current user does not have the 460 // CREATE privilege, then resolveSchemaForCreate raises an error. 461 func (b *Builder) resolveSchemaForCreate(name *tree.TableName) (cat.Schema, cat.SchemaName) { 462 flags := cat.Flags{AvoidDescriptorCaches: true} 463 sch, resName, err := b.catalog.ResolveSchema(b.ctx, flags, &name.ObjectNamePrefix) 464 if err != nil { 465 // Remap invalid schema name error text so that it references the catalog 466 // object that could not be created. 467 if code := pgerror.GetPGCode(err); code == pgcode.InvalidSchemaName { 468 var newErr error 469 newErr = pgerror.Newf(pgcode.InvalidSchemaName, 470 "cannot create %q because the target database or schema does not exist", 471 tree.ErrString(name)) 472 newErr = errors.WithSecondaryError(newErr, err) 473 newErr = errors.WithHint(newErr, "verify that the current database and search_path are valid and/or the target database exists") 474 panic(newErr) 475 } 476 panic(err) 477 } 478 479 // Only allow creation of objects in the public schema. 480 if resName.Schema() != tree.PublicSchema { 481 panic(pgerror.Newf(pgcode.InvalidName, 482 "schema cannot be modified: %q", tree.ErrString(&resName))) 483 } 484 485 if err := b.catalog.CheckPrivilege(b.ctx, sch, privilege.CREATE); err != nil { 486 panic(err) 487 } 488 489 return sch, resName 490 } 491 492 // resolveTableForMutation is a helper method for building mutations. It returns 493 // the table in the catalog that matches the given TableExpr, along with the 494 // table's MDDepName and alias, and the IDs of any columns explicitly specified 495 // by the TableExpr (see tree.TableRef). 496 // 497 // If the name does not resolve to a table, then resolveTableForMutation raises 498 // an error. Privileges are checked when resolving the table, and an error is 499 // raised if the current user does not have the given privilege. 500 func (b *Builder) resolveTableForMutation( 501 n tree.TableExpr, priv privilege.Kind, 502 ) (tab cat.Table, depName opt.MDDepName, alias tree.TableName, columns []tree.ColumnID) { 503 // Strip off an outer AliasedTableExpr if there is one. 504 var outerAlias *tree.TableName 505 if ate, ok := n.(*tree.AliasedTableExpr); ok { 506 n = ate.Expr 507 // It's okay to ignore the As columns here, as they're not permitted in 508 // DML aliases where this function is used. The grammar does not allow 509 // them, so the parser would have reported an error if they were present. 510 if ate.As.Alias != "" { 511 outerAlias = tree.NewUnqualifiedTableName(ate.As.Alias) 512 } 513 } 514 515 switch t := n.(type) { 516 case *tree.TableName: 517 tab, alias = b.resolveTable(t, priv) 518 depName = opt.DepByName(t) 519 520 case *tree.TableRef: 521 tab = b.resolveTableRef(t, priv) 522 alias = tree.MakeUnqualifiedTableName(t.As.Alias) 523 depName = opt.DepByID(cat.StableID(t.TableID)) 524 525 // See tree.TableRef: "Note that a nil [Columns] array means 'unspecified' 526 // (all columns). whereas an array of length 0 means 'zero columns'. 527 // Lists of zero columns are not supported and will throw an error." 528 if t.Columns != nil && len(t.Columns) == 0 { 529 panic(pgerror.Newf(pgcode.Syntax, 530 "an explicit list of column IDs must include at least one column")) 531 } 532 columns = t.Columns 533 534 default: 535 panic(pgerror.Newf(pgcode.WrongObjectType, 536 "%q does not resolve to a table", tree.ErrString(n))) 537 } 538 539 if outerAlias != nil { 540 alias = *outerAlias 541 } 542 543 return tab, depName, alias, columns 544 } 545 546 // resolveTable returns the table in the catalog with the given name. If the 547 // name does not resolve to a table, or if the current user does not have the 548 // given privilege, then resolveTable raises an error. 549 func (b *Builder) resolveTable( 550 tn *tree.TableName, priv privilege.Kind, 551 ) (cat.Table, tree.TableName) { 552 ds, resName := b.resolveDataSource(tn, priv) 553 tab, ok := ds.(cat.Table) 554 if !ok { 555 panic(sqlbase.NewWrongObjectTypeError(tn, "table")) 556 } 557 return tab, resName 558 } 559 560 // resolveTableRef returns the table in the catalog that matches the given 561 // TableRef spec. If the name does not resolve to a table, or if the current 562 // user does not have the given privilege, then resolveTableRef raises an error. 563 func (b *Builder) resolveTableRef(ref *tree.TableRef, priv privilege.Kind) cat.Table { 564 ds := b.resolveDataSourceRef(ref, priv) 565 tab, ok := ds.(cat.Table) 566 if !ok { 567 panic(sqlbase.NewWrongObjectTypeError(ref, "table")) 568 } 569 return tab 570 } 571 572 // resolveDataSource returns the data source in the catalog with the given name. 573 // If the name does not resolve to a table, or if the current user does not have 574 // the given privilege, then resolveDataSource raises an error. 575 // 576 // If the b.qualifyDataSourceNamesInAST flag is set, tn is updated to contain 577 // the fully qualified name. 578 func (b *Builder) resolveDataSource( 579 tn *tree.TableName, priv privilege.Kind, 580 ) (cat.DataSource, cat.DataSourceName) { 581 var flags cat.Flags 582 if b.insideViewDef { 583 // Avoid taking table leases when we're creating a view. 584 flags.AvoidDescriptorCaches = true 585 } 586 ds, resName, err := b.catalog.ResolveDataSource(b.ctx, flags, tn) 587 if err != nil { 588 panic(err) 589 } 590 b.checkPrivilege(opt.DepByName(tn), ds, priv) 591 592 if b.qualifyDataSourceNamesInAST { 593 *tn = resName 594 tn.ExplicitCatalog = true 595 tn.ExplicitSchema = true 596 } 597 return ds, resName 598 } 599 600 // resolveDataSourceFromRef returns the data source in the catalog that matches 601 // the given TableRef spec. If no data source matches, or if the current user 602 // does not have the given privilege, then resolveDataSourceFromRef raises an 603 // error. 604 func (b *Builder) resolveDataSourceRef(ref *tree.TableRef, priv privilege.Kind) cat.DataSource { 605 var flags cat.Flags 606 if b.insideViewDef { 607 // Avoid taking table leases when we're creating a view. 608 flags.AvoidDescriptorCaches = true 609 } 610 ds, _, err := b.catalog.ResolveDataSourceByID(b.ctx, flags, cat.StableID(ref.TableID)) 611 if err != nil { 612 panic(pgerror.Wrapf(err, pgcode.UndefinedObject, "%s", tree.ErrString(ref))) 613 } 614 b.checkPrivilege(opt.DepByID(cat.StableID(ref.TableID)), ds, priv) 615 return ds 616 } 617 618 // checkPrivilege ensures that the current user has the privilege needed to 619 // access the given object in the catalog. If not, then checkPrivilege raises an 620 // error. It also adds the object and it's original unresolved name as a 621 // dependency to the metadata, so that the privileges can be re-checked on reuse 622 // of the memo. 623 func (b *Builder) checkPrivilege(name opt.MDDepName, ds cat.DataSource, priv privilege.Kind) { 624 if !(priv == privilege.SELECT && b.skipSelectPrivilegeChecks) { 625 err := b.catalog.CheckPrivilege(b.ctx, ds, priv) 626 if err != nil { 627 panic(err) 628 } 629 } else { 630 // The check is skipped, so don't recheck when dependencies are checked. 631 priv = 0 632 } 633 634 // Add dependency on this object to the metadata, so that the metadata can be 635 // cached and later checked for freshness. 636 b.factory.Metadata().AddDependency(name, ds, priv) 637 }