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 }
// 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 }
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. // 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 }