func (b *Backend) FieldType(typ reflect.Type, t *structs.Tag) (string, error) { if c := codec.FromTag(t); c != nil { if c.Binary || t.PipeName() != "" { return "BLOB", nil } return "TEXT", nil } switch typ.Kind() { case reflect.Bool: return "BOOLEAN", nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return "INTEGER", nil case reflect.Float32, reflect.Float64: return "REAL", nil case reflect.String: return "TEXT", nil case reflect.Slice: // []byte if typ.Elem().Kind() == reflect.Uint8 { return "BLOB", nil } case reflect.Struct: if typ.Name() == "Time" && typ.PkgPath() == "time" { return "INTEGER", nil } } return "", fmt.Errorf("can't map field type %v to a database type", typ) }
func (b *Backend) FieldType(typ reflect.Type, t *structs.Tag) (string, error) { if c := codec.FromTag(t); c != nil { if c.Binary || t.PipeName() != "" { return "BLOB", nil } return "TEXT", nil } var ft string switch typ.Kind() { case reflect.Bool: ft = "BOOL" case reflect.Int8: ft = "TINYINT" case reflect.Uint8: ft = "TINYINT UNSIGNED" case reflect.Int16: ft = "SMALLINT" case reflect.Uint16: ft = "SMALLINT UNSIGNED" case reflect.Int32: ft = "INT" case reflect.Uint32: ft = "INT UNSIGNED" case reflect.Int, reflect.Int64: ft = "BIGINT" case reflect.Uint, reflect.Uint64: ft = "BIGINT UNSIGNED" case reflect.Float32: ft = "FLOAT" case reflect.Float64: ft = "DOUBLE" case reflect.String: if ml, ok := t.MaxLength(); ok { ft = fmt.Sprintf("VARCHAR (%d)", ml) } else if fl, ok := t.Length(); ok { ft = fmt.Sprintf("CHAR (%d)", fl) } else { ft = "TEXT" } case reflect.Slice: etyp := typ.Elem() if etyp.Kind() == reflect.Uint8 { // []byte ft = "BLOB" } case reflect.Struct: if typ.Name() == "Time" && typ.PkgPath() == "time" { ft = "DATETIME" } } if ft != "" { return ft, nil } return "", fmt.Errorf("can't map field type %v to a database type", typ) }
func (d *Driver) saveParameters(m driver.Model, data interface{}) (reflect.Value, []string, []interface{}, error) { // data is guaranteed to be of m.Type() val := driver.Direct(reflect.ValueOf(data)) fields := m.Fields() max := len(fields.MNames) names := make([]string, 0, max) values := make([]interface{}, 0, max) var err error if d.transforms != nil { for ii, v := range fields.Indexes { f := d.fieldByIndex(val, v, false) if !f.IsValid() { continue } if fields.OmitEmpty[ii] && driver.IsZero(f) { continue } ft := f.Type() var fval interface{} if _, ok := d.transforms[ft]; ok { fval, err = d.backend.TransformOutValue(f) if err != nil { return val, nil, nil, err } if fields.NullEmpty[ii] && driver.IsZero(reflect.ValueOf(fval)) { fval = nil } } else if !fields.NullEmpty[ii] || !driver.IsZero(f) { if c := codec.FromTag(fields.Tags[ii]); c != nil { fval, err = c.Encode(f.Interface()) if err != nil { return val, nil, nil, err } if p := pipe.FromTag(fields.Tags[ii]); p != nil { data, err := p.Encode(fval.([]byte)) if err != nil { return val, nil, nil, err } fval = data } } else { // Most sql drivers won't accept aliases for string type if ft.Kind() == reflect.String && ft != stringType { f = f.Convert(stringType) } fval = f.Interface() } } names = append(names, fields.MNames[ii]) values = append(values, fval) } } else { for ii, v := range fields.Indexes { f := d.fieldByIndex(val, v, false) if !f.IsValid() { continue } if fields.OmitEmpty[ii] && driver.IsZero(f) { continue } var fval interface{} if !fields.NullEmpty[ii] || !driver.IsZero(f) { if c := codec.FromTag(fields.Tags[ii]); c != nil { fval, err = c.Encode(&f) if err != nil { return val, nil, nil, err } } else { ft := f.Type() // Most sql drivers won't accept aliases for string type if ft.Kind() == reflect.String && ft != stringType { f = f.Convert(stringType) } fval = f.Interface() } } names = append(names, fields.MNames[ii]) values = append(values, fval) } } return val, names, values, nil }
func (s *scanner) codecAndPipe() (*codec.Codec, *pipe.Pipe) { if c := codec.FromTag(s.Tag); c != nil { return c, pipe.FromTag(s.Tag) } return nil, nil }
func (b *Backend) FieldType(typ reflect.Type, t *structs.Tag) (string, error) { if c := codec.FromTag(t); c != nil { // TODO: Use type JSON on Postgresql >= 9.2 for JSON encoded fields if c.Binary || t.PipeName() != "" { return "BYTEA", nil } return "TEXT", nil } var ft string switch typ.Kind() { case reflect.Bool: ft = "BOOL" case reflect.Int8, reflect.Uint8, reflect.Int16: ft = "INT2" case reflect.Uint16, reflect.Int32: ft = "INT4" case reflect.Int, reflect.Uint, reflect.Uint32, reflect.Int64, reflect.Uint64: ft = "INT8" case reflect.Float32: ft = "FLOAT4" case reflect.Float64: ft = "FLOAT8" case reflect.String: if t.Has("macaddr") { ft = "MACADDR" } else if t.Has("inet") { ft = "INET" } else { if ml, ok := t.MaxLength(); ok { ft = fmt.Sprintf("VARCHAR (%d)", ml) } else if fl, ok := t.Length(); ok { ft = fmt.Sprintf("CHAR (%d)", fl) } else { ft = "TEXT" } } case reflect.Slice: etyp := typ.Elem() if etyp.Kind() == reflect.Uint8 { // []byte ft = "BYTEA" // TODO: database/sql does not support array types. Enable this code // if that changes in the future // } else if typ.Elem().Kind() != reflect.Struct { // et, err := b.FieldType(typ.Elem(), tag) // if err != nil { // return "", err // } // t = et + "[]" } case reflect.Struct: if typ.Name() == "Time" && typ.PkgPath() == "time" { ft = "TIMESTAMP WITHOUT TIME ZONE" } } if t.Has("auto_increment") { if strings.HasPrefix(ft, "INT") { ft = strings.Replace(ft, "INT", "SERIAL", -1) } else { return "", fmt.Errorf("postgres does not support auto incrementing %v", typ) } } if ft != "" { return ft, nil } return "", fmt.Errorf("can't map field type %v to a database type", typ) }