func TestCreateTable(t *testing.T) { defer leaktest.AfterTest(t) s, db := setup() defer s.Stop() if err := db.CreateNamespace("t"); err != nil { t.Fatal(err) } if _, err := db.DescribeTable("t.users"); !isError(err, "unable to find table") { t.Fatalf("expected failure, but found '%+v'", err) } schema := makeTestSchema("t.users") if err := db.CreateTable(schema); err != nil { t.Fatal(err) } // Table names are case-insensitive. schema2, err := db.DescribeTable("T.USERS") if err != nil { t.Fatal(err) } if !reflect.DeepEqual(schema, *schema2) { t.Errorf("expected %+v, but got %+v", schema, schema2) } // Creating the table again should fail as the table already exists. if err := db.CreateTable(schema); !isError(err, "table .* already exists") { t.Fatalf("expected failure, but found '%+v'", err) } // Verify we were allocated a non-reserved table ID. This involves manually // retrieving the descriptor. Don't do this at home kiddies. gr, err := db.Get(keys.MakeNameMetadataKey(0, "t")) if err != nil { t.Fatal(err) } gr, err = db.Get(keys.MakeNameMetadataKey(uint32(gr.ValueInt()), "users")) if err != nil { t.Fatal(err) } desc := structured.TableDescriptor{} if err := db.GetProto(gr.ValueBytes(), &desc); err != nil { t.Fatal(err) } if desc.ID <= structured.MaxReservedDescID { t.Errorf("expected a non-reserved table ID, but got %d", desc.ID) } }
// 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. func (p *planner) CreateTable(n *parser.CreateTable) (planNode, error) { var err error n.Table, err = p.normalizeTableName(n.Table) if err != nil { return nil, err } dbDesc, err := p.getDatabaseDesc(n.Table.Database()) if err != nil { return nil, err } desc, err := makeTableDesc(n) if err != nil { return nil, err } if err := desc.AllocateIDs(); err != nil { return nil, err } nameKey := keys.MakeNameMetadataKey(dbDesc.ID, n.Table.Table()) if err := p.writeDescriptor(nameKey, &desc, n.IfNotExists); err != nil { return nil, err } return &valuesNode{}, nil }
// CreateDatabase creates a database. func (p *planner) CreateDatabase(n *parser.CreateDatabase) (planNode, error) { if n.Name == "" { return nil, errEmptyDatabaseName } nameKey := keys.MakeNameMetadataKey(structured.RootNamespaceID, strings.ToLower(string(n.Name))) if gr, err := p.db.Get(nameKey); err != nil { return nil, err } else if gr.Exists() { if n.IfNotExists { return &valuesNode{}, nil } return nil, fmt.Errorf("database \"%s\" already exists", n.Name) } ir, err := p.db.Inc(keys.DescIDGenerator, 1) if err != nil { return nil, err } nsID := uint32(ir.ValueInt() - 1) // TODO(pmattis): Need to handle if-not-exists here as well. if err := p.db.CPut(nameKey, nsID, nil); err != nil { return nil, err } return &valuesNode{}, nil }
func (c *conn) ShowTables(p *parser.ShowTables, args []driver.Value) (*rows, error) { if p.Name == "" { if c.database == "" { return nil, fmt.Errorf("no database specified") } p.Name = c.database } dbID, err := c.lookupDatabase(p.Name) if err != nil { return nil, err } prefix := keys.MakeNameMetadataKey(dbID, "") sr, err := c.db.Scan(prefix, prefix.PrefixEnd(), 0) if err != nil { return nil, err } names := make([]string, len(sr)) for i, row := range sr { names[i] = string(bytes.TrimPrefix(row.Key, prefix)) } return &rows{ columns: []string{"tables"}, rows: names, pos: -1, }, nil }
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 }
// DeleteTable deletes the specified table. Path has the form // "<namespace>.<table>". func (db *DB) DeleteTable(path string) error { nsID, name, err := db.lookupTable(path) if err != nil { return err } if name == "" { return fmt.Errorf("empty table name: %s", path) } nameKey := keys.MakeNameMetadataKey(nsID, name) gr, err := db.Get(nameKey) if err != nil { return err } if !gr.Exists() { return fmt.Errorf("unable to find table \"%s\"", path) } descKey := gr.ValueBytes() desc := structured.TableDescriptor{} if err := db.GetProto(descKey, &desc); err != nil { return err } panic("TODO(pmattis): delete all of the tables rows") // return db.Del(descKey) }
// CreateDatabase creates a database if it doesn't exist. func (s *Server) CreateDatabase(session *Session, p *parser.CreateDatabase, args []sqlwire.Datum, resp *sqlwire.Response) { if p.Name == "" { resp.SetGoError(errors.New("empty database name")) return } nameKey := keys.MakeNameMetadataKey(structured.RootNamespaceID, strings.ToLower(p.Name)) if gr, err := s.db.Get(nameKey); err != nil { resp.SetGoError(err) return } else if gr.Exists() { if p.IfNotExists { return } resp.SetGoError(fmt.Errorf("database \"%s\" already exists", p.Name)) return } ir, err := s.db.Inc(keys.DescIDGenerator, 1) if err != nil { resp.SetGoError(err) return } nsID := uint32(ir.ValueInt() - 1) if err := s.db.CPut(nameKey, nsID, nil); err != nil { // TODO(pmattis): Need to handle if-not-exists here as well. resp.SetGoError(err) return } }
// ShowTables returns all the tables. func (s *Server) ShowTables(session *Session, p *parser.ShowTables, args []sqlwire.Datum, resp *sqlwire.Response) error { if p.Name == nil { if session.Database == "" { return errNoDatabase } p.Name = append(p.Name, session.Database) } dbID, err := s.lookupDatabase(p.Name.String()) if err != nil { return err } prefix := keys.MakeNameMetadataKey(dbID, "") sr, err := s.db.Scan(prefix, prefix.PrefixEnd(), 0) if err != nil { return err } var rows []sqlwire.Result_Row for _, row := range sr { name := string(bytes.TrimPrefix(row.Key, prefix)) rows = append(rows, sqlwire.Result_Row{ Values: []sqlwire.Datum{ {StringVal: &name}, }, }) } resp.Results = []sqlwire.Result{ { Columns: []string{"tables"}, Rows: rows, }, } return nil }
func (s *Server) getTableDesc(database string, qname parser.QualifiedName) ( *structured.TableDescriptor, error) { var err error qname, err = s.normalizeTableName(database, qname) if err != nil { return nil, err } dbID, err := s.lookupDatabase(qname.Database()) if err != nil { return nil, err } gr, err := s.db.Get(keys.MakeNameMetadataKey(dbID, qname.Table())) if err != nil { return nil, err } if !gr.Exists() { return nil, fmt.Errorf("table \"%s\" does not exist", qname) } descKey := gr.ValueBytes() desc := structured.TableDescriptor{} if err := s.db.GetProto(descKey, &desc); err != nil { return nil, err } if err := desc.Validate(); 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) CreateDatabase(p *parser.CreateDatabase, args []driver.Value) (*rows, error) { if p.Name == "" { return nil, fmt.Errorf("empty database name") } nameKey := keys.MakeNameMetadataKey(structured.RootNamespaceID, strings.ToLower(p.Name)) 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("database \"%s\" already exists", p.Name) } ir, err := c.db.Inc(keys.DescIDGenerator, 1) if err != nil { return nil, err } nsID := uint32(ir.ValueInt() - 1) if err := c.db.CPut(nameKey, nsID, nil); err != nil { // TODO(pmattis): Need to handle if-not-exists here as well. return nil, err } return &rows{}, nil }
// getDatabaseDesc looks up the database descriptor given its name. func (p *planner) getDatabaseDesc(name string) (*structured.DatabaseDescriptor, error) { nameKey := keys.MakeNameMetadataKey(structured.RootNamespaceID, name) desc := structured.DatabaseDescriptor{} if err := p.getDescriptor(nameKey, &desc); err != nil { return nil, err } return &desc, nil }
// CreateTable creates a table. func (p *planner) CreateTable(n *parser.CreateTable) (planNode, error) { var err error n.Table, err = p.normalizeTableName(n.Table) if err != nil { return nil, err } dbID, err := p.lookupDatabase(n.Table.Database()) if err != nil { return nil, err } desc, err := makeTableDesc(n) if err != nil { return nil, err } if err := desc.AllocateIDs(); err != nil { return nil, err } nameKey := keys.MakeNameMetadataKey(dbID, n.Table.Table()) // 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 := p.db.Get(nameKey); err != nil { return nil, err } else if gr.Exists() { if n.IfNotExists { return &valuesNode{}, nil } return nil, fmt.Errorf("table \"%s\" already exists", n.Table) } ir, err := p.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. // TODO(pmattis): Need to handle if-not-exists here as well. err = p.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 { return nil, err } return &valuesNode{}, nil }
func (c *conn) lookupDatabase(name string) (uint32, error) { nameKey := keys.MakeNameMetadataKey(structured.RootNamespaceID, name) gr, err := c.db.Get(nameKey) if err != nil { return 0, err } else if !gr.Exists() { return 0, fmt.Errorf("database \"%s\" does not exist", name) } return uint32(gr.ValueInt()), 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 }
// ListNamespaces lists the namespaces. func (db *DB) ListNamespaces() ([]string, error) { prefix := keys.MakeNameMetadataKey(structured.RootNamespaceID, "") rows, err := db.Scan(prefix, prefix.PrefixEnd(), 0) if err != nil { return nil, err } names := make([]string, len(rows)) for i, row := range rows { names[i] = string(bytes.TrimPrefix(row.Key, prefix)) } return names, nil }
func (c *conn) ShowDatabases(p *parser.ShowDatabases, args []driver.Value) (*rows, error) { prefix := keys.MakeNameMetadataKey(structured.RootNamespaceID, "") sr, err := c.db.Scan(prefix, prefix.PrefixEnd(), 0) if err != nil { return nil, err } names := make([]string, len(sr)) for i, row := range sr { names[i] = string(bytes.TrimPrefix(row.Key, prefix)) } return newSingleColumnRows("database", names), nil }
// CreateDatabase creates a database. func (p *planner) CreateDatabase(n *parser.CreateDatabase) (planNode, error) { if n.Name == "" { return nil, errEmptyDatabaseName } nameKey := keys.MakeNameMetadataKey(structured.RootNamespaceID, string(n.Name)) desc := makeDatabaseDesc(n) if err := p.writeDescriptor(nameKey, &desc, n.IfNotExists); err != nil { return nil, err } return &valuesNode{}, nil }
// ShowDatabases returns all the databases. func (p *planner) ShowDatabases(n *parser.ShowDatabases) (planNode, error) { prefix := keys.MakeNameMetadataKey(structured.RootNamespaceID, "") sr, err := p.db.Scan(prefix, prefix.PrefixEnd(), 0) if err != nil { return nil, err } v := &valuesNode{columns: []string{"Database"}} for _, row := range sr { name := string(bytes.TrimPrefix(row.Key, prefix)) v.rows = append(v.rows, []parser.Datum{parser.DString(name)}) } return v, nil }
// CreateTable creates a table if it doesn't already exist. func (s *Server) CreateTable(session *Session, p *parser.CreateTable, args []sqlwire.Datum, resp *sqlwire.Response) error { if err := s.normalizeTableName(session.Database, p.Table); err != nil { return err } dbID, err := s.lookupDatabase(p.Table.Qualifier) if err != nil { return err } desc, err := makeTableDesc(p) if err != nil { return err } if err := desc.AllocateIDs(); err != nil { return 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 := s.db.Get(nameKey); err != nil { return err } else if gr.Exists() { if p.IfNotExists { return nil } return fmt.Errorf("table \"%s\" already exists", p.Table) } ir, err := s.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. // TODO(pmattis): Need to handle if-not-exists here as well. return s.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) }) }
func (db *DB) lookupTable(path string) (nsID uint32, name string, err error) { parts := strings.Split(strings.ToLower(path), ".") if len(parts) != 2 { return 0, "", fmt.Errorf("\"%s\" malformed, expected \"<namespace>.<table>\"", path) } nameKey := keys.MakeNameMetadataKey(nsID, parts[0]) gr, err := db.Get(nameKey) if err != nil { return 0, "", err } else if !gr.Exists() { return 0, "", fmt.Errorf("namespace \"%s\" does not exist", parts[0]) } nsID = uint32(gr.ValueInt()) return nsID, parts[1], nil }
// ListTables lists the tables in the specified namespace. func (db *DB) ListTables(namespace string) ([]string, error) { nsID, _, err := db.lookupTable(namespace + ".") if err != nil { return nil, err } prefix := keys.MakeNameMetadataKey(nsID, "") rows, err := db.Scan(prefix, prefix.PrefixEnd(), 0) if err != nil { return nil, err } names := make([]string, len(rows)) for i, row := range rows { names[i] = string(bytes.TrimPrefix(row.Key, prefix)) } return names, nil }
func (p *planner) getTableDesc(qname *parser.QualifiedName) ( *structured.TableDescriptor, error) { if err := p.normalizeTableName(qname); err != nil { return nil, err } dbDesc, err := p.getDatabaseDesc(qname.Database()) if err != nil { return nil, err } nameKey := keys.MakeNameMetadataKey(dbDesc.ID, qname.Table()) desc := structured.TableDescriptor{} if err := p.getDescriptor(nameKey, &desc); err != nil { return nil, err } return &desc, nil }
// CreateNamespace creates a new namespace. // // TODO(pmattis): Is "namespace" the correct terminology? PostgreSQL and MySQL // use "database". PostgreSQL also has the notion of a schema. A // fully-qualified name in PostgreSQL looks like // "<database>.<schema>.<table>". See // http://www.postgresql.org/docs/9.4/static/ddl-schemas.html. func (db *DB) CreateNamespace(name string) error { if name == "" { return fmt.Errorf("empty namespace name") } nameKey := keys.MakeNameMetadataKey(structured.RootNamespaceID, strings.ToLower(name)) if gr, err := db.Get(nameKey); err != nil { return err } else if gr.Exists() { return fmt.Errorf("namespace \"%s\" already exists", name) } ir, err := db.Inc(keys.DescIDGenerator, 1) if err != nil { return err } nsID := uint32(ir.ValueInt() - 1) return db.CPut(nameKey, nsID, nil) }
// 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) }) }
// CreateDatabase creates a database if it doesn't exist. func (s *Server) CreateDatabase(session *Session, p *parser.CreateDatabase, args []driver.Datum, resp *driver.Response) error { if p.Name == "" { return errEmptyDatabaseName } nameKey := keys.MakeNameMetadataKey(structured.RootNamespaceID, strings.ToLower(p.Name)) if gr, err := s.db.Get(nameKey); err != nil { return err } else if gr.Exists() { if p.IfNotExists { return nil } return fmt.Errorf("database \"%s\" already exists", p.Name) } ir, err := s.db.Inc(keys.DescIDGenerator, 1) if err != nil { return err } nsID := uint32(ir.ValueInt() - 1) // TODO(pmattis): Need to handle if-not-exists here as well. return s.db.CPut(nameKey, nsID, nil) }
// ShowTables returns all the tables. func (p *planner) ShowTables(n *parser.ShowTables) (planNode, error) { if n.Name == nil { if p.session.Database == "" { return nil, errNoDatabase } n.Name = &parser.QualifiedName{Base: parser.Name(p.session.Database)} } dbDesc, err := p.getDatabaseDesc(n.Name.String()) if err != nil { return nil, err } prefix := keys.MakeNameMetadataKey(dbDesc.ID, "") sr, err := p.db.Scan(prefix, prefix.PrefixEnd(), 0) if err != nil { return nil, err } v := &valuesNode{columns: []string{"Table"}} for _, row := range sr { name := string(bytes.TrimPrefix(row.Key, prefix)) v.rows = append(v.rows, []parser.Datum{parser.DString(name)}) } return v, nil }
// ShowDatabases returns all the databases. func (s *Server) ShowDatabases(session *Session, p *parser.ShowDatabases, args []sqlwire.Datum, resp *sqlwire.Response) error { prefix := keys.MakeNameMetadataKey(structured.RootNamespaceID, "") sr, err := s.db.Scan(prefix, prefix.PrefixEnd(), 0) if err != nil { return err } var rows []sqlwire.Result_Row for _, row := range sr { name := string(bytes.TrimPrefix(row.Key, prefix)) rows = append(rows, sqlwire.Result_Row{ Values: []sqlwire.Datum{ {StringVal: &name}, }, }) } resp.Results = []sqlwire.Result{ { Columns: []string{"Database"}, Rows: rows, }, } return nil }
// ShowTables returns all the tables. func (s *Server) ShowTables(p *parser.ShowTables, args []sqlwire.Datum, resp *sqlwire.Response) { if p.Name == "" { if s.database == "" { resp.SetGoError(errors.New("no database specified")) return } p.Name = s.database } dbID, err := s.lookupDatabase(p.Name) if err != nil { resp.SetGoError(err) return } prefix := keys.MakeNameMetadataKey(dbID, "") sr, err := s.db.Scan(prefix, prefix.PrefixEnd(), 0) if err != nil { resp.SetGoError(err) return } var rows []sqlwire.Result_Row for _, row := range sr { name := string(bytes.TrimPrefix(row.Key, prefix)) rows = append(rows, sqlwire.Result_Row{ Values: []sqlwire.Datum{ {StringVal: &name}, }, }) } resp.Results = []sqlwire.Result{ { Columns: []string{"tables"}, Rows: rows, }, } }