func makeIndexKeyVals(desc *structured.TableDescriptor, index structured.IndexDescriptor) ([]parser.Datum, error) { vals := make([]parser.Datum, len(index.ColumnIDs)) for i, id := range index.ColumnIDs { col, err := desc.FindColumnByID(id) if err != nil { return nil, err } switch col.Type.Kind { case structured.ColumnType_BIT, structured.ColumnType_INT: vals[i] = parser.DInt(0) case structured.ColumnType_FLOAT: vals[i] = parser.DFloat(0) case structured.ColumnType_CHAR, structured.ColumnType_TEXT, structured.ColumnType_BLOB: vals[i] = parser.DString("") default: return nil, util.Errorf("TODO(pmattis): decoded index key: %s", col.Type.Kind) } } if !index.Unique { // Non-unique columns are suffixed by the primary index key. pkVals, err := makeIndexKeyVals(desc, desc.PrimaryIndex) if err != nil { return nil, err } vals = append(vals, pkVals...) } return vals, 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 := desc.Validate(); err != nil { return nil, err } return &desc, 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 (s *Server) processColumns(desc *structured.TableDescriptor, node parser.Columns) ([]structured.ColumnDescriptor, error) { if node == nil { return desc.Columns, nil } cols := make([]structured.ColumnDescriptor, len(node)) for i, n := range node { switch nt := n.(type) { case *parser.StarExpr: return s.processColumns(desc, nil) case *parser.NonStarExpr: switch et := nt.Expr.(type) { case *parser.ColName: // TODO(pmattis): If et.Qualifier is not empty, verify it matches the // table name. var err error col, err := desc.FindColumnByName(et.Name) if err != nil { return nil, err } cols[i] = *col default: return nil, fmt.Errorf("unexpected node: %T", nt.Expr) } } } return cols, 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 := desc.Validate(); err != nil { return nil, err } return &desc, nil }
// DropTable drops a table. // Privileges: WRITE on table. // Notes: postgres allows only the table owner to DROP a table. // mysql requires the DROP privilege on the table. func (p *planner) DropTable(n *parser.DropTable) (planNode, error) { // TODO(XisiHuang): should do truncate and delete descriptor in // the same txn for i, tableQualifiedName := range n.Names { if err := tableQualifiedName.NormalizeTableName(p.session.Database); err != nil { return nil, err } dbDesc, err := p.getDatabaseDesc(tableQualifiedName.Database()) if err != nil { return nil, err } tbKey := tableKey{dbDesc.ID, tableQualifiedName.Table()} nameKey := tbKey.Key() gr, err := p.txn.Get(nameKey) if err != nil { return nil, err } if !gr.Exists() { if n.IfExists { // Noop. continue } // Key does not exist, but we want it to: error out. return nil, fmt.Errorf("table %q does not exist", tbKey.Name()) } tableDesc := structured.TableDescriptor{} if err := p.txn.GetProto(gr.ValueBytes(), &tableDesc); err != nil { return nil, err } if err := tableDesc.Validate(); err != nil { return nil, err } if !tableDesc.HasPrivilege(p.user, parser.PrivilegeWrite) { return nil, fmt.Errorf("user %s does not have %s privilege on table %s", p.user, parser.PrivilegeWrite, tableDesc.Name) } if _, err = p.Truncate(&parser.Truncate{Tables: n.Names[i : i+1]}); err != nil { return nil, err } // Delete table descriptor descKey := gr.ValueBytes() b := &client.Batch{} b.Del(descKey) b.Del(nameKey) err = p.txn.Run(b) if err != nil { return nil, err } } return &valuesNode{}, 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) }) }
func decodeIndexKey(desc *structured.TableDescriptor, index structured.IndexDescriptor, vals map[string]parser.Datum, key []byte) ([]byte, error) { if !bytes.HasPrefix(key, keys.TableDataPrefix) { return nil, fmt.Errorf("%s: invalid key prefix: %q", desc.Name, key) } key = bytes.TrimPrefix(key, keys.TableDataPrefix) var tableID uint64 key, tableID = encoding.DecodeUvarint(key) if uint32(tableID) != desc.ID { return nil, fmt.Errorf("%s: unexpected table ID: %d != %d", desc.Name, desc.ID, tableID) } var indexID uint64 key, indexID = encoding.DecodeUvarint(key) if uint32(indexID) != index.ID { return nil, fmt.Errorf("%s: unexpected index ID: %d != %d", desc.Name, index.ID, indexID) } for _, id := range index.ColumnIDs { col, err := desc.FindColumnByID(id) if err != nil { return nil, err } switch col.Type.Kind { case structured.ColumnType_BIT, structured.ColumnType_INT: var i int64 key, i = encoding.DecodeVarint(key) vals[col.Name] = parser.DInt(i) case structured.ColumnType_FLOAT: var f float64 key, f = encoding.DecodeNumericFloat(key) vals[col.Name] = parser.DFloat(f) case structured.ColumnType_CHAR, structured.ColumnType_TEXT, structured.ColumnType_BLOB: var r []byte key, r = encoding.DecodeBytes(key, nil) vals[col.Name] = parser.DString(r) default: return nil, util.Errorf("TODO(pmattis): decoded index key: %s", col.Type.Kind) } } return key, nil }
func (p *planner) processColumns(desc *structured.TableDescriptor, node parser.QualifiedNames) ([]structured.ColumnDescriptor, error) { if node == nil { return desc.Columns, nil } cols := make([]structured.ColumnDescriptor, len(node)) for i, n := range node { // TODO(pmattis): If the name is qualified, verify the table name matches // desc.Name. var err error col, err := desc.FindColumnByName(n.Column()) if err != nil { return nil, err } cols[i] = *col } return cols, 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(desc *structured.TableDescriptor) error { desc.Name = strings.ToLower(desc.Name) if err := desc.AllocateIDs(); 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) }) }
func makeTableDesc(p *parser.CreateTable) (structured.TableDescriptor, error) { desc := structured.TableDescriptor{} desc.Name = p.Table.String() for _, def := range p.Defs { switch d := def.(type) { case *parser.ColumnTableDef: col := structured.ColumnDescriptor{ Name: d.Name, Nullable: (d.Nullable != parser.NotNull), } switch t := d.Type.(type) { case *parser.BitType: col.Type.Kind = structured.ColumnType_BIT col.Type.Width = int32(t.N) case *parser.IntType: col.Type.Kind = structured.ColumnType_INT col.Type.Width = int32(t.N) case *parser.FloatType: col.Type.Kind = structured.ColumnType_FLOAT col.Type.Precision = int32(t.Prec) case *parser.DecimalType: col.Type.Kind = structured.ColumnType_DECIMAL col.Type.Width = int32(t.Scale) col.Type.Precision = int32(t.Prec) case *parser.DateType: col.Type.Kind = structured.ColumnType_DATE case *parser.TimeType: col.Type.Kind = structured.ColumnType_TIME case *parser.TimestampType: col.Type.Kind = structured.ColumnType_TIMESTAMP case *parser.CharType: col.Type.Kind = structured.ColumnType_CHAR col.Type.Width = int32(t.N) case *parser.TextType: col.Type.Kind = structured.ColumnType_TEXT case *parser.BlobType: col.Type.Kind = structured.ColumnType_BLOB } desc.Columns = append(desc.Columns, col) // Create any associated index. if d.PrimaryKey || d.Unique { index := structured.IndexDescriptor{ Unique: true, ColumnNames: []string{d.Name}, } if d.PrimaryKey { index.Name = "primary" } desc.Indexes = append(desc.Indexes, index) } case *parser.IndexTableDef: index := structured.IndexDescriptor{ Name: d.Name, Unique: d.Unique, ColumnNames: d.Columns, } desc.Indexes = append(desc.Indexes, index) default: return desc, fmt.Errorf("unsupported table def: %T", def) } } return desc, nil }
func makeTableDesc(p *parser.CreateTable) (structured.TableDescriptor, error) { desc := structured.TableDescriptor{} desc.Name = p.Table.Table() for _, def := range p.Defs { switch d := def.(type) { case *parser.ColumnTableDef: col := structured.ColumnDescriptor{ Name: string(d.Name), Nullable: (d.Nullable != parser.NotNull), } switch t := d.Type.(type) { case *parser.BitType: col.Type.Kind = structured.ColumnType_BIT col.Type.Width = int32(t.N) case *parser.BoolType: col.Type.Kind = structured.ColumnType_BOOL case *parser.IntType: col.Type.Kind = structured.ColumnType_INT col.Type.Width = int32(t.N) case *parser.FloatType: col.Type.Kind = structured.ColumnType_FLOAT col.Type.Precision = int32(t.Prec) case *parser.DecimalType: col.Type.Kind = structured.ColumnType_DECIMAL col.Type.Width = int32(t.Scale) col.Type.Precision = int32(t.Prec) case *parser.DateType: col.Type.Kind = structured.ColumnType_DATE case *parser.TimeType: col.Type.Kind = structured.ColumnType_TIME case *parser.TimestampType: col.Type.Kind = structured.ColumnType_TIMESTAMP case *parser.CharType: col.Type.Kind = structured.ColumnType_CHAR col.Type.Width = int32(t.N) case *parser.TextType: col.Type.Kind = structured.ColumnType_TEXT case *parser.BlobType: col.Type.Kind = structured.ColumnType_BLOB default: panic(fmt.Sprintf("unexpected type %T", t)) } desc.Columns = append(desc.Columns, col) // Create any associated index. if d.PrimaryKey || d.Unique { index := structured.IndexDescriptor{ Unique: true, ColumnNames: []string{string(d.Name)}, } if d.PrimaryKey { index.Name = structured.PrimaryKeyIndexName desc.PrimaryIndex = index } else { desc.Indexes = append(desc.Indexes, index) } } case *parser.IndexTableDef: index := structured.IndexDescriptor{ Name: string(d.Name), Unique: d.Unique, ColumnNames: d.Columns, } if d.PrimaryKey { // Only override the index name if it hasn't been set by the user. if index.Name == "" { index.Name = structured.PrimaryKeyIndexName } desc.PrimaryIndex = index } else { desc.Indexes = append(desc.Indexes, index) } default: return desc, fmt.Errorf("unsupported table def: %T", def) } } return desc, nil }
// Select selects rows from a single table. func (s *Server) Select(session *Session, p *parser.Select, args []sqlwire.Datum, resp *sqlwire.Response) error { if len(p.Exprs) != 1 { return fmt.Errorf("TODO(pmattis): unsupported select exprs: %s", p.Exprs) } if _, ok := p.Exprs[0].(*parser.StarExpr); !ok { return fmt.Errorf("TODO(pmattis): unsupported select expr: %s", p.Exprs) } if len(p.From) != 1 { return fmt.Errorf("TODO(pmattis): unsupported from: %s", p.From) } var desc *structured.TableDescriptor { ate, ok := p.From[0].(*parser.AliasedTableExpr) if !ok { return fmt.Errorf("TODO(pmattis): unsupported from: %s", p.From) } table, ok := ate.Expr.(parser.QualifiedName) if !ok { return fmt.Errorf("TODO(pmattis): unsupported from: %s", p.From) } var err error desc, err = s.getTableDesc(session.Database, table) if err != nil { return err } } // Retrieve all of the keys that start with our index key prefix. startKey := proto.Key(encodeIndexKeyPrefix(desc.ID, desc.Indexes[0].ID)) endKey := startKey.PrefixEnd() sr, err := s.db.Scan(startKey, endKey, 0) if err != nil { return err } // All of the columns for a particular row will be grouped together. We loop // over the returned key/value pairs and decode the key to extract the // columns encoded within the key and the column ID. We use the column ID to // lookup the column and decode the value. All of these values go into a map // keyed by column name. When the index key changes we output a row // containing the current values. // // The TODOs here are too numerous to list. This is only performing a full // table scan using the primary key. var rows []sqlwire.Result_Row var primaryKey []byte vals := valMap{} for _, kv := range sr { if primaryKey != nil && !bytes.HasPrefix(kv.Key, primaryKey) { if output, err := shouldOutputRow(p.Where, vals); err != nil { return err } else if output { rows = append(rows, outputRow(desc.Columns, vals)) } vals = valMap{} } remaining, err := decodeIndexKey(desc, desc.Indexes[0], vals, kv.Key) if err != nil { return err } primaryKey = []byte(kv.Key[:len(kv.Key)-len(remaining)]) _, colID := encoding.DecodeUvarint(remaining) if err != nil { return err } col, err := desc.FindColumnByID(uint32(colID)) if err != nil { return err } vals[col.Name] = unmarshalValue(*col, kv) if log.V(2) { log.Infof("Scan %q -> %v", kv.Key, vals[col.Name]) } } if output, err := shouldOutputRow(p.Where, vals); err != nil { return err } else if output { rows = append(rows, outputRow(desc.Columns, vals)) } resp.Results = []sqlwire.Result{ { Columns: make([]string, len(desc.Columns)), Rows: rows, }, } for i, col := range desc.Columns { resp.Results[0].Columns[i] = col.Name } return nil }
// SchemaFromModel allows the easy construction of a TableDescriptor from a Go // struct. Columns are created for each exported field in the struct. The "db" // struct tag is used to control the mapping of field name to column name and // to indicate exported fields which should be skipped. // // type User struct { // ID int // Name string `db:"old_name"` // Ignored int `db:"-"` // } // // Indexes are specified using the "roach" struct tag declaration. // // type User struct { // ID int `roach:"primary key"` // Name string `db:"old_name" roach:"index"` // } // // The following "roach" options are supported: // // "primary key [(columns...)]" - creates a unique index on <columns> and // marks it as the primary key for the table. If <columns> is not specified // it defaults to the name of the column the option is associated with. // // "index" [(columns...)]" - creates an index on <columns>. // // "unique index" [(columns...)]" - creates a unique index on <columns>. func SchemaFromModel(obj interface{}) (structured.TableDescriptor, error) { desc := structured.TableDescriptor{} m, err := getDBFields(deref(reflect.TypeOf(obj))) if err != nil { return desc, err } desc.Name = strings.ToLower(reflect.TypeOf(obj).Name()) // Create the columns for the table. for name, sf := range m { colType := structured.ColumnType{} // TODO(pmattis): The mapping from Go-type Kind to column-type Kind is // likely not complete or correct, but this is probably going away pretty // soon with the move to SQL. switch sf.Type.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: colType.Kind = structured.ColumnType_INT case reflect.Float32, reflect.Float64: colType.Kind = structured.ColumnType_FLOAT case reflect.String: colType.Kind = structured.ColumnType_TEXT } col := structured.ColumnDescriptor{ Name: name, Type: colType, } desc.Columns = append(desc.Columns, col) } // Create the indexes for the table. for name, f := range m { tag := f.Tag.Get("roach") if tag == "" { continue } for _, opt := range strings.Split(tag, ";") { match := schemaOptRE.FindStringSubmatch(opt) if match == nil { return desc, fmt.Errorf("invalid schema option: %s", opt) } cmd := match[1] var params []string if len(match[2]) > 0 { params = strings.Split(match[2], ",") } else { params = []string{name} } var index structured.IndexDescriptor switch strings.ToLower(cmd) { case "primary key": index.Name = structured.PrimaryKeyIndexName index.Unique = true case "unique index": index.Name = strings.Join(params, ":") index.Unique = true case "index": index.Name = strings.Join(params, ":") } index.ColumnNames = params desc.Indexes = append(desc.Indexes, index) } } // Normalize the column and index order. sort.Sort(columnsByName(desc.Columns)) sort.Sort(indexesByName(desc.Indexes)) return desc, nil }
// Select selects rows from a single table. // Privileges: READ on table // Notes: postgres requires SELECT. Also requires UPDATE on "FOR UPDATE". // mysql requires SELECT. func (p *planner) Select(n *parser.Select) (planNode, error) { var desc *structured.TableDescriptor var index *structured.IndexDescriptor var visibleCols []structured.ColumnDescriptor switch len(n.From) { case 0: // desc remains nil. case 1: var err error desc, err = p.getAliasedTableDesc(n.From[0]) if err != nil { return nil, err } if !desc.HasPrivilege(p.user, parser.PrivilegeRead) { return nil, fmt.Errorf("user %s does not have %s privilege on table %s", p.user, parser.PrivilegeRead, desc.Name) } // This is only kosher because we know that getAliasedDesc() succeeded. qname := n.From[0].(*parser.AliasedTableExpr).Expr.(*parser.QualifiedName) indexName := qname.Index() if indexName != "" && !strings.EqualFold(desc.PrimaryIndex.Name, indexName) { for i := range desc.Indexes { if strings.EqualFold(desc.Indexes[i].Name, indexName) { // Remove all but the matching index from the descriptor. desc.Indexes = desc.Indexes[i : i+1] index = &desc.Indexes[0] break } } if index == nil { return nil, fmt.Errorf("index \"%s\" not found", indexName) } // If the table was not aliased, use the index name instead of the table // name for fully-qualified columns in the expression. if n.From[0].(*parser.AliasedTableExpr).As == "" { desc.Alias = index.Name } // Strip out any columns from the table that are not present in the // index. indexColIDs := map[structured.ColumnID]struct{}{} for _, colID := range index.ColumnIDs { indexColIDs[colID] = struct{}{} } for _, col := range desc.Columns { if _, ok := indexColIDs[col.ID]; !ok { continue } visibleCols = append(visibleCols, col) } } else { index = &desc.PrimaryIndex visibleCols = desc.Columns } default: return nil, util.Errorf("TODO(pmattis): unsupported FROM: %s", n.From) } // Loop over the select expressions and expand them into the expressions // we're going to use to generate the returned column set and the names for // those columns. exprs := make([]parser.Expr, 0, len(n.Exprs)) columns := make([]string, 0, len(n.Exprs)) for _, e := range n.Exprs { // If a QualifiedName has a StarIndirection suffix we need to match the // prefix of the qualified name to one of the tables in the query and // then expand the "*" into a list of columns. if qname, ok := e.Expr.(*parser.QualifiedName); ok { if err := qname.NormalizeColumnName(); err != nil { return nil, err } if qname.IsStar() { if desc == nil { return nil, fmt.Errorf("\"%s\" with no tables specified is not valid", qname) } if e.As != "" { return nil, fmt.Errorf("\"%s\" cannot be aliased", qname) } tableName := qname.Table() if tableName != "" && !strings.EqualFold(desc.Alias, tableName) { return nil, fmt.Errorf("table \"%s\" not found", tableName) } if index != &desc.PrimaryIndex { for _, col := range index.ColumnNames { columns = append(columns, col) exprs = append(exprs, &parser.QualifiedName{Base: parser.Name(col)}) } } else { for _, col := range desc.Columns { columns = append(columns, col.Name) exprs = append(exprs, &parser.QualifiedName{Base: parser.Name(col.Name)}) } } continue } } exprs = append(exprs, e.Expr) if e.As != "" { columns = append(columns, string(e.As)) continue } // If the expression is a qualified name, use the column name, not the // full qualification as the column name to return. switch t := e.Expr.(type) { case *parser.QualifiedName: if err := t.NormalizeColumnName(); err != nil { return nil, err } columns = append(columns, t.Column()) default: columns = append(columns, e.Expr.String()) } } s := &scanNode{ txn: p.txn, desc: desc, index: index, visibleCols: visibleCols, columns: columns, render: exprs, } if index != nil { s.isSecondaryIndex = index != &desc.PrimaryIndex } if n.Where != nil { s.filter = n.Where.Expr } return s, nil }
// Select selects rows from a single table. // Privileges: READ on table // Notes: postgres requires SELECT. Also requires UPDATE on "FOR UPDATE". // mysql requires SELECT. func (p *planner) Select(n *parser.Select) (planNode, error) { var desc *structured.TableDescriptor switch len(n.From) { case 0: // desc remains nil. case 1: var err error desc, err = p.getAliasedTableDesc(n.From[0]) if err != nil { return nil, err } if !desc.HasPrivilege(p.user, parser.PrivilegeRead) { return nil, fmt.Errorf("user %s does not have %s privilege on table %s", p.user, parser.PrivilegeRead, desc.Name) } default: return nil, util.Errorf("TODO(pmattis): unsupported FROM: %s", n.From) } // Loop over the select expressions and expand them into the expressions // we're going to use to generate the returned column set and the names for // those columns. exprs := make([]parser.Expr, 0, len(n.Exprs)) columns := make([]string, 0, len(n.Exprs)) for _, e := range n.Exprs { switch t := e.(type) { case *parser.StarExpr: if desc == nil { return nil, fmt.Errorf("* with no tables specified is not valid") } for _, col := range desc.Columns { columns = append(columns, col.Name) exprs = append(exprs, &parser.QualifiedName{Base: parser.Name(col.Name)}) } case *parser.NonStarExpr: exprs = append(exprs, t.Expr) if t.As != "" { columns = append(columns, string(t.As)) } else { // TODO(pmattis): Should verify at this point that any referenced // columns are represented in the tables being selected from. columns = append(columns, t.Expr.String()) } } } s := &scanNode{ db: p.db, desc: desc, columns: columns, render: exprs, } if n.Where != nil { s.filter = n.Where.Expr } return s, nil }