github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/virtual_schema.go (about) 1 // Copyright 2016 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 sql 12 13 import ( 14 "context" 15 "math" 16 "sort" 17 18 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 19 "github.com/cockroachdb/cockroach/pkg/sql/catalog" 20 "github.com/cockroachdb/cockroach/pkg/sql/opt/constraint" 21 "github.com/cockroachdb/cockroach/pkg/sql/parser" 22 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 23 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 24 "github.com/cockroachdb/cockroach/pkg/sql/privilege" 25 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 26 "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" 27 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 28 "github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented" 29 "github.com/cockroachdb/cockroach/pkg/util/hlc" 30 "github.com/cockroachdb/errors" 31 ) 32 33 // 34 // Programmer interface to define virtual schemas. 35 // 36 37 // virtualSchema represents a database with a set of virtual tables. Virtual 38 // tables differ from standard tables in that they are not persisted to storage, 39 // and instead their contents are populated whenever they are queried. 40 // 41 // The virtual database and its virtual tables also differ from standard databases 42 // and tables in that their descriptors are not distributed, but instead live statically 43 // in code. This means that they are accessed separately from standard descriptors. 44 type virtualSchema struct { 45 name string 46 allTableNames map[string]struct{} 47 tableDefs map[sqlbase.ID]virtualSchemaDef 48 tableValidator func(*sqlbase.TableDescriptor) error // optional 49 // Some virtual tables can be used if there is no current database set; others can't. 50 validWithNoDatabaseContext bool 51 // Some virtual schemas (like pg_catalog) contain types that we can resolve. 52 containsTypes bool 53 } 54 55 // virtualSchemaDef represents the interface of a table definition within a virtualSchema. 56 type virtualSchemaDef interface { 57 getSchema() string 58 initVirtualTableDesc( 59 ctx context.Context, st *cluster.Settings, parentSchemaID, id sqlbase.ID, 60 ) (sqlbase.TableDescriptor, error) 61 getComment() string 62 } 63 64 type virtualIndex struct { 65 // populate populates the table given the constraint. matched is true if any 66 // rows were generated. 67 populate func(ctx context.Context, constraint tree.Datum, p *planner, db *DatabaseDescriptor, 68 addRow func(...tree.Datum) error, 69 ) (matched bool, err error) 70 71 // partial is true if the virtual index isn't able to satisfy all constraints. 72 // For example, the pg_class table contains both indexes and tables. Tables 73 // can be looked up via a virtual index, since we can look up their descriptor 74 // by their ID directly. But indexes can't - they're hashed identifiers with 75 // no actual index. So we mark this index as partial, and if we get no match 76 // during populate, we'll fall back on populating the entire table. 77 partial bool 78 } 79 80 // virtualSchemaTable represents a table within a virtualSchema. 81 type virtualSchemaTable struct { 82 // Exactly one of the populate and generator fields should be defined for 83 // each virtualSchemaTable. 84 schema string 85 86 // comment represents comment of virtual schema table. 87 comment string 88 89 // populate, if non-nil, is a function that is used when creating a 90 // valuesNode. This function eagerly loads every row of the virtual table 91 // during initialization of the valuesNode. 92 populate func(ctx context.Context, p *planner, db *DatabaseDescriptor, addRow func(...tree.Datum) error) error 93 94 // indexes, if non empty, is a slice of populate methods that also take a 95 // constraint, only generating rows that match the constraint. The order of 96 // indexes must match the order of the index definitions in the virtual table's 97 // schema. 98 indexes []virtualIndex 99 100 // generator, if non-nil, is a function that is used when creating a 101 // virtualTableNode. This function returns a virtualTableGenerator function 102 // which generates the next row of the virtual table when called. 103 generator func(ctx context.Context, p *planner, db *DatabaseDescriptor) (virtualTableGenerator, cleanupFunc, error) 104 } 105 106 // virtualSchemaView represents a view within a virtualSchema 107 type virtualSchemaView struct { 108 schema string 109 resultColumns sqlbase.ResultColumns 110 } 111 112 // getSchema is part of the virtualSchemaDef interface. 113 func (t virtualSchemaTable) getSchema() string { 114 return t.schema 115 } 116 117 // initVirtualTableDesc is part of the virtualSchemaDef interface. 118 func (t virtualSchemaTable) initVirtualTableDesc( 119 ctx context.Context, st *cluster.Settings, parentSchemaID, id sqlbase.ID, 120 ) (sqlbase.TableDescriptor, error) { 121 stmt, err := parser.ParseOne(t.schema) 122 if err != nil { 123 return sqlbase.TableDescriptor{}, err 124 } 125 126 create := stmt.AST.(*tree.CreateTable) 127 var firstColDef *tree.ColumnTableDef 128 for _, def := range create.Defs { 129 if d, ok := def.(*tree.ColumnTableDef); ok { 130 if d.HasDefaultExpr() { 131 return sqlbase.TableDescriptor{}, 132 errors.Errorf("virtual tables are not allowed to use default exprs "+ 133 "because bootstrapping: %s:%s", &create.Table, d.Name) 134 } 135 if firstColDef == nil { 136 firstColDef = d 137 } 138 } 139 if _, ok := def.(*tree.UniqueConstraintTableDef); ok { 140 return sqlbase.TableDescriptor{}, 141 errors.Errorf("virtual tables are not allowed to have unique constraints") 142 } 143 } 144 if firstColDef == nil { 145 return sqlbase.TableDescriptor{}, 146 errors.Errorf("can't have empty virtual tables") 147 } 148 149 // Virtual tables never use SERIAL so we need not process SERIAL 150 // types here. 151 mutDesc, err := MakeTableDesc( 152 ctx, 153 nil, /* txn */ 154 nil, /* vs */ 155 st, 156 create, 157 0, /* parentID */ 158 parentSchemaID, 159 id, 160 hlc.Timestamp{}, /* creationTime */ 161 publicSelectPrivileges, 162 nil, /* affected */ 163 nil, /* semaCtx */ 164 nil, /* evalCtx */ 165 &sessiondata.SessionData{}, /* sessionData */ 166 false, /* temporary */ 167 ) 168 if err != nil { 169 return mutDesc.TableDescriptor, err 170 } 171 for i := range mutDesc.Indexes { 172 idx := &mutDesc.Indexes[i] 173 if len(idx.ColumnIDs) > 1 { 174 panic("we don't know how to deal with virtual composite indexes yet") 175 } 176 // All indexes of virtual tables automatically STORE all other columns in 177 // the table. 178 idx.StoreColumnIDs = make([]sqlbase.ColumnID, len(mutDesc.Columns)-len(idx.ColumnIDs)) 179 idx.StoreColumnNames = make([]string, len(mutDesc.Columns)-len(idx.ColumnIDs)) 180 // Store all columns but the ones in the index. 181 outputIdx := 0 182 EACHCOLUMN: 183 for j := range mutDesc.Columns { 184 for _, id := range idx.ColumnIDs { 185 if mutDesc.Columns[j].ID == id { 186 // Skip columns in the index. 187 continue EACHCOLUMN 188 } 189 } 190 idx.StoreColumnIDs[outputIdx] = mutDesc.Columns[j].ID 191 idx.StoreColumnNames[outputIdx] = mutDesc.Columns[j].Name 192 outputIdx++ 193 } 194 } 195 return mutDesc.TableDescriptor, nil 196 } 197 198 // getComment is part of the virtualSchemaDef interface. 199 func (t virtualSchemaTable) getComment() string { 200 return t.comment 201 } 202 203 // getIndex returns the virtual index with the input ID. 204 func (t virtualSchemaTable) getIndex(id sqlbase.IndexID) *virtualIndex { 205 // Subtract 2 from the index id to get the ordinal in def.indexes, since 206 // the index with ID 1 is the "primary" index defined by def.populate. 207 return &t.indexes[id-2] 208 } 209 210 // getSchema is part of the virtualSchemaDef interface. 211 func (v virtualSchemaView) getSchema() string { 212 return v.schema 213 } 214 215 // initVirtualTableDesc is part of the virtualSchemaDef interface. 216 func (v virtualSchemaView) initVirtualTableDesc( 217 ctx context.Context, st *cluster.Settings, parentSchemaID sqlbase.ID, id sqlbase.ID, 218 ) (sqlbase.TableDescriptor, error) { 219 stmt, err := parser.ParseOne(v.schema) 220 if err != nil { 221 return sqlbase.TableDescriptor{}, err 222 } 223 224 create := stmt.AST.(*tree.CreateView) 225 226 columns := v.resultColumns 227 if len(create.ColumnNames) != 0 { 228 columns = overrideColumnNames(columns, create.ColumnNames) 229 } 230 mutDesc, err := makeViewTableDesc( 231 ctx, 232 create.Name.Table(), 233 tree.AsStringWithFlags(create.AsSource, tree.FmtParsable), 234 0, /* parentID */ 235 parentSchemaID, 236 id, 237 columns, 238 hlc.Timestamp{}, /* creationTime */ 239 publicSelectPrivileges, 240 nil, /* semaCtx */ 241 nil, /* evalCtx */ 242 false, /* temporary */ 243 ) 244 return mutDesc.TableDescriptor, err 245 } 246 247 // getComment is part of the virtualSchemaDef interface. 248 func (v virtualSchemaView) getComment() string { 249 return "" 250 } 251 252 // virtualSchemas holds a slice of statically registered virtualSchema objects. 253 // 254 // When adding a new virtualSchema, define a virtualSchema in a separate file, and 255 // add that object to this slice. 256 var virtualSchemas = map[sqlbase.ID]virtualSchema{ 257 sqlbase.InformationSchemaID: informationSchema, 258 sqlbase.PgCatalogID: pgCatalog, 259 sqlbase.CrdbInternalID: crdbInternal, 260 sqlbase.PgExtensionSchemaID: pgExtension, 261 } 262 263 // 264 // SQL-layer interface to work with virtual schemas. 265 // 266 267 // VirtualSchemaHolder is a type used to provide convenient access to virtual 268 // database and table descriptors. VirtualSchemaHolder, virtualSchemaEntry, 269 // and virtualDefEntry make up the generated data structure which the 270 // virtualSchemas slice is mapped to. Because of this, they should not be 271 // created directly, but instead will be populated in a post-startup hook 272 // on an Executor. 273 type VirtualSchemaHolder struct { 274 entries map[string]virtualSchemaEntry 275 defsByID map[sqlbase.ID]*virtualDefEntry 276 orderedNames []string 277 } 278 279 // GetVirtualSchema makes VirtualSchemaHolder implement schema.VirtualSchemas. 280 func (vs *VirtualSchemaHolder) GetVirtualSchema(schemaName string) (catalog.VirtualSchema, bool) { 281 virtualSchema, ok := vs.entries[schemaName] 282 return virtualSchema, ok 283 } 284 285 var _ catalog.VirtualSchemas = (*VirtualSchemaHolder)(nil) 286 287 type virtualSchemaEntry struct { 288 // TODO(ajwerner): Use a sqlbase.SchemaDescriptor here as part of the 289 // user-defined schema work. 290 desc *sqlbase.DatabaseDescriptor 291 defs map[string]virtualDefEntry 292 orderedDefNames []string 293 allTableNames map[string]struct{} 294 containsTypes bool 295 } 296 297 func (v virtualSchemaEntry) Desc() catalog.Descriptor { 298 return v.desc 299 } 300 301 func (v virtualSchemaEntry) NumTables() int { 302 return len(v.defs) 303 } 304 305 func (v virtualSchemaEntry) VisitTables(f func(object catalog.VirtualObject)) { 306 for _, name := range v.orderedDefNames { 307 f(v.defs[name]) 308 } 309 } 310 311 func (v virtualSchemaEntry) GetObjectByName( 312 name string, flags tree.ObjectLookupFlags, 313 ) (catalog.VirtualObject, error) { 314 switch flags.DesiredObjectKind { 315 case tree.TableObject: 316 if def, ok := v.defs[name]; ok { 317 if flags.RequireMutable { 318 return mutableVirtualDefEntry{desc: def.desc}, nil 319 } 320 return &def, nil 321 } 322 if _, ok := v.allTableNames[name]; ok { 323 return nil, unimplemented.Newf(v.desc.Name+"."+name, 324 "virtual schema table not implemented: %s.%s", v.desc.Name, name) 325 } 326 return nil, nil 327 case tree.TypeObject: 328 if !v.containsTypes { 329 return nil, nil 330 } 331 // Currently, we don't allow creation of types in virtual schemas, so 332 // the only types present in the virtual schemas that have types (i.e. 333 // pg_catalog) are types that are known at parse time. So, attempt to 334 // parse the input object as a statically known type. Note that an 335 // invalid input type like "notatype" will be parsed successfully as 336 // a ResolvableTypeReference, so the error here does not need to be 337 // intercepted and inspected. 338 typRef, err := parser.ParseType(name) 339 if err != nil { 340 return nil, err 341 } 342 // If the parsed reference is actually a statically known type, then 343 // we can return it. We return a simple wrapping of this type as 344 // TypeDescriptor that represents an alias of the result type. 345 typ, ok := tree.GetStaticallyKnownType(typRef) 346 if !ok { 347 return nil, nil 348 } 349 return virtualTypeEntry{ 350 desc: sqlbase.MakeSimpleAliasTypeDescriptor(typ), 351 mutable: flags.RequireMutable, 352 }, nil 353 default: 354 return nil, errors.AssertionFailedf("unknown desired object kind %d", flags.DesiredObjectKind) 355 } 356 } 357 358 type virtualDefEntry struct { 359 virtualDef virtualSchemaDef 360 desc *sqlbase.TableDescriptor 361 comment string 362 validWithNoDatabaseContext bool 363 } 364 365 func (e virtualDefEntry) Desc() catalog.Descriptor { 366 return sqlbase.NewImmutableTableDescriptor(*e.desc) 367 } 368 369 type mutableVirtualDefEntry struct { 370 desc *sqlbase.TableDescriptor 371 } 372 373 func (e mutableVirtualDefEntry) Desc() catalog.Descriptor { 374 return sqlbase.NewMutableExistingTableDescriptor(*e.desc) 375 } 376 377 type virtualTypeEntry struct { 378 desc *sqlbase.TypeDescriptor 379 mutable bool 380 } 381 382 func (e virtualTypeEntry) Desc() catalog.Descriptor { 383 if e.mutable { 384 return sqlbase.NewMutableExistingTypeDescriptor(*e.desc) 385 } 386 return sqlbase.NewImmutableTypeDescriptor(*e.desc) 387 } 388 389 type virtualTableConstructor func(context.Context, *planner, string) (planNode, error) 390 391 var errInvalidDbPrefix = errors.WithHint( 392 pgerror.New(pgcode.UndefinedObject, 393 "cannot access virtual schema in anonymous database"), 394 "verify that the current database is set") 395 396 func newInvalidVirtualSchemaError() error { 397 return errors.AssertionFailedf("virtualSchema cannot have both the populate and generator functions defined") 398 } 399 400 func newInvalidVirtualDefEntryError() error { 401 return errors.AssertionFailedf("virtualDefEntry.virtualDef must be a virtualSchemaTable") 402 } 403 404 func (e virtualDefEntry) validateRow(datums tree.Datums, columns sqlbase.ResultColumns) error { 405 if r, c := len(datums), len(columns); r != c { 406 return errors.AssertionFailedf("datum row count and column count differ: %d vs %d", r, c) 407 } 408 for i := range columns { 409 col := &columns[i] 410 datum := datums[i] 411 if datum == tree.DNull { 412 if !e.desc.Columns[i].Nullable { 413 return errors.AssertionFailedf("column %s.%s not nullable, but found NULL value", 414 e.desc.Name, col.Name) 415 } 416 } else if !datum.ResolvedType().Equivalent(col.Typ) { 417 return errors.AssertionFailedf("datum column %q expected to be type %s; found type %s", 418 col.Name, col.Typ, datum.ResolvedType()) 419 } 420 } 421 return nil 422 } 423 424 // getPlanInfo returns the column metadata and a constructor for a new 425 // valuesNode for the virtual table. We use deferred construction here 426 // so as to avoid populating a RowContainer during query preparation, 427 // where we can't guarantee it will be Close()d in case of error. 428 func (e virtualDefEntry) getPlanInfo( 429 table *sqlbase.TableDescriptor, 430 index *sqlbase.IndexDescriptor, 431 idxConstraint *constraint.Constraint, 432 ) (sqlbase.ResultColumns, virtualTableConstructor) { 433 var columns sqlbase.ResultColumns 434 for i := range e.desc.Columns { 435 col := &e.desc.Columns[i] 436 columns = append(columns, sqlbase.ResultColumn{ 437 Name: col.Name, 438 Typ: col.Type, 439 TableID: table.GetID(), 440 PGAttributeNum: col.GetLogicalColumnID(), 441 }) 442 } 443 444 constructor := func(ctx context.Context, p *planner, dbName string) (planNode, error) { 445 var dbDesc *DatabaseDescriptor 446 if dbName != "" { 447 var err error 448 dbDesc, err = p.LogicalSchemaAccessor().GetDatabaseDesc(ctx, p.txn, p.ExecCfg().Codec, 449 dbName, tree.DatabaseLookupFlags{Required: true, AvoidCached: p.avoidCachedDescriptors}) 450 if err != nil { 451 return nil, err 452 } 453 } else { 454 if !e.validWithNoDatabaseContext { 455 return nil, errInvalidDbPrefix 456 } 457 } 458 459 switch def := e.virtualDef.(type) { 460 case virtualSchemaTable: 461 if def.generator != nil && def.populate != nil { 462 return nil, newInvalidVirtualSchemaError() 463 } 464 465 if def.generator != nil { 466 next, cleanup, err := def.generator(ctx, p, dbDesc) 467 if err != nil { 468 return nil, err 469 } 470 return p.newVirtualTableNode(columns, next, cleanup), nil 471 } 472 473 constrainedScan := idxConstraint != nil && !idxConstraint.IsUnconstrained() 474 if !constrainedScan { 475 generator, cleanup := setupGenerator(ctx, func(pusher rowPusher) error { 476 return def.populate(ctx, p, dbDesc, func(row ...tree.Datum) error { 477 if err := e.validateRow(row, columns); err != nil { 478 return err 479 } 480 return pusher.pushRow(row...) 481 }) 482 }) 483 return p.newVirtualTableNode(columns, generator, cleanup), nil 484 } 485 486 // We are now dealing with a constrained virtual index scan. 487 488 if index.ID == 1 { 489 return nil, errors.AssertionFailedf( 490 "programming error: can't constrain scan on primary virtual index of table %s", e.desc.Name) 491 } 492 493 // Figure out the ordinal position of the column that we're filtering on. 494 columnIdxMap := table.ColumnIdxMap() 495 indexKeyDatums := make([]tree.Datum, len(index.ColumnIDs)) 496 497 generator, cleanup := setupGenerator(ctx, e.makeConstrainedRowsGenerator( 498 ctx, p, dbDesc, index, indexKeyDatums, columnIdxMap, idxConstraint, columns)) 499 return p.newVirtualTableNode(columns, generator, cleanup), nil 500 501 default: 502 return nil, newInvalidVirtualDefEntryError() 503 } 504 } 505 506 return columns, constructor 507 } 508 509 // makeConstrainedRowsGenerator returns a generator function that can be invoked 510 // to push all rows from this virtual table that satisfy the input index 511 // constraint to a row pusher that's supplied to the generator function. 512 func (e virtualDefEntry) makeConstrainedRowsGenerator( 513 ctx context.Context, 514 p *planner, 515 dbDesc *DatabaseDescriptor, 516 index *sqlbase.IndexDescriptor, 517 indexKeyDatums []tree.Datum, 518 columnIdxMap map[sqlbase.ColumnID]int, 519 idxConstraint *constraint.Constraint, 520 columns sqlbase.ResultColumns, 521 ) func(pusher rowPusher) error { 522 def := e.virtualDef.(virtualSchemaTable) 523 return func(pusher rowPusher) error { 524 var span constraint.Span 525 addRowIfPassesFilter := func(datums ...tree.Datum) error { 526 for i, id := range index.ColumnIDs { 527 indexKeyDatums[i] = datums[columnIdxMap[id]] 528 } 529 // Construct a single key span out of the current row, so that 530 // we can test it for containment within the constraint span of the 531 // filter that we're applying. The results of this containment check 532 // will tell us whether or not to let the current row pass the filter. 533 key := constraint.MakeCompositeKey(indexKeyDatums...) 534 span.Init(key, constraint.IncludeBoundary, key, constraint.IncludeBoundary) 535 var err error 536 if idxConstraint.ContainsSpan(p.EvalContext(), &span) { 537 if err := e.validateRow(datums, columns); err != nil { 538 return err 539 } 540 return pusher.pushRow(datums...) 541 } 542 return err 543 } 544 545 // We have a virtual index with a constraint. Run the constrained 546 // populate routine for every span. If for some reason we can't use the 547 // index for a given span, we exit the loop early and run a "full scan" 548 // over the virtual table, filtering the output using the remaining 549 // spans. 550 // N.B. we count down in this loop so that, if we have to give up half 551 // way through, we can easily truncate the spans we already processed 552 // from the end and use them as a filter for the remaining rows of the 553 // table. 554 currentConstraint := idxConstraint.Spans.Count() - 1 555 for ; currentConstraint >= 0; currentConstraint-- { 556 span := idxConstraint.Spans.Get(currentConstraint) 557 if span.StartKey().Length() > 1 { 558 return errors.AssertionFailedf( 559 "programming error: can't push down composite constraints into vtables") 560 } 561 if !span.HasSingleKey(p.EvalContext()) { 562 // No hope - we can't deal with range scans on virtual indexes. 563 break 564 } 565 constraintDatum := span.StartKey().Value(0) 566 virtualIndex := def.getIndex(index.ID) 567 568 // For each span, run the index's populate method, constrained to the 569 // constraint span's value. 570 found, err := virtualIndex.populate(ctx, constraintDatum, p, dbDesc, 571 addRowIfPassesFilter) 572 if err != nil { 573 return err 574 } 575 if !found && virtualIndex.partial { 576 // If we found nothing, and the index was partial, we have no choice 577 // but to populate the entire table and search through it. 578 break 579 } 580 } 581 if currentConstraint < 0 { 582 // We successfully processed all constraints, so we can leave now. 583 return nil 584 } 585 586 // Fall back to a full scan of the table, using the remaining filters 587 // that weren't able to be used as constraints. 588 idxConstraint.Spans.Truncate(currentConstraint + 1) 589 return def.populate(ctx, p, dbDesc, addRowIfPassesFilter) 590 } 591 } 592 593 // NewVirtualSchemaHolder creates a new VirtualSchemaHolder. 594 func NewVirtualSchemaHolder( 595 ctx context.Context, st *cluster.Settings, 596 ) (*VirtualSchemaHolder, error) { 597 vs := &VirtualSchemaHolder{ 598 entries: make(map[string]virtualSchemaEntry, len(virtualSchemas)), 599 orderedNames: make([]string, len(virtualSchemas)), 600 defsByID: make(map[sqlbase.ID]*virtualDefEntry, math.MaxUint32-sqlbase.MinVirtualID), 601 } 602 603 order := 0 604 for schemaID, schema := range virtualSchemas { 605 dbName := schema.name 606 dbDesc := initVirtualDatabaseDesc(schemaID, dbName) 607 defs := make(map[string]virtualDefEntry, len(schema.tableDefs)) 608 orderedDefNames := make([]string, 0, len(schema.tableDefs)) 609 610 for id, def := range schema.tableDefs { 611 tableDesc, err := def.initVirtualTableDesc(ctx, st, schemaID, id) 612 613 if err != nil { 614 return nil, errors.NewAssertionErrorWithWrappedErrf(err, 615 "failed to initialize %s", errors.Safe(def.getSchema())) 616 } 617 618 if schema.tableValidator != nil { 619 if err := schema.tableValidator(&tableDesc); err != nil { 620 return nil, errors.NewAssertionErrorWithWrappedErrf(err, "programmer error") 621 } 622 } 623 624 entry := virtualDefEntry{ 625 virtualDef: def, 626 desc: &tableDesc, 627 validWithNoDatabaseContext: schema.validWithNoDatabaseContext, 628 comment: def.getComment(), 629 } 630 defs[tableDesc.Name] = entry 631 vs.defsByID[tableDesc.ID] = &entry 632 orderedDefNames = append(orderedDefNames, tableDesc.Name) 633 } 634 635 sort.Strings(orderedDefNames) 636 637 vs.entries[dbName] = virtualSchemaEntry{ 638 desc: dbDesc, 639 defs: defs, 640 orderedDefNames: orderedDefNames, 641 allTableNames: schema.allTableNames, 642 containsTypes: schema.containsTypes, 643 } 644 vs.orderedNames[order] = dbName 645 order++ 646 } 647 sort.Strings(vs.orderedNames) 648 return vs, nil 649 } 650 651 // Virtual databases and tables each have SELECT privileges for "public", which includes 652 // all users. However, virtual schemas have more fine-grained access control. 653 // For instance, information_schema will only expose rows to a given user which that 654 // user has access to. 655 var publicSelectPrivileges = sqlbase.NewPrivilegeDescriptor(sqlbase.PublicRole, privilege.List{privilege.SELECT}) 656 657 func initVirtualDatabaseDesc(id sqlbase.ID, name string) *sqlbase.DatabaseDescriptor { 658 return &sqlbase.DatabaseDescriptor{ 659 Name: name, 660 ID: id, 661 Privileges: publicSelectPrivileges, 662 } 663 } 664 665 // getEntries is part of the VirtualTabler interface. 666 func (vs *VirtualSchemaHolder) getEntries() map[string]virtualSchemaEntry { 667 return vs.entries 668 } 669 670 // getSchemaNames is part of the VirtualTabler interface. 671 func (vs *VirtualSchemaHolder) getSchemaNames() []string { 672 return vs.orderedNames 673 } 674 675 // getVirtualSchemaEntry retrieves a virtual schema entry given a database name. 676 // getVirtualSchemaEntry is part of the VirtualTabler interface. 677 func (vs *VirtualSchemaHolder) getVirtualSchemaEntry(name string) (virtualSchemaEntry, bool) { 678 e, ok := vs.entries[name] 679 return e, ok 680 } 681 682 // getVirtualTableEntry checks if the provided name matches a virtual database/table 683 // pair. The function will return the table's virtual table entry if the name matches 684 // a specific table. It will return an error if the name references a virtual database 685 // but the table is non-existent. 686 // getVirtualTableEntry is part of the VirtualTabler interface. 687 func (vs *VirtualSchemaHolder) getVirtualTableEntry(tn *tree.TableName) (virtualDefEntry, error) { 688 if db, ok := vs.getVirtualSchemaEntry(tn.Schema()); ok { 689 tableName := tn.Table() 690 if t, ok := db.defs[tableName]; ok { 691 return t, nil 692 } 693 if _, ok := db.allTableNames[tableName]; ok { 694 return virtualDefEntry{}, unimplemented.NewWithIssueDetailf(8675, 695 tn.Schema()+"."+tableName, 696 "virtual schema table not implemented: %s.%s", tn.Schema(), tableName) 697 } 698 return virtualDefEntry{}, sqlbase.NewUndefinedRelationError(tn) 699 } 700 return virtualDefEntry{}, nil 701 } 702 703 func (vs *VirtualSchemaHolder) getVirtualTableEntryByID(id sqlbase.ID) (virtualDefEntry, error) { 704 entry, ok := vs.defsByID[id] 705 if !ok { 706 return virtualDefEntry{}, sqlbase.ErrDescriptorNotFound 707 } 708 return *entry, nil 709 } 710 711 // VirtualTabler is used to fetch descriptors for virtual tables and databases. 712 type VirtualTabler interface { 713 getVirtualTableDesc(tn *tree.TableName) (*sqlbase.TableDescriptor, error) 714 getVirtualSchemaEntry(name string) (virtualSchemaEntry, bool) 715 getVirtualTableEntry(tn *tree.TableName) (virtualDefEntry, error) 716 getVirtualTableEntryByID(id sqlbase.ID) (virtualDefEntry, error) 717 getEntries() map[string]virtualSchemaEntry 718 getSchemaNames() []string 719 } 720 721 // getVirtualTableDesc checks if the provided name matches a virtual database/table 722 // pair, and returns its descriptor if it does. 723 // getVirtualTableDesc is part of the VirtualTabler interface. 724 func (vs *VirtualSchemaHolder) getVirtualTableDesc( 725 tn *tree.TableName, 726 ) (*sqlbase.TableDescriptor, error) { 727 t, err := vs.getVirtualTableEntry(tn) 728 if err != nil { 729 return nil, err 730 } 731 return t.desc, nil 732 }