package intimate import ( "database/sql" "fmt" "reflect" "testing" ) type Store struct { db *sql.DB } type Table struct { store *Store name string setting interface{} updatesql string selectsql string insertsql string } // const updatesql = "UPDATE %s SET %s WHERE %s = ?" func NewStore(uri string) *Store { db, err := sql.Open("mysql", uri) if err != nil { panic(err) } s := &Store{db: db} return s } func (store *Store) Table(name string) *Table { table := &Table{store: store} table.name = name table.insertsql = `INSERT INTO ` + table.name + `(%s) values(%s)` table.updatesql = `UPDATE ` + table.name + ` SET %s WHERE %s = ?` return table } type Queue struct { table *Table obj reflect.Type } func (t *Table) Insert(obj interface{}) error { ov := reflect.ValueOf(obj).Elem() ot := reflect.TypeOf(obj) fieldsql := "" argssql := "" var args []interface{} for i := 0; i < ov.NumField(); i++ { field := ov.Field(i) ftype := ot.Elem().Field(i) if fname, ok := ftype.Tag.Lookup("field"); ok { if flag, ok := ftype.Tag.Lookup("uid"); ok { if flag == "auto" { continue } } k := ftype.Type.Kind() if k == reflect.Ptr || k == reflect.Interface { if !field.IsNil() { felem := field.Elem() args = append(args, felem.Interface()) fieldsql += fname + "," argssql += "?," } } else { args = append(args, field.Interface()) fieldsql += fname + "," argssql += "?," } } } ssql := fmt.Sprintf(t.insertsql, fieldsql[:len(fieldsql)-1], argssql[:len(argssql)-1]) _, err := t.store.db.Exec(ssql, args...) return err } func (t *Table) Update(obj interface{}) error { ov := reflect.ValueOf(obj).Elem() ot := reflect.TypeOf(obj) fieldsql := "" var uidname string var uidvalue interface{} var args []interface{} for i := 0; i < ov.NumField(); i++ { field := ov.Field(i) ftype := ot.Elem().Field(i) if fname, ok := ftype.Tag.Lookup("field"); ok { if _, ok := ftype.Tag.Lookup("uid"); ok { if uidvalue != nil { panic(fmt.Errorf("uid must unique, %s and %s", uidname, fname)) } uidname = fname uidvalue = field.Interface() continue } k := ftype.Type.Kind() if k == reflect.Ptr || k == reflect.Interface { if !field.IsNil() { felem := field.Elem() args = append(args, felem.Interface()) fieldsql += fname + " = ?," } } else { args = append(args, field.Interface()) fieldsql += fname + " = ?," } } } if uidvalue == nil { panic(fmt.Errorf("update must contain `uid` tag")) } usql := fmt.Sprintf(t.updatesql, fieldsql[:len(fieldsql)-1], uidname) args = append(args, uidvalue) _, err := t.store.db.Exec(usql, args...) return err } type TSreamer struct { Uid int `field:"uid" uid:"auto"` Name interface{} `field:"name"` UserID *sql.NullString `field:"userid"` Ext *sql.NullString `field:"ext"` Iface interface{} `field:"tag"` } func TestAutoStore(t *testing.T) { uri := "root:@tcp(127.0.0.1:4000)/test?parseTime=true&loc=Local&charset=utf8mb4&collation=utf8mb4_unicode_ci" store := NewStore(uri) streamer := &TSreamer{} streamer.Uid = 2 streamer.UserID = nil streamer.Name = "streamer" streamer.Ext = &sql.NullString{String: "ext", Valid: true} err := store.Table("streamer").Update(streamer) if err != nil { t.Error(err) } }