// SchemaFromModel allows the easy construction of a TableSchema 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.TableSchema, error) { s := structured.TableSchema{} m, err := getDBFields(deref(reflect.TypeOf(obj))) if err != nil { return s, err } s.Table.Name = strings.ToLower(reflect.TypeOf(obj).Name()) // Create the columns for the table. for name := range m { col := structured.Column{ Name: name, Type: structured.Column_BYTES, } s.Columns = append(s.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 s, 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.Index 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, ":") } s.Indexes = append(s.Indexes, structured.TableSchema_IndexByName{ Index: index, ColumnNames: params, }) } } // Normalize the column and index order. sort.Sort(columnsByName(s.Columns)) sort.Sort(indexesByName(s.Indexes)) return s, nil }
// SchemaFromModel allows the easy construction of a TableSchema 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.TableSchema, error) { s := structured.TableSchema{} m, err := getDBFields(deref(reflect.TypeOf(obj))) if err != nil { return s, err } s.Table.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.Column{ Name: name, Type: colType, } s.Columns = append(s.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 s, 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.Index 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, ":") } s.Indexes = append(s.Indexes, structured.TableSchema_IndexByName{ Index: index, ColumnNames: params, }) } } // Normalize the column and index order. sort.Sort(columnsByName(s.Columns)) sort.Sort(indexesByName(s.Indexes)) return s, nil }