github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/information_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 "fmt" 16 "sort" 17 "strconv" 18 "unicode/utf8" 19 20 "github.com/cockroachdb/cockroach/pkg/base" 21 "github.com/cockroachdb/cockroach/pkg/security" 22 "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" 23 "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" 24 "github.com/cockroachdb/cockroach/pkg/sql/privilege" 25 "github.com/cockroachdb/cockroach/pkg/sql/schemaexpr" 26 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 27 "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" 28 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 29 "github.com/cockroachdb/cockroach/pkg/sql/types" 30 "github.com/cockroachdb/cockroach/pkg/sql/vtable" 31 "github.com/cockroachdb/errors" 32 ) 33 34 const ( 35 pgCatalogName = sessiondata.PgCatalogName 36 ) 37 38 var pgCatalogNameDString = tree.NewDString(pgCatalogName) 39 40 // informationSchema lists all the table definitions for 41 // information_schema. 42 var informationSchema = virtualSchema{ 43 name: sessiondata.InformationSchemaName, 44 allTableNames: buildStringSet( 45 // Generated with: 46 // select distinct '"'||table_name||'",' from information_schema.tables 47 // where table_schema='information_schema' order by table_name; 48 "_pg_foreign_data_wrappers", 49 "_pg_foreign_servers", 50 "_pg_foreign_table_columns", 51 "_pg_foreign_tables", 52 "_pg_user_mappings", 53 "administrable_role_authorizations", 54 "applicable_roles", 55 "attributes", 56 "character_sets", 57 "check_constraint_routine_usage", 58 "check_constraints", 59 "collation_character_set_applicability", 60 "collations", 61 "column_domain_usage", 62 "column_options", 63 "column_privileges", 64 "column_udt_usage", 65 "columns", 66 "constraint_column_usage", 67 "constraint_table_usage", 68 "data_type_privileges", 69 "domain_constraints", 70 "domain_udt_usage", 71 "domains", 72 "element_types", 73 "enabled_roles", 74 "foreign_data_wrapper_options", 75 "foreign_data_wrappers", 76 "foreign_server_options", 77 "foreign_servers", 78 "foreign_table_options", 79 "foreign_tables", 80 "information_schema_catalog_name", 81 "key_column_usage", 82 "parameters", 83 "referential_constraints", 84 "role_column_grants", 85 "role_routine_grants", 86 "role_table_grants", 87 "role_udt_grants", 88 "role_usage_grants", 89 "routine_privileges", 90 "routines", 91 "schemata", 92 "sequences", 93 "sql_features", 94 "sql_implementation_info", 95 "sql_languages", 96 "sql_packages", 97 "sql_parts", 98 "sql_sizing", 99 "sql_sizing_profiles", 100 "table_constraints", 101 "table_privileges", 102 "tables", 103 "transforms", 104 "triggered_update_columns", 105 "triggers", 106 "udt_privileges", 107 "usage_privileges", 108 "user_defined_types", 109 "user_mapping_options", 110 "user_mappings", 111 "view_column_usage", 112 "view_routine_usage", 113 "view_table_usage", 114 "views", 115 ), 116 tableDefs: map[sqlbase.ID]virtualSchemaDef{ 117 sqlbase.InformationSchemaAdministrableRoleAuthorizationsID: informationSchemaAdministrableRoleAuthorizations, 118 sqlbase.InformationSchemaApplicableRolesID: informationSchemaApplicableRoles, 119 sqlbase.InformationSchemaCheckConstraints: informationSchemaCheckConstraints, 120 sqlbase.InformationSchemaColumnPrivilegesID: informationSchemaColumnPrivileges, 121 sqlbase.InformationSchemaColumnsTableID: informationSchemaColumnsTable, 122 sqlbase.InformationSchemaConstraintColumnUsageTableID: informationSchemaConstraintColumnUsageTable, 123 sqlbase.InformationSchemaEnabledRolesID: informationSchemaEnabledRoles, 124 sqlbase.InformationSchemaKeyColumnUsageTableID: informationSchemaKeyColumnUsageTable, 125 sqlbase.InformationSchemaParametersTableID: informationSchemaParametersTable, 126 sqlbase.InformationSchemaReferentialConstraintsTableID: informationSchemaReferentialConstraintsTable, 127 sqlbase.InformationSchemaRoleTableGrantsID: informationSchemaRoleTableGrants, 128 sqlbase.InformationSchemaRoutineTableID: informationSchemaRoutineTable, 129 sqlbase.InformationSchemaSchemataTableID: informationSchemaSchemataTable, 130 sqlbase.InformationSchemaSchemataTablePrivilegesID: informationSchemaSchemataTablePrivileges, 131 sqlbase.InformationSchemaSequencesID: informationSchemaSequences, 132 sqlbase.InformationSchemaStatisticsTableID: informationSchemaStatisticsTable, 133 sqlbase.InformationSchemaTableConstraintTableID: informationSchemaTableConstraintTable, 134 sqlbase.InformationSchemaTablePrivilegesID: informationSchemaTablePrivileges, 135 sqlbase.InformationSchemaTablesTableID: informationSchemaTablesTable, 136 sqlbase.InformationSchemaViewsTableID: informationSchemaViewsTable, 137 sqlbase.InformationSchemaUserPrivilegesID: informationSchemaUserPrivileges, 138 }, 139 tableValidator: validateInformationSchemaTable, 140 validWithNoDatabaseContext: true, 141 } 142 143 func buildStringSet(ss ...string) map[string]struct{} { 144 m := map[string]struct{}{} 145 for _, s := range ss { 146 m[s] = struct{}{} 147 } 148 return m 149 } 150 151 var ( 152 emptyString = tree.NewDString("") 153 // information_schema was defined before the BOOLEAN data type was added to 154 // the SQL specification. Because of this, boolean values are represented as 155 // STRINGs. The BOOLEAN data type should NEVER be used in information_schema 156 // tables. Instead, define columns as STRINGs and map bools to STRINGs using 157 // yesOrNoDatum. 158 yesString = tree.NewDString("YES") 159 noString = tree.NewDString("NO") 160 ) 161 162 func yesOrNoDatum(b bool) tree.Datum { 163 if b { 164 return yesString 165 } 166 return noString 167 } 168 169 func dNameOrNull(s string) tree.Datum { 170 if s == "" { 171 return tree.DNull 172 } 173 return tree.NewDName(s) 174 } 175 176 func dIntFnOrNull(fn func() (int32, bool)) tree.Datum { 177 if n, ok := fn(); ok { 178 return tree.NewDInt(tree.DInt(n)) 179 } 180 return tree.DNull 181 } 182 183 func validateInformationSchemaTable(table *sqlbase.TableDescriptor) error { 184 // Make sure no tables have boolean columns. 185 for i := range table.Columns { 186 if table.Columns[i].Type.Family() == types.BoolFamily { 187 return errors.Errorf("information_schema tables should never use BOOL columns. "+ 188 "See the comment about yesOrNoDatum. Found BOOL column in %s.", table.Name) 189 } 190 } 191 return nil 192 } 193 194 var informationSchemaAdministrableRoleAuthorizations = virtualSchemaTable{ 195 comment: `roles for which the current user has admin option 196 ` + base.DocsURL("information-schema.html#administrable_role_authorizations") + ` 197 https://www.postgresql.org/docs/9.5/infoschema-administrable-role-authorizations.html`, 198 schema: vtable.InformationSchemaAdministrableRoleAuthorizations, 199 populate: func(ctx context.Context, p *planner, _ *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 200 currentUser := p.SessionData().User 201 memberMap, err := p.MemberOfWithAdminOption(ctx, currentUser) 202 if err != nil { 203 return err 204 } 205 206 grantee := tree.NewDString(currentUser) 207 for roleName, isAdmin := range memberMap { 208 if !isAdmin { 209 // We only show memberships with the admin option. 210 continue 211 } 212 213 if err := addRow( 214 grantee, // grantee: always the current user 215 tree.NewDString(roleName), // role_name 216 yesString, // is_grantable: always YES 217 ); err != nil { 218 return err 219 } 220 } 221 222 return nil 223 }, 224 } 225 226 var informationSchemaApplicableRoles = virtualSchemaTable{ 227 comment: `roles available to the current user 228 ` + base.DocsURL("information-schema.html#applicable_roles") + ` 229 https://www.postgresql.org/docs/9.5/infoschema-applicable-roles.html`, 230 schema: vtable.InformationSchemaApplicableRoles, 231 populate: func(ctx context.Context, p *planner, _ *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 232 currentUser := p.SessionData().User 233 memberMap, err := p.MemberOfWithAdminOption(ctx, currentUser) 234 if err != nil { 235 return err 236 } 237 238 grantee := tree.NewDString(currentUser) 239 240 for roleName, isAdmin := range memberMap { 241 if err := addRow( 242 grantee, // grantee: always the current user 243 tree.NewDString(roleName), // role_name 244 yesOrNoDatum(isAdmin), // is_grantable 245 ); err != nil { 246 return err 247 } 248 } 249 250 return nil 251 }, 252 } 253 254 var informationSchemaCheckConstraints = virtualSchemaTable{ 255 comment: `check constraints 256 ` + base.DocsURL("information-schema.html#check_constraints") + ` 257 https://www.postgresql.org/docs/9.5/infoschema-check-constraints.html`, 258 schema: vtable.InformationSchemaCheckConstraints, 259 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 260 h := makeOidHasher() 261 return forEachTableDescWithTableLookup(ctx, p, dbContext, hideVirtual /* no constraints in virtual tables */, func( 262 db *sqlbase.DatabaseDescriptor, 263 scName string, 264 table *sqlbase.TableDescriptor, 265 tableLookup tableLookupFn, 266 ) error { 267 conInfo, err := table.GetConstraintInfoWithLookup(tableLookup.getTableByID) 268 if err != nil { 269 return err 270 } 271 dbNameStr := tree.NewDString(db.Name) 272 scNameStr := tree.NewDString(scName) 273 for conName, con := range conInfo { 274 // Only Check constraints are included. 275 if con.Kind != sqlbase.ConstraintTypeCheck { 276 continue 277 } 278 conNameStr := tree.NewDString(conName) 279 // Like with pg_catalog.pg_constraint, Postgres wraps the check 280 // constraint expression in two pairs of parentheses. 281 chkExprStr := tree.NewDString(fmt.Sprintf("((%s))", con.Details)) 282 if err := addRow( 283 dbNameStr, // constraint_catalog 284 scNameStr, // constraint_schema 285 conNameStr, // constraint_name 286 chkExprStr, // check_clause 287 ); err != nil { 288 return err 289 } 290 } 291 292 // Unlike with pg_catalog.pg_constraint, Postgres also includes NOT 293 // NULL column constraints in information_schema.check_constraints. 294 // Cockroach doesn't track these constraints as check constraints, 295 // but we can pull them off of the table's column descriptors. 296 colNum := 0 297 return forEachColumnInTable(table, func(column *sqlbase.ColumnDescriptor) error { 298 colNum++ 299 // Only visible, non-nullable columns are included. 300 if column.Hidden || column.Nullable { 301 return nil 302 } 303 // Generate a unique name for each NOT NULL constraint. Postgres 304 // uses the format <namespace_oid>_<table_oid>_<col_idx>_not_null. 305 // We might as well do the same. 306 conNameStr := tree.NewDString(fmt.Sprintf( 307 "%s_%s_%d_not_null", h.NamespaceOid(db, scName), tableOid(table.ID), colNum, 308 )) 309 chkExprStr := tree.NewDString(fmt.Sprintf( 310 "%s IS NOT NULL", column.Name, 311 )) 312 return addRow( 313 dbNameStr, // constraint_catalog 314 scNameStr, // constraint_schema 315 conNameStr, // constraint_name 316 chkExprStr, // check_clause 317 ) 318 }) 319 }) 320 }, 321 } 322 323 var informationSchemaColumnPrivileges = virtualSchemaTable{ 324 comment: `column privilege grants (incomplete) 325 ` + base.DocsURL("information-schema.html#column_privileges") + ` 326 https://www.postgresql.org/docs/9.5/infoschema-column-privileges.html`, 327 schema: vtable.InformationSchemaColumnPrivileges, 328 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 329 return forEachTableDesc(ctx, p, dbContext, virtualMany, func(db *sqlbase.DatabaseDescriptor, scName string, table *sqlbase.TableDescriptor) error { 330 dbNameStr := tree.NewDString(db.Name) 331 scNameStr := tree.NewDString(scName) 332 columndata := privilege.List{privilege.SELECT, privilege.INSERT, privilege.UPDATE} // privileges for column level granularity 333 for _, u := range table.Privileges.Users { 334 for _, priv := range columndata { 335 if priv.Mask()&u.Privileges != 0 { 336 for i := range table.Columns { 337 cd := &table.Columns[i] 338 if err := addRow( 339 tree.DNull, // grantor 340 tree.NewDString(u.User), // grantee 341 dbNameStr, // table_catalog 342 scNameStr, // table_schema 343 tree.NewDString(table.Name), // table_name 344 tree.NewDString(cd.Name), // column_name 345 tree.NewDString(priv.String()), // privilege_type 346 tree.DNull, // is_grantable 347 ); err != nil { 348 return err 349 } 350 } 351 } 352 } 353 } 354 return nil 355 }) 356 }, 357 } 358 359 var informationSchemaColumnsTable = virtualSchemaTable{ 360 comment: `table and view columns (incomplete) 361 ` + base.DocsURL("information-schema.html#columns") + ` 362 https://www.postgresql.org/docs/9.5/infoschema-columns.html`, 363 schema: vtable.InformationSchemaColumns, 364 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 365 return forEachTableDesc(ctx, p, dbContext, virtualMany, func(db *sqlbase.DatabaseDescriptor, scName string, table *sqlbase.TableDescriptor) error { 366 dbNameStr := tree.NewDString(db.Name) 367 scNameStr := tree.NewDString(scName) 368 return forEachColumnInTable(table, func(column *sqlbase.ColumnDescriptor) error { 369 collationCatalog := tree.DNull 370 collationSchema := tree.DNull 371 collationName := tree.DNull 372 if locale := column.Type.Locale(); locale != "" { 373 collationCatalog = dbNameStr 374 collationSchema = pgCatalogNameDString 375 collationName = tree.NewDString(locale) 376 } 377 colDefault := tree.DNull 378 if column.DefaultExpr != nil { 379 colExpr, err := schemaexpr.DeserializeTableDescExpr(ctx, &p.semaCtx, table, *column.DefaultExpr) 380 if err != nil { 381 return err 382 } 383 colDefault = tree.NewDString(tree.SerializeForDisplay(colExpr)) 384 } 385 colComputed := emptyString 386 if column.ComputeExpr != nil { 387 colExpr, err := schemaexpr.DeserializeTableDescExpr(ctx, &p.semaCtx, table, *column.ComputeExpr) 388 if err != nil { 389 return err 390 } 391 colComputed = tree.NewDString(tree.SerializeForDisplay(colExpr)) 392 } 393 return addRow( 394 dbNameStr, // table_catalog 395 scNameStr, // table_schema 396 tree.NewDString(table.Name), // table_name 397 tree.NewDString(column.Name), // column_name 398 tree.NewDInt(tree.DInt(column.GetLogicalColumnID())), // ordinal_position 399 colDefault, // column_default 400 yesOrNoDatum(column.Nullable), // is_nullable 401 tree.NewDString(column.Type.InformationSchemaName()), // data_type 402 characterMaximumLength(column.Type), // character_maximum_length 403 characterOctetLength(column.Type), // character_octet_length 404 numericPrecision(column.Type), // numeric_precision 405 numericPrecisionRadix(column.Type), // numeric_precision_radix 406 numericScale(column.Type), // numeric_scale 407 datetimePrecision(column.Type), // datetime_precision 408 tree.DNull, // interval_type 409 tree.DNull, // interval_precision 410 tree.DNull, // character_set_catalog 411 tree.DNull, // character_set_schema 412 tree.DNull, // character_set_name 413 collationCatalog, // collation_catalog 414 collationSchema, // collation_schema 415 collationName, // collation_name 416 tree.DNull, // domain_catalog 417 tree.DNull, // domain_schema 418 tree.DNull, // domain_name 419 dbNameStr, // udt_catalog 420 pgCatalogNameDString, // udt_schema 421 tree.NewDString(column.Type.PGName()), // udt_name 422 tree.DNull, // scope_catalog 423 tree.DNull, // scope_schema 424 tree.DNull, // scope_name 425 tree.DNull, // maximum_cardinality 426 tree.DNull, // dtd_identifier 427 tree.DNull, // is_self_referencing 428 tree.DNull, // is_identity 429 tree.DNull, // identity_generation 430 tree.DNull, // identity_start 431 tree.DNull, // identity_increment 432 tree.DNull, // identity_maximum 433 tree.DNull, // identity_minimum 434 tree.DNull, // identity_cycle 435 yesOrNoDatum(column.IsComputed()), // is_generated 436 colComputed, // generation_expression 437 yesOrNoDatum(table.IsTable() && 438 !table.IsVirtualTable() && 439 !column.IsComputed(), 440 ), // is_updatable 441 yesOrNoDatum(column.Hidden), // is_hidden 442 tree.NewDString(column.Type.SQLString()), // crdb_sql_type 443 ) 444 }) 445 }) 446 }, 447 } 448 449 var informationSchemaEnabledRoles = virtualSchemaTable{ 450 comment: `roles for the current user 451 ` + base.DocsURL("information-schema.html#enabled_roles") + ` 452 https://www.postgresql.org/docs/9.5/infoschema-enabled-roles.html`, 453 schema: ` 454 CREATE TABLE information_schema.enabled_roles ( 455 ROLE_NAME STRING NOT NULL 456 )`, 457 populate: func(ctx context.Context, p *planner, _ *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 458 currentUser := p.SessionData().User 459 memberMap, err := p.MemberOfWithAdminOption(ctx, currentUser) 460 if err != nil { 461 return err 462 } 463 464 // The current user is always listed. 465 if err := addRow( 466 tree.NewDString(currentUser), // role_name: the current user 467 ); err != nil { 468 return err 469 } 470 471 for roleName := range memberMap { 472 if err := addRow( 473 tree.NewDString(roleName), // role_name 474 ); err != nil { 475 return err 476 } 477 } 478 479 return nil 480 }, 481 } 482 483 // characterMaximumLength returns the declared maximum length of 484 // characters if the type is a character or bit string data 485 // type. Returns false if the data type is not a character or bit 486 // string, or if the string's length is not bounded. 487 func characterMaximumLength(colType *types.T) tree.Datum { 488 return dIntFnOrNull(func() (int32, bool) { 489 switch colType.Family() { 490 case types.StringFamily, types.CollatedStringFamily, types.BitFamily: 491 if colType.Width() > 0 { 492 return colType.Width(), true 493 } 494 } 495 return 0, false 496 }) 497 } 498 499 // characterOctetLength returns the maximum possible length in 500 // octets of a datum if the T is a character string. Returns 501 // false if the data type is not a character string, or if the 502 // string's length is not bounded. 503 func characterOctetLength(colType *types.T) tree.Datum { 504 return dIntFnOrNull(func() (int32, bool) { 505 switch colType.Family() { 506 case types.StringFamily, types.CollatedStringFamily: 507 if colType.Width() > 0 { 508 return colType.Width() * utf8.UTFMax, true 509 } 510 } 511 return 0, false 512 }) 513 } 514 515 // numericPrecision returns the declared or implicit precision of numeric 516 // data types. Returns false if the data type is not numeric, or if the precision 517 // of the numeric type is not bounded. 518 func numericPrecision(colType *types.T) tree.Datum { 519 return dIntFnOrNull(func() (int32, bool) { 520 switch colType.Family() { 521 case types.IntFamily: 522 return colType.Width(), true 523 case types.FloatFamily: 524 if colType.Width() == 32 { 525 return 24, true 526 } 527 return 53, true 528 case types.DecimalFamily: 529 if colType.Precision() > 0 { 530 return colType.Precision(), true 531 } 532 } 533 return 0, false 534 }) 535 } 536 537 // numericPrecisionRadix returns the implicit precision radix of 538 // numeric data types. Returns false if the data type is not numeric. 539 func numericPrecisionRadix(colType *types.T) tree.Datum { 540 return dIntFnOrNull(func() (int32, bool) { 541 switch colType.Family() { 542 case types.IntFamily: 543 return 2, true 544 case types.FloatFamily: 545 return 2, true 546 case types.DecimalFamily: 547 return 10, true 548 } 549 return 0, false 550 }) 551 } 552 553 // NumericScale returns the declared or implicit precision of exact numeric 554 // data types. Returns false if the data type is not an exact numeric, or if the 555 // scale of the exact numeric type is not bounded. 556 func numericScale(colType *types.T) tree.Datum { 557 return dIntFnOrNull(func() (int32, bool) { 558 switch colType.Family() { 559 case types.IntFamily: 560 return 0, true 561 case types.DecimalFamily: 562 if colType.Precision() > 0 { 563 return colType.Width(), true 564 } 565 } 566 return 0, false 567 }) 568 } 569 570 func datetimePrecision(colType *types.T) tree.Datum { 571 // We currently do not support a datetime precision. 572 return tree.DNull 573 } 574 575 var informationSchemaConstraintColumnUsageTable = virtualSchemaTable{ 576 comment: `columns usage by constraints 577 https://www.postgresql.org/docs/9.5/infoschema-constraint-column-usage.html`, 578 schema: ` 579 CREATE TABLE information_schema.constraint_column_usage ( 580 TABLE_CATALOG STRING NOT NULL, 581 TABLE_SCHEMA STRING NOT NULL, 582 TABLE_NAME STRING NOT NULL, 583 COLUMN_NAME STRING NOT NULL, 584 CONSTRAINT_CATALOG STRING NOT NULL, 585 CONSTRAINT_SCHEMA STRING NOT NULL, 586 CONSTRAINT_NAME STRING NOT NULL 587 )`, 588 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 589 return forEachTableDescWithTableLookup(ctx, p, dbContext, hideVirtual /* no constraints in virtual tables */, func( 590 db *sqlbase.DatabaseDescriptor, 591 scName string, 592 table *sqlbase.TableDescriptor, 593 tableLookup tableLookupFn, 594 ) error { 595 conInfo, err := table.GetConstraintInfoWithLookup(tableLookup.getTableByID) 596 if err != nil { 597 return err 598 } 599 scNameStr := tree.NewDString(scName) 600 dbNameStr := tree.NewDString(db.Name) 601 602 for conName, con := range conInfo { 603 conTable := table 604 conCols := con.Columns 605 conNameStr := tree.NewDString(conName) 606 if con.Kind == sqlbase.ConstraintTypeFK { 607 // For foreign key constraint, constraint_column_usage 608 // identifies the table/columns that the foreign key 609 // references. 610 conTable = con.ReferencedTable 611 conCols, err = conTable.NamesForColumnIDs(con.FK.ReferencedColumnIDs) 612 if err != nil { 613 return err 614 } 615 } 616 tableNameStr := tree.NewDString(conTable.Name) 617 for _, col := range conCols { 618 if err := addRow( 619 dbNameStr, // table_catalog 620 scNameStr, // table_schema 621 tableNameStr, // table_name 622 tree.NewDString(col), // column_name 623 dbNameStr, // constraint_catalog 624 scNameStr, // constraint_schema 625 conNameStr, // constraint_name 626 ); err != nil { 627 return err 628 } 629 } 630 } 631 return nil 632 }) 633 }, 634 } 635 636 // MySQL: https://dev.mysql.com/doc/refman/5.7/en/key-column-usage-table.html 637 var informationSchemaKeyColumnUsageTable = virtualSchemaTable{ 638 comment: `column usage by indexes and key constraints 639 ` + base.DocsURL("information-schema.html#key_column_usage") + ` 640 https://www.postgresql.org/docs/9.5/infoschema-key-column-usage.html`, 641 schema: ` 642 CREATE TABLE information_schema.key_column_usage ( 643 CONSTRAINT_CATALOG STRING NOT NULL, 644 CONSTRAINT_SCHEMA STRING NOT NULL, 645 CONSTRAINT_NAME STRING NOT NULL, 646 TABLE_CATALOG STRING NOT NULL, 647 TABLE_SCHEMA STRING NOT NULL, 648 TABLE_NAME STRING NOT NULL, 649 COLUMN_NAME STRING NOT NULL, 650 ORDINAL_POSITION INT NOT NULL, 651 POSITION_IN_UNIQUE_CONSTRAINT INT 652 )`, 653 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 654 return forEachTableDescWithTableLookup(ctx, p, dbContext, hideVirtual /* no constraints in virtual tables */, func( 655 db *sqlbase.DatabaseDescriptor, 656 scName string, 657 table *sqlbase.TableDescriptor, 658 tableLookup tableLookupFn, 659 ) error { 660 conInfo, err := table.GetConstraintInfoWithLookup(tableLookup.getTableByID) 661 if err != nil { 662 return err 663 } 664 dbNameStr := tree.NewDString(db.Name) 665 scNameStr := tree.NewDString(scName) 666 tbNameStr := tree.NewDString(table.Name) 667 for conName, con := range conInfo { 668 // Only Primary Key, Foreign Key, and Unique constraints are included. 669 switch con.Kind { 670 case sqlbase.ConstraintTypePK: 671 case sqlbase.ConstraintTypeFK: 672 case sqlbase.ConstraintTypeUnique: 673 default: 674 continue 675 } 676 677 cstNameStr := tree.NewDString(conName) 678 679 for pos, col := range con.Columns { 680 ordinalPos := tree.NewDInt(tree.DInt(pos + 1)) 681 uniquePos := tree.DNull 682 if con.Kind == sqlbase.ConstraintTypeFK { 683 uniquePos = ordinalPos 684 } 685 if err := addRow( 686 dbNameStr, // constraint_catalog 687 scNameStr, // constraint_schema 688 cstNameStr, // constraint_name 689 dbNameStr, // table_catalog 690 scNameStr, // table_schema 691 tbNameStr, // table_name 692 tree.NewDString(col), // column_name 693 ordinalPos, // ordinal_position, 1-indexed 694 uniquePos, // position_in_unique_constraint 695 ); err != nil { 696 return err 697 } 698 } 699 } 700 return nil 701 }) 702 }, 703 } 704 705 // Postgres: https://www.postgresql.org/docs/9.6/static/infoschema-parameters.html 706 // MySQL: https://dev.mysql.com/doc/refman/5.7/en/parameters-table.html 707 var informationSchemaParametersTable = virtualSchemaTable{ 708 comment: `built-in function parameters (empty - introspection not yet supported) 709 https://www.postgresql.org/docs/9.5/infoschema-parameters.html`, 710 schema: ` 711 CREATE TABLE information_schema.parameters ( 712 SPECIFIC_CATALOG STRING, 713 SPECIFIC_SCHEMA STRING, 714 SPECIFIC_NAME STRING, 715 ORDINAL_POSITION INT, 716 PARAMETER_MODE STRING, 717 IS_RESULT STRING, 718 AS_LOCATOR STRING, 719 PARAMETER_NAME STRING, 720 DATA_TYPE STRING, 721 CHARACTER_MAXIMUM_LENGTH INT, 722 CHARACTER_OCTET_LENGTH INT, 723 CHARACTER_SET_CATALOG STRING, 724 CHARACTER_SET_SCHEMA STRING, 725 CHARACTER_SET_NAME STRING, 726 COLLATION_CATALOG STRING, 727 COLLATION_SCHEMA STRING, 728 COLLATION_NAME STRING, 729 NUMERIC_PRECISION INT, 730 NUMERIC_PRECISION_RADIX INT, 731 NUMERIC_SCALE INT, 732 DATETIME_PRECISION INT, 733 INTERVAL_TYPE STRING, 734 INTERVAL_PRECISION INT, 735 UDT_CATALOG STRING, 736 UDT_SCHEMA STRING, 737 UDT_NAME STRING, 738 SCOPE_CATALOG STRING, 739 SCOPE_SCHEMA STRING, 740 SCOPE_NAME STRING, 741 MAXIMUM_CARDINALITY INT, 742 DTD_IDENTIFIER STRING, 743 PARAMETER_DEFAULT STRING 744 )`, 745 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 746 return nil 747 }, 748 } 749 750 var ( 751 matchOptionFull = tree.NewDString("FULL") 752 matchOptionPartial = tree.NewDString("PARTIAL") 753 matchOptionNone = tree.NewDString("NONE") 754 755 matchOptionMap = map[sqlbase.ForeignKeyReference_Match]tree.Datum{ 756 sqlbase.ForeignKeyReference_SIMPLE: matchOptionNone, 757 sqlbase.ForeignKeyReference_FULL: matchOptionFull, 758 sqlbase.ForeignKeyReference_PARTIAL: matchOptionPartial, 759 } 760 761 refConstraintRuleNoAction = tree.NewDString("NO ACTION") 762 refConstraintRuleRestrict = tree.NewDString("RESTRICT") 763 refConstraintRuleSetNull = tree.NewDString("SET NULL") 764 refConstraintRuleSetDefault = tree.NewDString("SET DEFAULT") 765 refConstraintRuleCascade = tree.NewDString("CASCADE") 766 ) 767 768 func dStringForFKAction(action sqlbase.ForeignKeyReference_Action) tree.Datum { 769 switch action { 770 case sqlbase.ForeignKeyReference_NO_ACTION: 771 return refConstraintRuleNoAction 772 case sqlbase.ForeignKeyReference_RESTRICT: 773 return refConstraintRuleRestrict 774 case sqlbase.ForeignKeyReference_SET_NULL: 775 return refConstraintRuleSetNull 776 case sqlbase.ForeignKeyReference_SET_DEFAULT: 777 return refConstraintRuleSetDefault 778 case sqlbase.ForeignKeyReference_CASCADE: 779 return refConstraintRuleCascade 780 } 781 panic(errors.Errorf("unexpected ForeignKeyReference_Action: %v", action)) 782 } 783 784 // MySQL: https://dev.mysql.com/doc/refman/5.7/en/referential-constraints-table.html 785 var informationSchemaReferentialConstraintsTable = virtualSchemaTable{ 786 comment: `foreign key constraints 787 ` + base.DocsURL("information-schema.html#referential_constraints") + ` 788 https://www.postgresql.org/docs/9.5/infoschema-referential-constraints.html`, 789 schema: ` 790 CREATE TABLE information_schema.referential_constraints ( 791 CONSTRAINT_CATALOG STRING NOT NULL, 792 CONSTRAINT_SCHEMA STRING NOT NULL, 793 CONSTRAINT_NAME STRING NOT NULL, 794 UNIQUE_CONSTRAINT_CATALOG STRING NOT NULL, 795 UNIQUE_CONSTRAINT_SCHEMA STRING NOT NULL, 796 UNIQUE_CONSTRAINT_NAME STRING, 797 MATCH_OPTION STRING NOT NULL, 798 UPDATE_RULE STRING NOT NULL, 799 DELETE_RULE STRING NOT NULL, 800 TABLE_NAME STRING NOT NULL, 801 REFERENCED_TABLE_NAME STRING NOT NULL 802 )`, 803 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 804 return forEachTableDescWithTableLookup(ctx, p, dbContext, hideVirtual /* no constraints in virtual tables */, func( 805 db *sqlbase.DatabaseDescriptor, 806 scName string, 807 table *sqlbase.TableDescriptor, 808 tableLookup tableLookupFn, 809 ) error { 810 dbNameStr := tree.NewDString(db.Name) 811 scNameStr := tree.NewDString(scName) 812 tbNameStr := tree.NewDString(table.Name) 813 for i := range table.OutboundFKs { 814 fk := &table.OutboundFKs[i] 815 refTable, err := tableLookup.getTableByID(fk.ReferencedTableID) 816 if err != nil { 817 return err 818 } 819 var matchType = tree.DNull 820 if r, ok := matchOptionMap[fk.Match]; ok { 821 matchType = r 822 } 823 referencedIdx, err := sqlbase.FindFKReferencedIndex(refTable, fk.ReferencedColumnIDs) 824 if err != nil { 825 return err 826 } 827 if err := addRow( 828 dbNameStr, // constraint_catalog 829 scNameStr, // constraint_schema 830 tree.NewDString(fk.Name), // constraint_name 831 dbNameStr, // unique_constraint_catalog 832 scNameStr, // unique_constraint_schema 833 tree.NewDString(referencedIdx.Name), // unique_constraint_name 834 matchType, // match_option 835 dStringForFKAction(fk.OnUpdate), // update_rule 836 dStringForFKAction(fk.OnDelete), // delete_rule 837 tbNameStr, // table_name 838 tree.NewDString(refTable.Name), // referenced_table_name 839 ); err != nil { 840 return err 841 } 842 } 843 return nil 844 }) 845 }, 846 } 847 848 // Postgres: https://www.postgresql.org/docs/9.6/static/infoschema-role-table-grants.html 849 // MySQL: missing 850 var informationSchemaRoleTableGrants = virtualSchemaTable{ 851 comment: `privileges granted on table or views (incomplete; see also information_schema.table_privileges; may contain excess users or roles) 852 ` + base.DocsURL("information-schema.html#role_table_grants") + ` 853 https://www.postgresql.org/docs/9.5/infoschema-role-table-grants.html`, 854 schema: ` 855 CREATE TABLE information_schema.role_table_grants ( 856 GRANTOR STRING, 857 GRANTEE STRING NOT NULL, 858 TABLE_CATALOG STRING NOT NULL, 859 TABLE_SCHEMA STRING NOT NULL, 860 TABLE_NAME STRING NOT NULL, 861 PRIVILEGE_TYPE STRING NOT NULL, 862 IS_GRANTABLE STRING, 863 WITH_HIERARCHY STRING 864 )`, 865 // This is the same as information_schema.table_privileges. In postgres, this virtual table does 866 // not show tables with grants provided through PUBLIC, but table_privileges does. 867 // Since we don't have the PUBLIC concept, the two virtual tables are identical. 868 populate: populateTablePrivileges, 869 } 870 871 // MySQL: https://dev.mysql.com/doc/mysql-infoschema-excerpt/5.7/en/routines-table.html 872 var informationSchemaRoutineTable = virtualSchemaTable{ 873 comment: `built-in functions (empty - introspection not yet supported) 874 https://www.postgresql.org/docs/9.5/infoschema-routines.html`, 875 schema: ` 876 CREATE TABLE information_schema.routines ( 877 SPECIFIC_CATALOG STRING, 878 SPECIFIC_SCHEMA STRING, 879 SPECIFIC_NAME STRING, 880 ROUTINE_CATALOG STRING, 881 ROUTINE_SCHEMA STRING, 882 ROUTINE_NAME STRING, 883 ROUTINE_TYPE STRING, 884 MODULE_CATALOG STRING, 885 MODULE_SCHEMA STRING, 886 MODULE_NAME STRING, 887 UDT_CATALOG STRING, 888 UDT_SCHEMA STRING, 889 UDT_NAME STRING, 890 DATA_TYPE STRING, 891 CHARACTER_MAXIMUM_LENGTH INT, 892 CHARACTER_OCTET_LENGTH INT, 893 CHARACTER_SET_CATALOG STRING, 894 CHARACTER_SET_SCHEMA STRING, 895 CHARACTER_SET_NAME STRING, 896 COLLATION_CATALOG STRING, 897 COLLATION_SCHEMA STRING, 898 COLLATION_NAME STRING, 899 NUMERIC_PRECISION INT, 900 NUMERIC_PRECISION_RADIX INT, 901 NUMERIC_SCALE INT, 902 DATETIME_PRECISION INT, 903 INTERVAL_TYPE STRING, 904 INTERVAL_PRECISION STRING, 905 TYPE_UDT_CATALOG STRING, 906 TYPE_UDT_SCHEMA STRING, 907 TYPE_UDT_NAME STRING, 908 SCOPE_CATALOG STRING, 909 SCOPE_NAME STRING, 910 MAXIMUM_CARDINALITY INT, 911 DTD_IDENTIFIER STRING, 912 ROUTINE_BODY STRING, 913 ROUTINE_DEFINITION STRING, 914 EXTERNAL_NAME STRING, 915 EXTERNAL_LANGUAGE STRING, 916 PARAMETER_STYLE STRING, 917 IS_DETERMINISTIC STRING, 918 SQL_DATA_ACCESS STRING, 919 IS_NULL_CALL STRING, 920 SQL_PATH STRING, 921 SCHEMA_LEVEL_ROUTINE STRING, 922 MAX_DYNAMIC_RESULT_SETS INT, 923 IS_USER_DEFINED_CAST STRING, 924 IS_IMPLICITLY_INVOCABLE STRING, 925 SECURITY_TYPE STRING, 926 TO_SQL_SPECIFIC_CATALOG STRING, 927 TO_SQL_SPECIFIC_SCHEMA STRING, 928 TO_SQL_SPECIFIC_NAME STRING, 929 AS_LOCATOR STRING, 930 CREATED TIMESTAMPTZ, 931 LAST_ALTERED TIMESTAMPTZ, 932 NEW_SAVEPOINT_LEVEL STRING, 933 IS_UDT_DEPENDENT STRING, 934 RESULT_CAST_FROM_DATA_TYPE STRING, 935 RESULT_CAST_AS_LOCATOR STRING, 936 RESULT_CAST_CHAR_MAX_LENGTH INT, 937 RESULT_CAST_CHAR_OCTET_LENGTH STRING, 938 RESULT_CAST_CHAR_SET_CATALOG STRING, 939 RESULT_CAST_CHAR_SET_SCHEMA STRING, 940 RESULT_CAST_CHAR_SET_NAME STRING, 941 RESULT_CAST_COLLATION_CATALOG STRING, 942 RESULT_CAST_COLLATION_SCHEMA STRING, 943 RESULT_CAST_COLLATION_NAME STRING, 944 RESULT_CAST_NUMERIC_PRECISION INT, 945 RESULT_CAST_NUMERIC_PRECISION_RADIX INT, 946 RESULT_CAST_NUMERIC_SCALE INT, 947 RESULT_CAST_DATETIME_PRECISION STRING, 948 RESULT_CAST_INTERVAL_TYPE STRING, 949 RESULT_CAST_INTERVAL_PRECISION INT, 950 RESULT_CAST_TYPE_UDT_CATALOG STRING, 951 RESULT_CAST_TYPE_UDT_SCHEMA STRING, 952 RESULT_CAST_TYPE_UDT_NAME STRING, 953 RESULT_CAST_SCOPE_CATALOG STRING, 954 RESULT_CAST_SCOPE_SCHEMA STRING, 955 RESULT_CAST_SCOPE_NAME STRING, 956 RESULT_CAST_MAXIMUM_CARDINALITY INT, 957 RESULT_CAST_DTD_IDENTIFIER STRING 958 )`, 959 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 960 return nil 961 }, 962 } 963 964 // MySQL: https://dev.mysql.com/doc/refman/5.7/en/schemata-table.html 965 var informationSchemaSchemataTable = virtualSchemaTable{ 966 comment: `database schemas (may contain schemata without permission) 967 ` + base.DocsURL("information-schema.html#schemata") + ` 968 https://www.postgresql.org/docs/9.5/infoschema-schemata.html`, 969 schema: vtable.InformationSchemaSchemata, 970 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 971 return forEachDatabaseDesc(ctx, p, dbContext, true, /* requiresPrivileges */ 972 func(db *sqlbase.DatabaseDescriptor) error { 973 return forEachSchemaName(ctx, p, db, func(sc string) error { 974 return addRow( 975 tree.NewDString(db.Name), // catalog_name 976 tree.NewDString(sc), // schema_name 977 tree.DNull, // default_character_set_name 978 tree.DNull, // sql_path 979 ) 980 }) 981 }) 982 }, 983 } 984 985 // MySQL: https://dev.mysql.com/doc/refman/5.7/en/schema-privileges-table.html 986 var informationSchemaSchemataTablePrivileges = virtualSchemaTable{ 987 comment: `schema privileges (incomplete; may contain excess users or roles) 988 ` + base.DocsURL("information-schema.html#schema_privileges"), 989 schema: ` 990 CREATE TABLE information_schema.schema_privileges ( 991 GRANTEE STRING NOT NULL, 992 TABLE_CATALOG STRING NOT NULL, 993 TABLE_SCHEMA STRING NOT NULL, 994 PRIVILEGE_TYPE STRING NOT NULL, 995 IS_GRANTABLE STRING 996 )`, 997 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 998 return forEachDatabaseDesc(ctx, p, dbContext, true, /* requiresPrivileges */ 999 func(db *sqlbase.DatabaseDescriptor) error { 1000 return forEachSchemaName(ctx, p, db, func(scName string) error { 1001 privs := db.Privileges.Show() 1002 dbNameStr := tree.NewDString(db.Name) 1003 scNameStr := tree.NewDString(scName) 1004 // TODO(knz): This should filter for the current user, see 1005 // https://github.com/cockroachdb/cockroach/issues/35572 1006 for _, u := range privs { 1007 userNameStr := tree.NewDString(u.User) 1008 for _, priv := range u.Privileges { 1009 if err := addRow( 1010 userNameStr, // grantee 1011 dbNameStr, // table_catalog 1012 scNameStr, // table_schema 1013 tree.NewDString(priv), // privilege_type 1014 tree.DNull, // is_grantable 1015 ); err != nil { 1016 return err 1017 } 1018 } 1019 } 1020 return nil 1021 }) 1022 }) 1023 }, 1024 } 1025 1026 var ( 1027 indexDirectionNA = tree.NewDString("N/A") 1028 indexDirectionAsc = tree.NewDString(sqlbase.IndexDescriptor_ASC.String()) 1029 indexDirectionDesc = tree.NewDString(sqlbase.IndexDescriptor_DESC.String()) 1030 ) 1031 1032 func dStringForIndexDirection(dir sqlbase.IndexDescriptor_Direction) tree.Datum { 1033 switch dir { 1034 case sqlbase.IndexDescriptor_ASC: 1035 return indexDirectionAsc 1036 case sqlbase.IndexDescriptor_DESC: 1037 return indexDirectionDesc 1038 } 1039 panic("unreachable") 1040 } 1041 1042 var informationSchemaSequences = virtualSchemaTable{ 1043 comment: `sequences 1044 ` + base.DocsURL("information-schema.html#sequences") + ` 1045 https://www.postgresql.org/docs/9.5/infoschema-sequences.html`, 1046 schema: ` 1047 CREATE TABLE information_schema.sequences ( 1048 SEQUENCE_CATALOG STRING NOT NULL, 1049 SEQUENCE_SCHEMA STRING NOT NULL, 1050 SEQUENCE_NAME STRING NOT NULL, 1051 DATA_TYPE STRING NOT NULL, 1052 NUMERIC_PRECISION INT NOT NULL, 1053 NUMERIC_PRECISION_RADIX INT NOT NULL, 1054 NUMERIC_SCALE INT NOT NULL, 1055 START_VALUE STRING NOT NULL, 1056 MINIMUM_VALUE STRING NOT NULL, 1057 MAXIMUM_VALUE STRING NOT NULL, 1058 INCREMENT STRING NOT NULL, 1059 CYCLE_OPTION STRING NOT NULL 1060 )`, 1061 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 1062 return forEachTableDesc(ctx, p, dbContext, hideVirtual, /* no sequences in virtual schemas */ 1063 func(db *sqlbase.DatabaseDescriptor, scName string, table *sqlbase.TableDescriptor) error { 1064 if !table.IsSequence() { 1065 return nil 1066 } 1067 return addRow( 1068 tree.NewDString(db.GetName()), // catalog 1069 tree.NewDString(scName), // schema 1070 tree.NewDString(table.GetName()), // name 1071 tree.NewDString("bigint"), // type 1072 tree.NewDInt(64), // numeric precision 1073 tree.NewDInt(2), // numeric precision radix 1074 tree.NewDInt(0), // numeric scale 1075 tree.NewDString(strconv.FormatInt(table.SequenceOpts.Start, 10)), // start value 1076 tree.NewDString(strconv.FormatInt(table.SequenceOpts.MinValue, 10)), // min value 1077 tree.NewDString(strconv.FormatInt(table.SequenceOpts.MaxValue, 10)), // max value 1078 tree.NewDString(strconv.FormatInt(table.SequenceOpts.Increment, 10)), // increment 1079 noString, // cycle 1080 ) 1081 }) 1082 }, 1083 } 1084 1085 // Postgres: missing 1086 // MySQL: https://dev.mysql.com/doc/refman/5.7/en/statistics-table.html 1087 var informationSchemaStatisticsTable = virtualSchemaTable{ 1088 comment: `index metadata and statistics (incomplete) 1089 ` + base.DocsURL("information-schema.html#statistics"), 1090 schema: ` 1091 CREATE TABLE information_schema.statistics ( 1092 TABLE_CATALOG STRING NOT NULL, 1093 TABLE_SCHEMA STRING NOT NULL, 1094 TABLE_NAME STRING NOT NULL, 1095 NON_UNIQUE STRING NOT NULL, 1096 INDEX_SCHEMA STRING NOT NULL, 1097 INDEX_NAME STRING NOT NULL, 1098 SEQ_IN_INDEX INT NOT NULL, 1099 COLUMN_NAME STRING NOT NULL, 1100 "COLLATION" STRING, 1101 CARDINALITY INT, 1102 DIRECTION STRING NOT NULL, 1103 STORING STRING NOT NULL, 1104 IMPLICIT STRING NOT NULL 1105 )`, 1106 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 1107 return forEachTableDesc(ctx, p, dbContext, hideVirtual, /* virtual tables have no indexes */ 1108 func(db *sqlbase.DatabaseDescriptor, scName string, table *sqlbase.TableDescriptor) error { 1109 dbNameStr := tree.NewDString(db.GetName()) 1110 scNameStr := tree.NewDString(scName) 1111 tbNameStr := tree.NewDString(table.GetName()) 1112 1113 appendRow := func(index *sqlbase.IndexDescriptor, colName string, sequence int, 1114 direction tree.Datum, isStored, isImplicit bool, 1115 ) error { 1116 return addRow( 1117 dbNameStr, // table_catalog 1118 scNameStr, // table_schema 1119 tbNameStr, // table_name 1120 yesOrNoDatum(!index.Unique), // non_unique 1121 scNameStr, // index_schema 1122 tree.NewDString(index.Name), // index_name 1123 tree.NewDInt(tree.DInt(sequence)), // seq_in_index 1124 tree.NewDString(colName), // column_name 1125 tree.DNull, // collation 1126 tree.DNull, // cardinality 1127 direction, // direction 1128 yesOrNoDatum(isStored), // storing 1129 yesOrNoDatum(isImplicit), // implicit 1130 ) 1131 } 1132 1133 return forEachIndexInTable(table, func(index *sqlbase.IndexDescriptor) error { 1134 // Columns in the primary key that aren't in index.ColumnNames or 1135 // index.StoreColumnNames are implicit columns in the index. 1136 var implicitCols map[string]struct{} 1137 var hasImplicitCols bool 1138 if index.HasOldStoredColumns() { 1139 // Old STORING format: implicit columns are extra columns minus stored 1140 // columns. 1141 hasImplicitCols = len(index.ExtraColumnIDs) > len(index.StoreColumnNames) 1142 } else { 1143 // New STORING format: implicit columns are extra columns. 1144 hasImplicitCols = len(index.ExtraColumnIDs) > 0 1145 } 1146 if hasImplicitCols { 1147 implicitCols = make(map[string]struct{}) 1148 for _, col := range table.PrimaryIndex.ColumnNames { 1149 implicitCols[col] = struct{}{} 1150 } 1151 } 1152 1153 sequence := 1 1154 for i, col := range index.ColumnNames { 1155 // We add a row for each column of index. 1156 dir := dStringForIndexDirection(index.ColumnDirections[i]) 1157 if err := appendRow(index, col, sequence, dir, false, false); err != nil { 1158 return err 1159 } 1160 sequence++ 1161 delete(implicitCols, col) 1162 } 1163 for _, col := range index.StoreColumnNames { 1164 // We add a row for each stored column of index. 1165 if err := appendRow(index, col, sequence, 1166 indexDirectionNA, true, false); err != nil { 1167 return err 1168 } 1169 sequence++ 1170 delete(implicitCols, col) 1171 } 1172 for col := range implicitCols { 1173 // We add a row for each implicit column of index. 1174 if err := appendRow(index, col, sequence, 1175 indexDirectionAsc, false, true); err != nil { 1176 return err 1177 } 1178 sequence++ 1179 } 1180 return nil 1181 }) 1182 }) 1183 }, 1184 } 1185 1186 // MySQL: https://dev.mysql.com/doc/refman/5.7/en/table-constraints-table.html 1187 var informationSchemaTableConstraintTable = virtualSchemaTable{ 1188 comment: `table constraints 1189 ` + base.DocsURL("information-schema.html#table_constraints") + ` 1190 https://www.postgresql.org/docs/9.5/infoschema-table-constraints.html`, 1191 schema: ` 1192 CREATE TABLE information_schema.table_constraints ( 1193 CONSTRAINT_CATALOG STRING NOT NULL, 1194 CONSTRAINT_SCHEMA STRING NOT NULL, 1195 CONSTRAINT_NAME STRING NOT NULL, 1196 TABLE_CATALOG STRING NOT NULL, 1197 TABLE_SCHEMA STRING NOT NULL, 1198 TABLE_NAME STRING NOT NULL, 1199 CONSTRAINT_TYPE STRING NOT NULL, 1200 IS_DEFERRABLE STRING NOT NULL, 1201 INITIALLY_DEFERRED STRING NOT NULL 1202 )`, 1203 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 1204 h := makeOidHasher() 1205 return forEachTableDescWithTableLookup(ctx, p, dbContext, hideVirtual, /* virtual tables have no constraints */ 1206 func( 1207 db *sqlbase.DatabaseDescriptor, 1208 scName string, 1209 table *sqlbase.TableDescriptor, 1210 tableLookup tableLookupFn, 1211 ) error { 1212 conInfo, err := table.GetConstraintInfoWithLookup(tableLookup.getTableByID) 1213 if err != nil { 1214 return err 1215 } 1216 1217 dbNameStr := tree.NewDString(db.Name) 1218 scNameStr := tree.NewDString(scName) 1219 tbNameStr := tree.NewDString(table.Name) 1220 1221 for conName, c := range conInfo { 1222 if err := addRow( 1223 dbNameStr, // constraint_catalog 1224 scNameStr, // constraint_schema 1225 tree.NewDString(conName), // constraint_name 1226 dbNameStr, // table_catalog 1227 scNameStr, // table_schema 1228 tbNameStr, // table_name 1229 tree.NewDString(string(c.Kind)), // constraint_type 1230 yesOrNoDatum(false), // is_deferrable 1231 yesOrNoDatum(false), // initially_deferred 1232 ); err != nil { 1233 return err 1234 } 1235 } 1236 1237 // Unlike with pg_catalog.pg_constraint, Postgres also includes NOT 1238 // NULL column constraints in information_schema.check_constraints. 1239 // Cockroach doesn't track these constraints as check constraints, 1240 // but we can pull them off of the table's column descriptors. 1241 colNum := 0 1242 return forEachColumnInTable(table, func(col *sqlbase.ColumnDescriptor) error { 1243 colNum++ 1244 // NOT NULL column constraints are implemented as a CHECK in postgres. 1245 conNameStr := tree.NewDString(fmt.Sprintf( 1246 "%s_%s_%d_not_null", h.NamespaceOid(db, scName), tableOid(table.ID), colNum, 1247 )) 1248 if !col.Nullable { 1249 if err := addRow( 1250 dbNameStr, // constraint_catalog 1251 scNameStr, // constraint_schema 1252 conNameStr, // constraint_name 1253 dbNameStr, // table_catalog 1254 scNameStr, // table_schema 1255 tbNameStr, // table_name 1256 tree.NewDString("CHECK"), // constraint_type 1257 yesOrNoDatum(false), // is_deferrable 1258 yesOrNoDatum(false), // initially_deferred 1259 ); err != nil { 1260 return err 1261 } 1262 } 1263 return nil 1264 }) 1265 }) 1266 }, 1267 } 1268 1269 // Postgres: not provided 1270 // MySQL: https://dev.mysql.com/doc/refman/5.7/en/user-privileges-table.html 1271 // TODO(knz): this introspection facility is of dubious utility. 1272 var informationSchemaUserPrivileges = virtualSchemaTable{ 1273 comment: `grantable privileges (incomplete)`, 1274 schema: ` 1275 CREATE TABLE information_schema.user_privileges ( 1276 GRANTEE STRING NOT NULL, 1277 TABLE_CATALOG STRING NOT NULL, 1278 PRIVILEGE_TYPE STRING NOT NULL, 1279 IS_GRANTABLE STRING 1280 )`, 1281 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 1282 return forEachDatabaseDesc(ctx, p, dbContext, true, /* requiresPrivileges */ 1283 func(dbDesc *DatabaseDescriptor) error { 1284 dbNameStr := tree.NewDString(dbDesc.Name) 1285 for _, u := range []string{security.RootUser, sqlbase.AdminRole} { 1286 grantee := tree.NewDString(u) 1287 for _, p := range privilege.List(privilege.ByValue[:]).SortedNames() { 1288 if err := addRow( 1289 grantee, // grantee 1290 dbNameStr, // table_catalog 1291 tree.NewDString(p), // privilege_type 1292 tree.DNull, // is_grantable 1293 ); err != nil { 1294 return err 1295 } 1296 } 1297 } 1298 return nil 1299 }) 1300 }, 1301 } 1302 1303 // MySQL: https://dev.mysql.com/doc/refman/5.7/en/table-privileges-table.html 1304 var informationSchemaTablePrivileges = virtualSchemaTable{ 1305 comment: `privileges granted on table or views (incomplete; may contain excess users or roles) 1306 ` + base.DocsURL("information-schema.html#table_privileges") + ` 1307 https://www.postgresql.org/docs/9.5/infoschema-table-privileges.html`, 1308 schema: ` 1309 CREATE TABLE information_schema.table_privileges ( 1310 GRANTOR STRING, 1311 GRANTEE STRING NOT NULL, 1312 TABLE_CATALOG STRING NOT NULL, 1313 TABLE_SCHEMA STRING NOT NULL, 1314 TABLE_NAME STRING NOT NULL, 1315 PRIVILEGE_TYPE STRING NOT NULL, 1316 IS_GRANTABLE STRING, 1317 WITH_HIERARCHY STRING NOT NULL 1318 )`, 1319 populate: populateTablePrivileges, 1320 } 1321 1322 // populateTablePrivileges is used to populate both table_privileges and role_table_grants. 1323 func populateTablePrivileges( 1324 ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error, 1325 ) error { 1326 return forEachTableDesc(ctx, p, dbContext, virtualMany, 1327 func(db *sqlbase.DatabaseDescriptor, scName string, table *sqlbase.TableDescriptor) error { 1328 dbNameStr := tree.NewDString(db.Name) 1329 scNameStr := tree.NewDString(scName) 1330 tbNameStr := tree.NewDString(table.Name) 1331 // TODO(knz): This should filter for the current user, see 1332 // https://github.com/cockroachdb/cockroach/issues/35572 1333 for _, u := range table.Privileges.Show() { 1334 for _, priv := range u.Privileges { 1335 if err := addRow( 1336 tree.DNull, // grantor 1337 tree.NewDString(u.User), // grantee 1338 dbNameStr, // table_catalog 1339 scNameStr, // table_schema 1340 tbNameStr, // table_name 1341 tree.NewDString(priv), // privilege_type 1342 tree.DNull, // is_grantable 1343 yesOrNoDatum(priv == "SELECT"), // with_hierarchy 1344 ); err != nil { 1345 return err 1346 } 1347 } 1348 } 1349 return nil 1350 }) 1351 } 1352 1353 var ( 1354 tableTypeSystemView = tree.NewDString("SYSTEM VIEW") 1355 tableTypeBaseTable = tree.NewDString("BASE TABLE") 1356 tableTypeView = tree.NewDString("VIEW") 1357 tableTypeTemporary = tree.NewDString("LOCAL TEMPORARY") 1358 ) 1359 1360 var informationSchemaTablesTable = virtualSchemaTable{ 1361 comment: `tables and views 1362 ` + base.DocsURL("information-schema.html#tables") + ` 1363 https://www.postgresql.org/docs/9.5/infoschema-tables.html`, 1364 schema: vtable.InformationSchemaTables, 1365 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 1366 return forEachTableDesc(ctx, p, dbContext, virtualMany, addTablesTableRow(addRow)) 1367 }, 1368 indexes: []virtualIndex{ 1369 { 1370 populate: func(ctx context.Context, constraint tree.Datum, p *planner, db *DatabaseDescriptor, 1371 addRow func(...tree.Datum) error) (bool, error) { 1372 // This index is on the TABLE_NAME column. 1373 name := tree.MustBeDString(constraint) 1374 desc, err := resolver.ResolveExistingTableObject(ctx, p, tree.NewUnqualifiedTableName(tree.Name(name)), 1375 tree.ObjectLookupFlags{}, resolver.ResolveAnyDescType) 1376 if err != nil || desc == nil { 1377 return false, err 1378 } 1379 schemaName, err := resolver.ResolveSchemaNameByID(ctx, p.txn, p.ExecCfg().Codec, db.ID, desc.GetParentSchemaID()) 1380 if err != nil { 1381 return false, err 1382 } 1383 return true, addTablesTableRow(addRow)(db, schemaName, desc.TableDesc()) 1384 }, 1385 }, 1386 }, 1387 } 1388 1389 func addTablesTableRow( 1390 addRow func(...tree.Datum) error, 1391 ) func(db *sqlbase.DatabaseDescriptor, scName string, 1392 table *sqlbase.TableDescriptor) error { 1393 return func(db *sqlbase.DatabaseDescriptor, scName string, table *sqlbase.TableDescriptor) error { 1394 if table.IsSequence() { 1395 return nil 1396 } 1397 tableType := tableTypeBaseTable 1398 insertable := yesString 1399 if table.IsVirtualTable() { 1400 tableType = tableTypeSystemView 1401 insertable = noString 1402 } else if table.IsView() { 1403 tableType = tableTypeView 1404 insertable = noString 1405 } else if table.Temporary { 1406 tableType = tableTypeTemporary 1407 } 1408 dbNameStr := tree.NewDString(db.Name) 1409 scNameStr := tree.NewDString(scName) 1410 tbNameStr := tree.NewDString(table.Name) 1411 return addRow( 1412 dbNameStr, // table_catalog 1413 scNameStr, // table_schema 1414 tbNameStr, // table_name 1415 tableType, // table_type 1416 insertable, // is_insertable_into 1417 tree.NewDInt(tree.DInt(table.Version)), // version 1418 ) 1419 } 1420 } 1421 1422 // Postgres: https://www.postgresql.org/docs/9.6/static/infoschema-views.html 1423 // MySQL: https://dev.mysql.com/doc/refman/5.7/en/views-table.html 1424 var informationSchemaViewsTable = virtualSchemaTable{ 1425 comment: `views (incomplete) 1426 ` + base.DocsURL("information-schema.html#views") + ` 1427 https://www.postgresql.org/docs/9.5/infoschema-views.html`, 1428 schema: ` 1429 CREATE TABLE information_schema.views ( 1430 TABLE_CATALOG STRING NOT NULL, 1431 TABLE_SCHEMA STRING NOT NULL, 1432 TABLE_NAME STRING NOT NULL, 1433 VIEW_DEFINITION STRING NOT NULL, 1434 CHECK_OPTION STRING, 1435 IS_UPDATABLE STRING NOT NULL, 1436 IS_INSERTABLE_INTO STRING NOT NULL, 1437 IS_TRIGGER_UPDATABLE STRING NOT NULL, 1438 IS_TRIGGER_DELETABLE STRING NOT NULL, 1439 IS_TRIGGER_INSERTABLE_INTO STRING NOT NULL 1440 )`, 1441 populate: func(ctx context.Context, p *planner, dbContext *DatabaseDescriptor, addRow func(...tree.Datum) error) error { 1442 return forEachTableDesc(ctx, p, dbContext, hideVirtual, /* virtual schemas have no views */ 1443 func(db *sqlbase.DatabaseDescriptor, scName string, table *sqlbase.TableDescriptor) error { 1444 if !table.IsView() { 1445 return nil 1446 } 1447 // Note that the view query printed will not include any column aliases 1448 // specified outside the initial view query into the definition returned, 1449 // unlike Postgres. For example, for the view created via 1450 // `CREATE VIEW (a) AS SELECT b FROM foo` 1451 // we'll only print `SELECT b FROM foo` as the view definition here, 1452 // while Postgres would more accurately print `SELECT b AS a FROM foo`. 1453 // TODO(a-robinson): Insert column aliases into view query once we 1454 // have a semantic query representation to work with (#10083). 1455 return addRow( 1456 tree.NewDString(db.Name), // table_catalog 1457 tree.NewDString(scName), // table_schema 1458 tree.NewDString(table.Name), // table_name 1459 tree.NewDString(table.ViewQuery), // view_definition 1460 tree.DNull, // check_option 1461 noString, // is_updatable 1462 noString, // is_insertable_into 1463 noString, // is_trigger_updatable 1464 noString, // is_trigger_deletable 1465 noString, // is_trigger_insertable_into 1466 ) 1467 }) 1468 }, 1469 } 1470 1471 // forEachSchemaName iterates over the physical and virtual schemas. 1472 func forEachSchemaName( 1473 ctx context.Context, p *planner, db *sqlbase.DatabaseDescriptor, fn func(string) error, 1474 ) error { 1475 schemaNames, err := getSchemaNames(ctx, p, db) 1476 if err != nil { 1477 return err 1478 } 1479 vtableEntries := p.getVirtualTabler().getEntries() 1480 scNames := make([]string, 0, len(schemaNames)+len(vtableEntries)) 1481 for _, name := range schemaNames { 1482 scNames = append(scNames, name) 1483 } 1484 for _, schema := range vtableEntries { 1485 scNames = append(scNames, schema.desc.Name) 1486 } 1487 sort.Strings(scNames) 1488 for _, sc := range scNames { 1489 if err := fn(sc); err != nil { 1490 return err 1491 } 1492 } 1493 return nil 1494 } 1495 1496 // forEachDatabaseDesc calls a function for the given DatabaseDescriptor, or if 1497 // it is nil, retrieves all database descriptors and iterates through them in 1498 // lexicographical order with respect to their name. If privileges are required, 1499 // the function is only called if the user has privileges on the database. 1500 func forEachDatabaseDesc( 1501 ctx context.Context, 1502 p *planner, 1503 dbContext *DatabaseDescriptor, 1504 requiresPrivileges bool, 1505 fn func(*sqlbase.DatabaseDescriptor) error, 1506 ) error { 1507 var dbDescs []*sqlbase.DatabaseDescriptor 1508 if dbContext == nil { 1509 allDbDescs, err := p.Tables().GetAllDatabaseDescriptors(ctx, p.txn) 1510 if err != nil { 1511 return err 1512 } 1513 dbDescs = allDbDescs 1514 } else { 1515 // We can't just use dbContext here because we need to fetch the descriptor 1516 // with privileges from kv. 1517 fetchedDbDesc, err := catalogkv.GetDatabaseDescriptorsFromIDs(ctx, p.txn, p.ExecCfg().Codec, []sqlbase.ID{dbContext.ID}) 1518 if err != nil { 1519 return err 1520 } 1521 dbDescs = fetchedDbDesc 1522 } 1523 1524 // Ignore databases that the user cannot see. 1525 for _, dbDesc := range dbDescs { 1526 if !requiresPrivileges || userCanSeeDatabase(ctx, p, dbDesc) { 1527 if err := fn(dbDesc); err != nil { 1528 return err 1529 } 1530 } 1531 } 1532 1533 return nil 1534 } 1535 1536 // forEachTypeDesc calls a function for each TypeDescriptor. If dbContext is 1537 // not nil, then the function is called for only TypeDescriptors within the 1538 // given database. 1539 func forEachTypeDesc( 1540 ctx context.Context, 1541 p *planner, 1542 dbContext *DatabaseDescriptor, 1543 fn func(db *DatabaseDescriptor, sc string, typ *TypeDescriptor) error, 1544 ) error { 1545 descs, err := p.Tables().GetAllDescriptors(ctx, p.txn) 1546 if err != nil { 1547 return err 1548 } 1549 schemaNames, err := getSchemaNames(ctx, p, dbContext) 1550 if err != nil { 1551 return err 1552 } 1553 lCtx := newInternalLookupCtx(descs, dbContext) 1554 for _, id := range lCtx.typIDs { 1555 typ := lCtx.typDescs[id] 1556 dbDesc, parentExists := lCtx.dbDescs[typ.ParentID] 1557 if !parentExists { 1558 continue 1559 } 1560 scName, ok := schemaNames[typ.GetParentSchemaID()] 1561 if !ok { 1562 return errors.AssertionFailedf("schema id %d not found", typ.GetParentSchemaID()) 1563 } 1564 if err := fn(dbDesc, scName, typ); err != nil { 1565 return err 1566 } 1567 } 1568 return nil 1569 } 1570 1571 // forEachTableDesc retrieves all table descriptors from the current 1572 // database and all system databases and iterates through them. For 1573 // each table, the function will call fn with its respective database 1574 // and table descriptor. 1575 // 1576 // The dbContext argument specifies in which database context we are 1577 // requesting the descriptors. In context nil all descriptors are 1578 // visible, in non-empty contexts only the descriptors of that 1579 // database are visible. 1580 // 1581 // The virtualOpts argument specifies how virtual tables are made 1582 // visible. 1583 func forEachTableDesc( 1584 ctx context.Context, 1585 p *planner, 1586 dbContext *DatabaseDescriptor, 1587 virtualOpts virtualOpts, 1588 fn func(*sqlbase.DatabaseDescriptor, string, *sqlbase.TableDescriptor) error, 1589 ) error { 1590 return forEachTableDescWithTableLookup(ctx, p, dbContext, virtualOpts, func( 1591 db *sqlbase.DatabaseDescriptor, 1592 scName string, 1593 table *sqlbase.TableDescriptor, 1594 _ tableLookupFn, 1595 ) error { 1596 return fn(db, scName, table) 1597 }) 1598 } 1599 1600 type virtualOpts int 1601 1602 const ( 1603 // virtualMany iterates over virtual schemas in every catalog/database. 1604 virtualMany virtualOpts = iota 1605 // virtualOnce iterates over virtual schemas once, in the nil database. 1606 virtualOnce 1607 // hideVirtual completely hides virtual schemas during iteration. 1608 hideVirtual 1609 ) 1610 1611 // forEachTableDescAll does the same as forEachTableDesc but also 1612 // includes newly added non-public descriptors. 1613 func forEachTableDescAll( 1614 ctx context.Context, 1615 p *planner, 1616 dbContext *DatabaseDescriptor, 1617 virtualOpts virtualOpts, 1618 fn func(*sqlbase.DatabaseDescriptor, string, *sqlbase.TableDescriptor) error, 1619 ) error { 1620 return forEachTableDescAllWithTableLookup(ctx, 1621 p, dbContext, virtualOpts, 1622 func( 1623 db *sqlbase.DatabaseDescriptor, 1624 scName string, 1625 table *sqlbase.TableDescriptor, 1626 _ tableLookupFn, 1627 ) error { 1628 return fn(db, scName, table) 1629 }) 1630 } 1631 1632 // forEachTableDescAllWithTableLookup is like forEachTableDescAll, but it also 1633 // provides a tableLookupFn like forEachTableDescWithTableLookup. 1634 func forEachTableDescAllWithTableLookup( 1635 ctx context.Context, 1636 p *planner, 1637 dbContext *DatabaseDescriptor, 1638 virtualOpts virtualOpts, 1639 fn func(*sqlbase.DatabaseDescriptor, string, *sqlbase.TableDescriptor, tableLookupFn) error, 1640 ) error { 1641 return forEachTableDescWithTableLookupInternal(ctx, 1642 p, dbContext, virtualOpts, true /* allowAdding */, fn) 1643 } 1644 1645 // forEachTableDescWithTableLookup acts like forEachTableDesc, except it also provides a 1646 // tableLookupFn when calling fn to allow callers to lookup fetched table descriptors 1647 // on demand. This is important for callers dealing with objects like foreign keys, where 1648 // the metadata for each object must be augmented by looking at the referenced table. 1649 // 1650 // The dbContext argument specifies in which database context we are 1651 // requesting the descriptors. In context "" all descriptors are 1652 // visible, in non-empty contexts only the descriptors of that 1653 // database are visible. 1654 func forEachTableDescWithTableLookup( 1655 ctx context.Context, 1656 p *planner, 1657 dbContext *DatabaseDescriptor, 1658 virtualOpts virtualOpts, 1659 fn func(*sqlbase.DatabaseDescriptor, string, *sqlbase.TableDescriptor, tableLookupFn) error, 1660 ) error { 1661 return forEachTableDescWithTableLookupInternal(ctx, p, dbContext, virtualOpts, false /* allowAdding */, fn) 1662 } 1663 1664 func getSchemaNames( 1665 ctx context.Context, p *planner, dbContext *DatabaseDescriptor, 1666 ) (map[sqlbase.ID]string, error) { 1667 if dbContext != nil { 1668 return p.Tables().GetSchemasForDatabase(ctx, p.txn, dbContext.ID) 1669 } 1670 ret := make(map[sqlbase.ID]string) 1671 dbs, err := p.Tables().GetAllDatabaseDescriptors(ctx, p.txn) 1672 if err != nil { 1673 return nil, err 1674 } 1675 for _, db := range dbs { 1676 schemas, err := p.Tables().GetSchemasForDatabase(ctx, p.txn, db.ID) 1677 if err != nil { 1678 return nil, err 1679 } 1680 for id, name := range schemas { 1681 ret[id] = name 1682 } 1683 } 1684 return ret, nil 1685 } 1686 1687 // forEachTableDescWithTableLookupInternal is the logic that supports 1688 // forEachTableDescWithTableLookup. 1689 // 1690 // The allowAdding argument if true includes newly added tables that 1691 // are not yet public. 1692 func forEachTableDescWithTableLookupInternal( 1693 ctx context.Context, 1694 p *planner, 1695 dbContext *DatabaseDescriptor, 1696 virtualOpts virtualOpts, 1697 allowAdding bool, 1698 fn func(*DatabaseDescriptor, string, *TableDescriptor, tableLookupFn) error, 1699 ) error { 1700 descs, err := p.Tables().GetAllDescriptors(ctx, p.txn) 1701 if err != nil { 1702 return err 1703 } 1704 lCtx := newInternalLookupCtx(descs, dbContext) 1705 1706 if virtualOpts == virtualMany || virtualOpts == virtualOnce { 1707 // Virtual descriptors first. 1708 vt := p.getVirtualTabler() 1709 vEntries := vt.getEntries() 1710 vSchemaNames := vt.getSchemaNames() 1711 iterate := func(dbDesc *DatabaseDescriptor) error { 1712 for _, virtSchemaName := range vSchemaNames { 1713 e := vEntries[virtSchemaName] 1714 for _, tName := range e.orderedDefNames { 1715 te := e.defs[tName] 1716 if err := fn(dbDesc, virtSchemaName, te.desc, lCtx); err != nil { 1717 return err 1718 } 1719 } 1720 } 1721 return nil 1722 } 1723 1724 switch virtualOpts { 1725 case virtualOnce: 1726 if err := iterate(nil); err != nil { 1727 return err 1728 } 1729 case virtualMany: 1730 for _, dbID := range lCtx.dbIDs { 1731 dbDesc := lCtx.dbDescs[dbID] 1732 if err := iterate(dbDesc); err != nil { 1733 return err 1734 } 1735 } 1736 } 1737 } 1738 1739 // Generate all schema names, and keep a mapping. 1740 schemaNames, err := getSchemaNames(ctx, p, dbContext) 1741 if err != nil { 1742 return err 1743 } 1744 1745 // Physical descriptors next. 1746 for _, tbID := range lCtx.tbIDs { 1747 table := lCtx.tbDescs[tbID] 1748 dbDesc, parentExists := lCtx.dbDescs[table.GetParentID()] 1749 if table.Dropped() || !userCanSeeTable(ctx, p, table, allowAdding) || !parentExists { 1750 continue 1751 } 1752 scName, ok := schemaNames[table.GetParentSchemaID()] 1753 if !ok { 1754 return errors.AssertionFailedf("schema id %d not found", table.GetParentSchemaID()) 1755 } 1756 if err := fn(dbDesc, scName, table, lCtx); err != nil { 1757 return err 1758 } 1759 } 1760 return nil 1761 } 1762 1763 func forEachIndexInTable( 1764 table *sqlbase.TableDescriptor, fn func(*sqlbase.IndexDescriptor) error, 1765 ) error { 1766 if table.IsPhysicalTable() { 1767 if err := fn(&table.PrimaryIndex); err != nil { 1768 return err 1769 } 1770 } 1771 for i := range table.Indexes { 1772 if err := fn(&table.Indexes[i]); err != nil { 1773 return err 1774 } 1775 } 1776 return nil 1777 } 1778 1779 func forEachColumnInTable( 1780 table *sqlbase.TableDescriptor, fn func(*sqlbase.ColumnDescriptor) error, 1781 ) error { 1782 // Table descriptors already hold columns in-order. 1783 for i := range table.Columns { 1784 if err := fn(&table.Columns[i]); err != nil { 1785 return err 1786 } 1787 } 1788 return nil 1789 } 1790 1791 func forEachColumnInIndex( 1792 table *sqlbase.TableDescriptor, 1793 index *sqlbase.IndexDescriptor, 1794 fn func(*sqlbase.ColumnDescriptor) error, 1795 ) error { 1796 colMap := make(map[sqlbase.ColumnID]*sqlbase.ColumnDescriptor, len(table.Columns)) 1797 for i := range table.Columns { 1798 id := table.Columns[i].ID 1799 colMap[id] = &table.Columns[i] 1800 } 1801 for _, columnID := range index.ColumnIDs { 1802 column := colMap[columnID] 1803 if err := fn(column); err != nil { 1804 return err 1805 } 1806 } 1807 return nil 1808 } 1809 1810 func forEachRole( 1811 ctx context.Context, p *planner, fn func(username string, isRole bool, noLogin bool) error, 1812 ) error { 1813 query := ` 1814 SELECT 1815 username, 1816 "isRole", 1817 EXISTS( 1818 SELECT 1819 option 1820 FROM 1821 system.role_options AS r 1822 WHERE 1823 r.username = u.username AND option = 'NOLOGIN' 1824 ) 1825 AS nologin 1826 FROM 1827 system.users AS u; 1828 ` 1829 rows, err := p.ExtendedEvalContext().ExecCfg.InternalExecutor.Query( 1830 ctx, "read-roles", p.txn, query, 1831 ) 1832 1833 if err != nil { 1834 return err 1835 } 1836 1837 for _, row := range rows { 1838 username := tree.MustBeDString(row[0]) 1839 isRole, ok := row[1].(*tree.DBool) 1840 if !ok { 1841 return errors.Errorf("isRole should be a boolean value, found %s instead", row[1].ResolvedType()) 1842 } 1843 noLogin, ok := row[2].(*tree.DBool) 1844 if !ok { 1845 return errors.Errorf("noLogin should be a boolean value, found %s instead", row[1].ResolvedType()) 1846 } 1847 if err := fn(string(username), bool(*isRole), bool(*noLogin)); err != nil { 1848 return err 1849 } 1850 } 1851 1852 return nil 1853 } 1854 1855 func forEachRoleMembership( 1856 ctx context.Context, p *planner, fn func(role, member string, isAdmin bool) error, 1857 ) error { 1858 query := `SELECT "role", "member", "isAdmin" FROM system.role_members` 1859 rows, err := p.ExtendedEvalContext().ExecCfg.InternalExecutor.Query( 1860 ctx, "read-members", p.txn, query, 1861 ) 1862 if err != nil { 1863 return err 1864 } 1865 1866 for _, row := range rows { 1867 roleName := tree.MustBeDString(row[0]) 1868 memberName := tree.MustBeDString(row[1]) 1869 isAdmin := row[2].(*tree.DBool) 1870 1871 if err := fn(string(roleName), string(memberName), bool(*isAdmin)); err != nil { 1872 return err 1873 } 1874 } 1875 return nil 1876 } 1877 1878 func userCanSeeDatabase(ctx context.Context, p *planner, db *sqlbase.DatabaseDescriptor) bool { 1879 return p.CheckAnyPrivilege(ctx, db) == nil 1880 } 1881 1882 func userCanSeeTable( 1883 ctx context.Context, p *planner, table *sqlbase.TableDescriptor, allowAdding bool, 1884 ) bool { 1885 return tableIsVisible(table, allowAdding) && p.CheckAnyPrivilege(ctx, table) == nil 1886 } 1887 1888 func tableIsVisible(table *TableDescriptor, allowAdding bool) bool { 1889 return table.State == sqlbase.TableDescriptor_PUBLIC || 1890 (allowAdding && table.State == sqlbase.TableDescriptor_ADD) 1891 }