func (c *conn) getTableDesc(table *parser.TableName) (*structured.TableDescriptor, error) { if err := c.normalizeTableName(table); err != nil { return nil, err } dbID, err := c.lookupDatabase(table.Qualifier) if err != nil { return nil, err } gr, err := c.db.Get(keys.MakeNameMetadataKey(dbID, table.Name)) if err != nil { return nil, err } if !gr.Exists() { return nil, fmt.Errorf("table \"%s\" does not exist", table) } descKey := gr.ValueBytes() desc := structured.TableDescriptor{} if err := c.db.GetProto(descKey, &desc); err != nil { return nil, err } if err := structured.ValidateTableDesc(desc); err != nil { return nil, err } return &desc, nil }
func (db *DB) getTableDesc(path string) (*structured.TableDescriptor, error) { nsID, name, err := db.lookupTable(path) if err != nil { return nil, err } if name == "" { return nil, fmt.Errorf("empty table name: %s", path) } gr, err := db.Get(keys.MakeNameMetadataKey(nsID, name)) if err != nil { return nil, err } if !gr.Exists() { return nil, fmt.Errorf("unable to find table \"%s\"", path) } descKey := gr.ValueBytes() desc := structured.TableDescriptor{} if err := db.GetProto(descKey, &desc); err != nil { return nil, err } if err := structured.ValidateTableDesc(desc); err != nil { return nil, err } return &desc, nil }
func (c *conn) CreateTable(p *parser.CreateTable, args []driver.Value) (*rows, error) { if err := c.normalizeTableName(p.Table); err != nil { return nil, err } dbID, err := c.lookupDatabase(p.Table.Qualifier) if err != nil { return nil, err } schema, err := makeSchema(p) if err != nil { return nil, err } desc := structured.TableDescFromSchema(schema) if err := structured.ValidateTableDesc(desc); err != nil { return nil, err } nameKey := keys.MakeNameMetadataKey(dbID, p.Table.Name) // This isn't strictly necessary as the conditional put below will fail if // the key already exists, but it seems good to avoid the table ID allocation // in most cases when the table already exists. if gr, err := c.db.Get(nameKey); err != nil { return nil, err } else if gr.Exists() { if p.IfNotExists { return &rows{}, nil } return nil, fmt.Errorf("table \"%s\" already exists", p.Table) } ir, err := c.db.Inc(keys.DescIDGenerator, 1) if err != nil { return nil, err } desc.ID = uint32(ir.ValueInt() - 1) // TODO(pmattis): Be cognizant of error messages when this is ported to the // server. The error currently returned below is likely going to be difficult // to interpret. err = c.db.Txn(func(txn *client.Txn) error { descKey := keys.MakeDescMetadataKey(desc.ID) b := &client.Batch{} b.CPut(nameKey, descKey, nil) b.Put(descKey, &desc) return txn.Commit(b) }) if err != nil { // TODO(pmattis): Need to handle if-not-exists here as well. return nil, err } return &rows{}, nil }
// RenameTable renames a table. Old path and new path have the form // "<namespace>.<table>". func (db *DB) RenameTable(oldPath, newPath string) error { // TODO(pmattis): Should we allow both the old and new name to exist // simultaneously for a period of time? The thought is to allow an // application to access the table via either name while the application is // being upgraded. Alternatively, instead of a rename table operation perhaps // there should be a link table operation which adds a "hard link" to the // table. Similar to a file, a table would not be removed until all of the // hard links are removed. oldNSID, oldName, err := db.lookupTable(oldPath) if err != nil { return err } newNSID, newName, err := db.lookupTable(newPath) if err != nil { return err } if newName == "" { return fmt.Errorf("empty table name: %s", newPath) } return db.Txn(func(txn *Txn) error { oldNameKey := keys.MakeNameMetadataKey(oldNSID, oldName) gr, err := txn.Get(oldNameKey) if err != nil { return err } if !gr.Exists() { return fmt.Errorf("unable to find table \"%s\"", oldPath) } descKey := gr.ValueBytes() desc := structured.TableDescriptor{} if err := txn.GetProto(descKey, &desc); err != nil { return err } desc.Name = strings.ToLower(newPath) if err := structured.ValidateTableDesc(desc); err != nil { return err } newNameKey := keys.MakeNameMetadataKey(newNSID, newName) b := &Batch{} b.Put(descKey, &desc) // If the new name already exists the conditional put will fail causing the // transaction to fail. b.CPut(newNameKey, descKey, nil) b.Del(oldNameKey) return txn.Commit(b) }) }
// CreateTable creates a table from the specified schema. Table creation will // fail if the table name is already in use. The table name is required to have // the form "<namespace>.<table>". func (db *DB) CreateTable(schema structured.TableSchema) error { schema.Name = strings.ToLower(schema.Name) desc := structured.TableDescFromSchema(schema) if err := structured.ValidateTableDesc(desc); err != nil { return err } nsID, name, err := db.lookupTable(desc.Name) if err != nil { return err } if name == "" { return fmt.Errorf("empty table name: %s", desc.Name) } nameKey := keys.MakeNameMetadataKey(nsID, name) // This isn't strictly necessary as the conditional put below will fail if // the key already exists, but it seems good to avoid the table ID allocation // in most cases when the table already exists. if gr, err := db.Get(nameKey); err != nil { return err } else if gr.Exists() { return fmt.Errorf("table \"%s\" already exists", desc.Name) } ir, err := db.Inc(keys.DescIDGenerator, 1) if err != nil { return err } desc.ID = uint32(ir.ValueInt() - 1) // TODO(pmattis): Be cognizant of error messages when this is ported to the // server. The error currently returned below is likely going to be difficult // to interpret. return db.Txn(func(txn *Txn) error { descKey := keys.MakeDescMetadataKey(desc.ID) b := &Batch{} b.CPut(nameKey, descKey, nil) b.Put(descKey, &desc) return txn.Commit(b) }) }