// getTableID retrieves the table ID for the specified table. func getTableID(p *planner, tn *parser.TableName) (sqlbase.ID, error) { if err := tn.QualifyWithDatabase(p.session.Database); err != nil { return 0, err } virtual, err := p.session.virtualSchemas.getVirtualTableDesc(tn) if err != nil { return 0, err } if virtual != nil { return virtual.GetID(), nil } dbID, err := p.getDatabaseID(tn.Database()) if err != nil { return 0, err } nameKey := tableKey{dbID, tn.Table()} key := nameKey.Key() gr, err := p.txn.Get(key) if err != nil { return 0, err } if !gr.Exists() { return 0, sqlbase.NewUndefinedTableError(parser.AsString(tn)) } return sqlbase.ID(gr.ValueInt()), nil }
// getVirtualTableEntry checks if the provided name matches a virtual database/table // pair. The function will return the table's virtual table entry if the name matches // a specific table. It will return an error if the name references a virtual database // but the table is non-existent. func (vs *virtualSchemaHolder) getVirtualTableEntry( tn *parser.TableName, ) (virtualTableEntry, error) { if db, ok := vs.getVirtualSchemaEntry(tn.DatabaseName.Normalize()); ok { if t, ok := db.tables[tn.TableName.Normalize()]; ok { return t, nil } return virtualTableEntry{}, sqlbase.NewUndefinedTableError(tn.String()) } return virtualTableEntry{}, nil }
// mustGetTableOrViewDesc implements the SchemaAccessor interface. func (p *planner) mustGetTableOrViewDesc(tn *parser.TableName) (*sqlbase.TableDescriptor, error) { desc, err := p.getTableOrViewDesc(tn) if err != nil { return nil, err } if desc == nil { return nil, sqlbase.NewUndefinedTableError(tn.String()) } if err := filterTableState(desc); err != nil { return nil, err } return desc, nil }
func mustGetTableDesc( txn *client.Txn, vt VirtualTabler, tn *parser.TableName, ) (*sqlbase.TableDescriptor, error) { desc, err := getTableDesc(txn, vt, tn) if err != nil { return nil, err } if desc == nil { return nil, sqlbase.NewUndefinedTableError(tn.String()) } if err := filterTableState(desc); err != nil { return nil, err } return desc, nil }
// getTableLeaseByID is a by-ID variant of getTableLease (i.e. uses same cache). func (p *planner) getTableLeaseByID(tableID sqlbase.ID) (*sqlbase.TableDescriptor, error) { if log.V(2) { log.Infof(p.ctx(), "planner acquiring lease on table ID %d", tableID) } if testDisableTableLeases { table, err := sqlbase.GetTableDescFromID(p.txn, tableID) if err != nil { return nil, err } if err := filterTableState(table); err != nil { return nil, err } return table, nil } // First, look to see if we already have a lease for this table -- including // leases acquired via `getTableLease`. var lease *LeaseState for _, l := range p.leases { if l.ID == tableID { lease = l if log.V(2) { log.Infof(p.ctx(), "found lease in planner cache for table %d", tableID) } break } } // If we didn't find a lease or the lease is about to expire, acquire one. if lease == nil || p.removeLeaseIfExpiring(lease) { var err error lease, err = p.leaseMgr.Acquire(p.txn, tableID, 0) if err != nil { if err == sqlbase.ErrDescriptorNotFound { // Transform the descriptor error into an error that references the // table's ID. return nil, sqlbase.NewUndefinedTableError(fmt.Sprintf("<id=%d>", tableID)) } return nil, err } p.leases = append(p.leases, lease) // If the lease we just acquired expires before the txn's deadline, reduce // the deadline. p.txn.UpdateDeadlineMaybe(hlc.Timestamp{WallTime: lease.Expiration().UnixNano()}) } return &lease.TableDescriptor, nil }
// ShowGrants returns grant details for the specified objects and users. // TODO(marc): implement no targets (meaning full scan). // Privileges: None. // Notes: postgres does not have a SHOW GRANTS statement. // mysql only returns the user's privileges. func (p *planner) ShowGrants(n *parser.ShowGrants) (planNode, error) { if n.Targets == nil { return nil, errors.Errorf("TODO(marc): implement SHOW GRANT with no targets") } objectType := "Database" if n.Targets.Tables != nil { objectType = "Table" } columns := ResultColumns{ {Name: objectType, Typ: parser.TypeString}, {Name: "User", Typ: parser.TypeString}, {Name: "Privileges", Typ: parser.TypeString}, } return &delayedNode{ p: p, name: "SHOW GRANTS", columns: columns, constructor: func(p *planner) (planNode, error) { v := p.newContainerValuesNode(columns, 0) // Check if the target exists. checkFn := func(sql string, args ...interface{}) (bool, error) { values, err := p.queryRowsAsRoot(sql, args...) if err != nil { return false, err } if len(values) > 0 { return true, nil } return false, nil } queryFn := func(sql string, args ...interface{}) error { rows, err := p.queryRows(sql, args...) if err != nil { return err } for _, r := range rows { if _, err := v.rows.AddRow(r); err != nil { return err } } return nil } // Get grants of database from information_schema.schema_privileges // if the type of target is database. if n.Targets.Databases != nil { // TODO(nvanbenschoten): Clean up parameter assignment throughout. var params []interface{} var paramHolders []string paramSeq := 1 for _, db := range n.Targets.Databases.ToStrings() { exists, err := checkFn(checkSchema, db) if err != nil { v.rows.Close() return nil, err } if !exists { v.rows.Close() return nil, sqlbase.NewUndefinedDatabaseError(db) } paramHolders = append(paramHolders, fmt.Sprintf("$%d", paramSeq)) paramSeq++ params = append(params, db) } schemaGrants := fmt.Sprintf(`SELECT TABLE_SCHEMA AS "Database", GRANTEE AS "User", PRIVILEGE_TYPE AS "Privileges" FROM information_schema.schema_privileges WHERE TABLE_SCHEMA IN (%s)`, strings.Join(paramHolders, ",")) if n.Grantees != nil { paramHolders = paramHolders[:0] for _, grantee := range n.Grantees.ToStrings() { paramHolders = append(paramHolders, fmt.Sprintf("$%d", paramSeq)) params = append(params, grantee) paramSeq++ } schemaGrants = fmt.Sprintf(`%s AND GRANTEE IN(%s)`, schemaGrants, strings.Join(paramHolders, ",")) } if err := queryFn(schemaGrants, params...); err != nil { v.rows.Close() return nil, err } } // Get grants of table from information_schema.table_privileges // if the type of target is table. if n.Targets.Tables != nil { // TODO(nvanbenschoten): Clean up parameter assignment throughout. var params []interface{} var paramHolders []string paramSeq := 1 for _, tableTarget := range n.Targets.Tables { tableGlob, err := tableTarget.NormalizeTablePattern() if err != nil { v.rows.Close() return nil, err } tables, err := p.expandTableGlob(tableGlob) if err != nil { v.rows.Close() return nil, err } for i := range tables { exists, err := checkFn(checkTable, tables[i].Database(), tables[i].Table()) if err != nil { v.rows.Close() return nil, err } if !exists { v.rows.Close() return nil, sqlbase.NewUndefinedTableError(tables[i].String()) } paramHolders = append(paramHolders, fmt.Sprintf("($%d,$%d)", paramSeq, paramSeq+1)) params = append(params, tables[i].Database(), tables[i].Table()) paramSeq += 2 } } tableGrants := fmt.Sprintf(`SELECT TABLE_NAME, GRANTEE, PRIVILEGE_TYPE FROM information_schema.table_privileges WHERE (TABLE_SCHEMA, TABLE_NAME) IN (%s)`, strings.Join(paramHolders, ",")) if n.Grantees != nil { paramHolders = paramHolders[:0] for _, grantee := range n.Grantees.ToStrings() { paramHolders = append(paramHolders, fmt.Sprintf("$%d", paramSeq)) params = append(params, grantee) paramSeq++ } tableGrants = fmt.Sprintf(`%s AND GRANTEE IN(%s)`, tableGrants, strings.Join(paramHolders, ",")) } if err := queryFn(tableGrants, params...); err != nil { v.rows.Close() return nil, err } } // Sort the result by target name, user name and privileges. sort := &sortNode{ ctx: p.ctx(), p: p, ordering: sqlbase.ColumnOrdering{ {ColIdx: 0, Direction: encoding.Ascending}, {ColIdx: 1, Direction: encoding.Ascending}, {ColIdx: 2, Direction: encoding.Ascending}, }, columns: v.columns, } return &selectTopNode{source: v, sort: sort}, nil }, }, nil }
// ShowColumns of a table. // Privileges: Any privilege on table. // Notes: postgres does not have a SHOW COLUMNS statement. // mysql only returns columns you have privileges on. func (p *planner) ShowColumns(n *parser.ShowColumns) (planNode, error) { tn, err := n.Table.NormalizeWithDatabaseName(p.session.Database) if err != nil { return nil, err } columns := ResultColumns{ {Name: "Field", Typ: parser.TypeString}, {Name: "Type", Typ: parser.TypeString}, {Name: "Null", Typ: parser.TypeBool}, {Name: "Default", Typ: parser.TypeString}, } return &delayedNode{ p: p, name: "SHOW COLUMNS FROM " + tn.String(), columns: columns, constructor: func(p *planner) (planNode, error) { const getColumns = `SELECT COLUMN_NAME AS "Field", DATA_TYPE AS "Type", (IS_NULLABLE!='NO') AS "Null",` + ` COLUMN_DEFAULT AS "Default" FROM information_schema.columns WHERE TABLE_SCHEMA=$1 AND TABLE_NAME=$2` + ` ORDER BY ORDINAL_POSITION` { // Check if the database exists by using the security.RootUser. values, err := p.queryRowsAsRoot(checkSchema, tn.Database()) if err != nil { return nil, err } if len(values) == 0 { return nil, sqlbase.NewUndefinedDatabaseError(tn.Database()) } } { // Check if the table exists by using the security.RootUser. values, err := p.queryRowsAsRoot(checkTable, tn.Database(), tn.Table()) if err != nil { return nil, err } if len(values) == 0 { return nil, sqlbase.NewUndefinedTableError(tn.String()) } } // Check if the user has been granted. // Skip the checking if the table is a virtual table. { virDesc, err := p.session.virtualSchemas.getVirtualTableDesc(tn) if err != nil { return nil, err } if virDesc == nil { values, err := p.QueryRow(checkTablePrivilege, tn.Database(), tn.Table(), p.session.User) if err != nil { return nil, err } if len(values) == 0 { return nil, fmt.Errorf("user %s has no privileges on table %s", p.session.User, tn.String()) } } } // Temporarily set the current database to get visibility into // information_schema if the current user isn't root. origDatabase := p.evalCtx.Database p.evalCtx.Database = tn.Database() defer func() { p.evalCtx.Database = origDatabase }() // Get columns of table from information_schema.columns. rows, err := p.queryRows(getColumns, tn.Database(), tn.Table()) if err != nil { return nil, err } v := p.newContainerValuesNode(columns, 0) for _, r := range rows { if _, err := v.rows.AddRow(r); err != nil { v.rows.Close() return nil, err } } return v, nil }, }, nil }
// getTableLease implements the SchemaAccessor interface. func (p *planner) getTableLease(tn *parser.TableName) (*sqlbase.TableDescriptor, error) { if log.V(2) { log.Infof(p.ctx(), "planner acquiring lease on table '%s'", tn) } isSystemDB := tn.Database() == sqlbase.SystemDB.Name isVirtualDB := p.session.virtualSchemas.isVirtualDatabase(tn.Database()) if isSystemDB || isVirtualDB || testDisableTableLeases { // We don't go through the normal lease mechanism for: // - system tables. The system.lease and system.descriptor table, in // particular, are problematic because they are used for acquiring // leases itself, creating a chicken&egg problem. // - virtual tables. These tables' descriptors are not persisted, // so they cannot be leased. Instead, we simply return the static // descriptor and rely on the immutability privileges set on the // descriptors to cause upper layers to reject mutations statements. tbl, err := p.mustGetTableDesc(tn) if err != nil { return nil, err } if err := filterTableState(tbl); err != nil { return nil, err } return tbl, nil } dbID, err := p.getDatabaseID(tn.Database()) if err != nil { return nil, err } // First, look to see if we already have a lease for this table. // This ensures that, once a SQL transaction resolved name N to id X, it will // continue to use N to refer to X even if N is renamed during the // transaction. var lease *LeaseState for _, l := range p.leases { if parser.ReNormalizeName(l.Name) == tn.TableName.Normalize() && l.ParentID == dbID { lease = l if log.V(2) { log.Infof(p.ctx(), "found lease in planner cache for table '%s'", tn) } break } } // If we didn't find a lease or the lease is about to expire, acquire one. if lease == nil || p.removeLeaseIfExpiring(lease) { var err error lease, err = p.leaseMgr.AcquireByName(p.txn, dbID, tn.Table()) if err != nil { if err == sqlbase.ErrDescriptorNotFound { // Transform the descriptor error into an error that references the // table's name. return nil, sqlbase.NewUndefinedTableError(tn.String()) } return nil, err } p.leases = append(p.leases, lease) if log.V(2) { log.Infof(p.ctx(), "added lease on table '%s' to planner cache", tn) } // If the lease we just acquired expires before the txn's deadline, reduce // the deadline. p.txn.UpdateDeadlineMaybe(hlc.Timestamp{WallTime: lease.Expiration().UnixNano()}) } return &lease.TableDescriptor, nil }
// RenameTable renames the table or view. // Privileges: DROP on source table/view, CREATE on destination database. // Notes: postgres requires the table owner. // mysql requires ALTER, DROP on the original table, and CREATE, INSERT // on the new table (and does not copy privileges over). func (p *planner) RenameTable(n *parser.RenameTable) (planNode, error) { oldTn, err := n.Name.NormalizeWithDatabaseName(p.session.Database) if err != nil { return nil, err } newTn, err := n.NewName.NormalizeWithDatabaseName(p.session.Database) if err != nil { return nil, err } dbDesc, err := p.mustGetDatabaseDesc(oldTn.Database()) if err != nil { return nil, err } // Check if source table or view exists. // Note that Postgres's behavior here is a little lenient - it'll let you // modify views by running ALTER TABLE, but won't let you modify tables // by running ALTER VIEW. Our behavior is strict for now, but can be // made more lenient down the road if needed. var tableDesc *sqlbase.TableDescriptor if n.IsView { tableDesc, err = p.getViewDesc(oldTn) if err != nil { return nil, err } if tableDesc == nil { if n.IfExists { // Noop. return &emptyNode{}, nil } // Key does not exist, but we want it to: error out. return nil, sqlbase.NewUndefinedViewError(oldTn.String()) } if tableDesc.State != sqlbase.TableDescriptor_PUBLIC { return nil, sqlbase.NewUndefinedViewError(oldTn.String()) } } else { tableDesc, err = p.getTableDesc(oldTn) if err != nil { return nil, err } if tableDesc == nil { if n.IfExists { // Noop. return &emptyNode{}, nil } // Key does not exist, but we want it to: error out. return nil, sqlbase.NewUndefinedTableError(oldTn.String()) } if tableDesc.State != sqlbase.TableDescriptor_PUBLIC { return nil, sqlbase.NewUndefinedTableError(oldTn.String()) } } if err := p.checkPrivilege(tableDesc, privilege.DROP); err != nil { return nil, err } // Check if any views depend on this table/view. Because our views // are currently just stored as strings, they explicitly specify the name // of everything they depend on. Rather than trying to rewrite the view's // query with the new name, we simply disallow such renames for now. if len(tableDesc.DependedOnBy) > 0 { return nil, p.dependentViewRenameError( tableDesc.TypeName(), oldTn.String(), tableDesc.ParentID, tableDesc.DependedOnBy[0].ID) } // Check if target database exists. targetDbDesc, err := p.mustGetDatabaseDesc(newTn.Database()) if err != nil { return nil, err } if err := p.checkPrivilege(targetDbDesc, privilege.CREATE); err != nil { return nil, err } // oldTn and newTn are already normalized, so we can compare directly here. if oldTn.Database() == newTn.Database() && oldTn.Table() == newTn.Table() { // Noop. return &emptyNode{}, nil } tableDesc.SetName(newTn.Table()) tableDesc.ParentID = targetDbDesc.ID descKey := sqlbase.MakeDescMetadataKey(tableDesc.GetID()) newTbKey := tableKey{targetDbDesc.ID, newTn.Table()}.Key() if err := tableDesc.Validate(p.txn); err != nil { return nil, err } descID := tableDesc.GetID() descDesc := sqlbase.WrapDescriptor(tableDesc) if err := tableDesc.SetUpVersion(); err != nil { return nil, err } renameDetails := sqlbase.TableDescriptor_RenameInfo{ OldParentID: dbDesc.ID, OldName: oldTn.Table()} tableDesc.Renames = append(tableDesc.Renames, renameDetails) if err := p.writeTableDesc(tableDesc); err != nil { return nil, err } // We update the descriptor to the new name, but also leave the mapping of the // old name to the id, so that the name is not reused until the schema changer // has made sure it's not in use any more. b := &client.Batch{} b.Put(descKey, descDesc) b.CPut(newTbKey, descID, nil) if err := p.txn.Run(b); err != nil { if _, ok := err.(*roachpb.ConditionFailedError); ok { return nil, sqlbase.NewRelationAlreadyExistsError(newTn.Table()) } return nil, err } p.notifySchemaChange(tableDesc.ID, sqlbase.InvalidMutationID) p.setTestingVerifyMetadata(func(systemConfig config.SystemConfig) error { if err := expectDescriptorID(systemConfig, newTbKey, descID); err != nil { return err } if err := expectDescriptor(systemConfig, descKey, descDesc); err != nil { return err } return nil }) return &emptyNode{}, nil }