func (ø *Row) SelectByStruct(structPtr interface{}, tagVal string, opts ...interface{}) error { str, err := meta.StructByValue(meta.FinalValue(structPtr)) if err != nil { return err } tags, err2 := str.Tags() if err2 != nil { return err2 } options := []interface{}{} order := []string{} for k, v := range tags { if t := v.Get("db.select"); t != "" && strings.Contains(t, tagVal) { fi := ø.queryField(k, opts...) if fi == nil { panic("can't find field " + k + " : no Queryfield property of given fields or aliases") } options = append(options, fi) order = append(order, k) } } options = append(options, opts...) row, err := ø.Any(options...) if err != nil { return err } err = row.GetStruct(tagVal, structPtr) if err != nil { return err } return nil }
/* only the fields that are *fat.Field and not nil are chosen to be set from the row. fields that are not set in the row, are set to nil */ func (r *Registry) FromRow(row *Row, øptrToFatStruct interface{}) (err error) { fn := func(field *meta.Field) { if err != nil { return } if field.Value.IsNil() { return } ff, isFat := field.Value.Interface().(*fat.Field) if !isFat { return } dbField := r.FieldOf(ff) if row.Values()[dbField] != nil { fatField := field.Value.Interface().(*fat.Field) err = scanFieldToStruct(row, fatField, dbField) return } field.Value.Set(fatFieldNil) // ff.Set(fatFieldNil) /* if dbField.Is(NullAllowed) { ff.Set(fatFieldNil) } */ } var stru *meta.Struct stru, err = meta.StructByValue(reflect.ValueOf(øptrToFatStruct)) if err == nil { stru.Each(fn) } return }
func setFieldInStruct(vl reflect.Value, fieldName string, tagVal string, v *TypedValue, s interface{}) error { str, err := meta.StructByValue(meta.FinalValue(s)) if err != nil { return err } tag, err2 := str.Tag(fieldName) if err2 != nil { return err2 } // tag does match the given if tag != nil && strings.Contains(tag.Get("db.select"), tagVal) { err := Convert(v, vl.Addr().Interface()) if err != nil { return fmt.Errorf("error in field %s: %s", fieldName, err.Error()) } } return nil }
func (r *Registry) RegisterTable(name string, ptrToFatStru interface{}) (*Table, error) { val := reflect.ValueOf(ptrToFatStru) if val.Kind() != reflect.Ptr { return nil, fmt.Errorf("%T is no pointer to a struct", ptrToFatStru) } if val.Elem().Kind() != reflect.Struct { return nil, fmt.Errorf("%T is no pointer to a struct", ptrToFatStru) } valType := TypeString(ptrToFatStru) stru, err := mt.StructByValue(val) if err != nil { return nil, err } table := NewTable(name) fn := func(fld *mt.Field) { dbFlag := splitSpace(fld.Type.Tag.Get("db")) if len(dbFlag) < 1 { return } //fname := fld.Type.Tag.Get("db") fname := dbFlag[0] // fld.Type.Tag.Get("db") if fname == "-" { return } //if fname != "" && fname != "-" { ff := fld.Value.Interface().(*fat.Field) var typ Type ftype := findType(fld.Type.Tag.Get("type")) if ftype != "" { switch ftype { case "[string]string", "[string]int", "[string]time", "[string]float", "[string]bool": typ = JsonType case "int": typ = IntType case "text": typ = TextType case "bool": typ = BoolType case "date": typ = DateType /* case "time": typ = TimeType */ case "xml": typ = XmlType case "float": typ = FloatType case "[]float": typ = FloatsType case "timestamptz": typ = TimeStampTZType case "timestamp": typ = TimeStampType case "json": typ = JsonType case "[]int": typ = IntsType case "[]string": typ = StringsType case "[]bool": typ = BoolsType case "html": typ = HtmlType case "[]time": typ = TimeStampsTZType case "uuid": typ = UuidType /* case "ltree": typ = LtreeType case "trigger": typ = TriggerType */ default: if varcharReg.MatchString(fld.Type.Tag.Get("type")) { a := varcharReg.FindStringSubmatch(fld.Type.Tag.Get("type")) i, err := strconv.Atoi(a[1]) if err != nil { panic(fmt.Sprintf("can't parse varchar value: %#v: %s of field %s", ftype, err.Error(), fld.Type.Name)) } if i > 255 { panic(fmt.Sprintf("max number for varchar is 255, not %v in field %s", i, fld.Type.Name)) } typ = VarChar(i) } else { panic(fmt.Sprintf("unknown type %#v of field %s", ftype, fld.Type.Name)) } } } else { /* switch ff.Typ() { case "string": typ = VarChar(255) case "bool": typ = BoolType case "int": typ = IntType case "time": typ = TimeType case "[]string": typ = StringsType case "[]int": typ = IntsType default: */ panic(fmt.Sprintf("type: %#v has no corresponding pgsql.Type in field %s", ff.Typ(), fld.Type.Name)) /* } */ } f := table.NewField(fname, typ) var isPkey bool //fflags := fld.Type.Tag.Get("pgsql.flags") //if fflags != "" { // flgs := strings.Split(fflags, ",") if len(dbFlag) > 1 { //for _, fl := range flgs { for _, fl := range dbFlag[1:] { fl = strings.TrimSpace(fl) var fffl Flag switch fl { case "NULL": fffl = NullAllowed case "PKEY": isPkey = true fffl = PrimaryKey case "SERIAL": fffl = Serial case "UUIDGEN": fffl = UuidGenerate case "DELETE_CASCADE": fffl = OnDeleteCascade default: panic(fmt.Sprintf("unsupported flag: %#v in field %s", fl, fld.Type.Name)) } f.Add(fffl) } } //} /* if ff.Default() != nil { f.Default = Sql(ff.Default().String()) } */ if isPkey { table.PrimaryKey = append(table.PrimaryKey, f) } //fmt.Printf("adding field %#v, %#v, %s, %s\n", val.Type().String(), val.Type().Name(), fld.Type.Name, f.Name) //FieldRegistry.AddField(val.Type().String(), fld.Type.Name, f) r.AddField(valType, fld.Type.Name, f) //} } // TableRegistry.AddTable(val.Type().String(), table) r.AddTable(valType, table) stru.Each(fn) return table, nil }
func TableDefinition(strPtr interface{}) (td *tableDefinition, err error) { td = &tableDefinition{} td.Fields = map[string]*Field{} td.Uniques = map[string][]string{} // maps fieldname to field st, err := meta.StructByValue(reflect.ValueOf(strPtr)) if err != nil { return } st.Each(func(f *meta.Field) { field := f.Type val := f.Value switch field.Type { case tableType: tName := field.Name if name := field.Tag.Get("name"); name != "" { tName = name } td.Table = NewTable(tName) val.Set(reflect.ValueOf(td.Table)) if un := field.Tag.Get("unique"); un != "" { for _, uniq := range strings.Split(un, ",") { td.Uniques[uniq] = strings.Split(uniq, "#") } } case fieldType: fName := field.Name if name := field.Tag.Get("name"); name != "" { fName = name } options := []interface{}{} type_ := field.Tag.Get("type") if type_ == "" { err = fmt.Errorf("no type tag set for field %s in table definition %T", field.Name, strPtr) return } var ty Type switch type_ { case "int": ty = IntType case "float": ty = FloatType case "text": ty = TextType case "bool": ty = BoolType case "timestamptz": ty = TimeStampTZType case "timestamp": ty = TimeType case "date": ty = DateType case "time": ty = TimeType case "xml": ty = XmlType case "integer[]": ty = IntsType case "character varying[]": ty = StringsType case "uuid": ty = UuidType case "ltree": ty = LtreeType case "trigger": ty = TriggerType default: md := varcharRegexp.FindStringSubmatch(type_) if len(md) == 2 { i, e := strconv.Atoi(md[1]) if e != nil { err = fmt.Errorf("error in varchar type tag for field %s in table definition %T, can't parse integer", field.Name, strPtr) return } ty = VarChar(i) } else { err = fmt.Errorf("error in type tag for field %s in table definition %T, unknown type: %s", field.Name, strPtr, type_) return } } options = append(options, ty) if flags := field.Tag.Get("flag"); flags != "" { for _, fl := range strings.Split(flags, ",") { switch fl { case "null": options = append(options, NullAllowed) case "pkey": options = append(options, PrimaryKey) case "unique": options = append(options, Unique) case "index": options = append(options, Indexed) case "serial": options = append(options, Serial) case "uuidgenerate": options = append(options, UuidGenerate) default: err = fmt.Errorf("error in flags tag for field %s in table definition %T, unknown flag: %s", field.Name, strPtr, fl) return } } } if enum := field.Tag.Get("enum"); enum != "" { enums := strings.Split(enum, ",") sel := make([]interface{}, len(enums)) for i, en := range enums { sel[i] = en } options = append(options, SelectionArray(sel)) } f := NewField(fName, options...).SetQueryField(field.Name) td.Fields[fName] = f val.Set(reflect.ValueOf(f)) } }) // meta.Struct.EachRaw(strPtr, func(field reflect.StructField, val reflect.Value) { return }
// TODO make a compilable version that saves the infos about // fieldnumbers etc and allows faster queriing func (ø *Row) SelectByStructs(result interface{}, tagVal string, opts ...interface{}) (int, error) { /* if !meta.Slice.Check(result) { return 0, fmt.Errorf("result is no slice") } */ slic := reflect.ValueOf(result) l := slic.Len() if l == 0 { return 0, fmt.Errorf("result slice has length 0") } stru, err := meta.StructByValue(slic.Index(0)) if err != nil { return 0, err } tags, err2 := stru.Tags() if err2 != nil { return 0, err2 } // tags := meta.Struct.Tags(slic.Index(0).Interface()) options := []interface{}{Limit(l)} order := []string{} for k, v := range tags { if t := v.Get("db.select"); t != "" && strings.Contains(t, tagVal) { fi := ø.queryField(k, opts...) if fi == nil { panic("can't find field " + k + " : no Queryfield property of given fields or aliases") } options = append(options, fi) order = append(order, k) } } options = append(options, opts...) rows, err := ø.Find(options...) if err != nil { return 0, fmt.Errorf("error in find: %s", err.Error()) } i := 0 errs := []string{} for rows.Next() { ro, e := rows.ScanRow() if e != nil { errs = append(errs, e.Error()) continue } e = ro.GetStruct(tagVal, slic.Index(i).Addr().Interface()) if e != nil { errs = append(errs, fmt.Sprintf("error while scanning row %v: %s", i, e.Error())) } i++ } if len(errs) > 0 { return i, fmt.Errorf(strings.Join(errs, "\n")) } return i, nil }
func (r *CRUD) scanFields() (err error) { var hasDeleteField bool //fn := func(fld reflect.StructField, vl reflect.Value, tag string) { fn := func(fld *meta.Field) { if err != nil { return } tag := fld.Type.Tag.Get("rest") if tag == "" { return } methods := map[string]bool{} if strings.Contains(tag, "C") { methods["C"] = true } if strings.Contains(tag, "D") { if hasDeleteField { err = fmt.Errorf("more than one delete field (key) is not supported") return } methods["D"] = true hasDeleteField = true } if strings.Contains(tag, "L") { methods["L"] = true } if strings.Contains(tag, "U") { methods["U"] = true } if strings.Contains(tag, "R") { methods["R"] = true } if len(methods) == 0 { return } //ff := r.field(fld.Name) ff := r.field(fld.Type.Name) if ff == nil { err = fmt.Errorf("can't find field for table %s field %s\n", r.typeString(), fld.Type.Name) return } // pgsql.flags if strings.Contains(fld.Type.Tag.Get("db"), "PKEY") { if r.primaryKey != nil { err = fmt.Errorf("can't have more than one primary key %s and %s\n", r.primaryKey, fld.Type.Name) return } r.primaryKey = r.field(fld.Type.Name) } r.fields[fld.Type.Name] = methods } var stru *meta.Struct stru, err = meta.StructByValue(reflect.ValueOf(r.prototype)) stru.Each(fn) //meta.Struct.EachTag(r.prototype, "rest", fn) if r.primaryKey == nil { err = fmt.Errorf("has not primary key, add db:\"PKEY\"") } pkType := r.primaryKey.Type if !pkType.IsCompatible(IntType) { if pkType.IsCompatible(TextType) { r.pKeyIsString = true return } err = fmt.Errorf("primary key %s (%s) is not compatible to int or string", r.primaryKey.Name, pkType.String()) } return }
/* only the fields that are *fat.Field and not nil are chosen to set the row. øptrToFatStruct must be registered with RegisterTable before using this function */ func (r *Registry) ToRow(øptrToFatStruct interface{}, row *Row) (err error) { var stru *meta.Struct stru, err = meta.StructByValue(reflect.ValueOf(øptrToFatStruct)) if err != nil { return } t := r.TableOf(øptrToFatStruct) if t == nil { err = fmt.Errorf("%T is not registered, use RegisterTable", øptrToFatStruct) return } if row.Table != t { err = fmt.Errorf("table of the given fatstruct (%s) is not the same as table of the given row (%s)", t.Sql().String(), row.Table.Sql().String(), ) } if err != nil { return } fn := func(field *meta.Field) { // stop on first error if err != nil { return } if field.Value.IsNil() { return } ff, isFat := field.Value.Interface().(*fat.Field) if !isFat { return } rowField := r.FieldOf(ff) v := ff.Get() switch v.(type) { case []fat.Type: vl := ff.String() vl = strings.Replace(vl, "[", "{", -1) vl = strings.Replace(vl, "]", "}", -1) err = row.Set(rowField, vl) case map[string]fat.Type: err = row.Set(rowField, ff.String()) default: err = row.Set(rowField, v) } } stru.Each(fn) return }