diff --git a/engine.go b/engine.go index 50b45a78..d9d49b09 100644 --- a/engine.go +++ b/engine.go @@ -6,6 +6,7 @@ import ( "database/sql" "errors" "fmt" + "io" "os" "reflect" "strconv" @@ -265,6 +266,77 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) { return tables, nil } +func (engine *Engine) DumpAllToFile(fp string) error { + f, err := os.Create(fp) + if err != nil { + return err + } + defer f.Close() + return engine.DumpAll(f) +} + +func (engine *Engine) DumpAll(w io.Writer) error { + tables, err := engine.DBMetas() + if err != nil { + return err + } + + for _, table := range tables { + _, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", "", "")+"\n\n") + if err != nil { + return err + } + for _, index := range table.Indexes { + _, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+"\n\n") + if err != nil { + return err + } + } + + rows, err := engine.DB().Query("SELECT * FROM " + engine.Quote(table.Name)) + if err != nil { + return err + } + cols, err := rows.Columns() + if err != nil { + return err + } + if len(cols) == 0 { + continue + } + for rows.Next() { + dest := make([]interface{}, len(cols)) + err = rows.ScanSlice(&dest) + if err != nil { + return err + } + + _, err = io.WriteString(w, "INSERT INTO "+engine.Quote(table.Name)+" ("+engine.Quote(strings.Join(cols, engine.Quote(", ")))+") VALUES (") + if err != nil { + return err + } + + var temp string + for _, d := range dest { + if d == nil { + temp += ", NULL" + } else if reflect.TypeOf(d).Kind() == reflect.String { + temp += ", '" + strings.Replace(d.(string), "'", "''", -1) + "'" + } else if reflect.TypeOf(d).Kind() == reflect.Slice { + temp += fmt.Sprintf(", %s", engine.dialect.FormatBytes(d.([]byte))) + } else { + temp += fmt.Sprintf(", %v", d) + } + } + _, err = io.WriteString(w, temp[2:]+");\n\n") + if err != nil { + return err + } + } + } + return nil +} + // use cascade or not func (engine *Engine) Cascade(trueOrFalse ...bool) *Session { session := engine.NewSession() diff --git a/examples/tables.go b/examples/tables.go new file mode 100644 index 00000000..97c842be --- /dev/null +++ b/examples/tables.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "os" + + "github.com/go-xorm/xorm" + _ "github.com/mattn/go-sqlite3" +) + +func main() { + if len(os.Args) < 2 { + fmt.Println("need db path") + return + } + + orm, err := xorm.NewEngine("sqlite3", os.Args[1]) + if err != nil { + fmt.Println(err) + return + } + defer orm.Close() + orm.ShowSQL = true + + tables, err := orm.DBMetas() + if err != nil { + fmt.Println(err) + return + } + + for _, table := range tables { + fmt.Println(table.Name) + } +} diff --git a/processors.go b/processors.go index 770515e6..03ae8e0f 100644 --- a/processors.go +++ b/processors.go @@ -15,6 +15,10 @@ type BeforeDeleteProcessor interface { BeforeDelete() } +type BeforeSetProcessor interface { + BeforeSet(string, Cell) +} + // !nashtsai! TODO enable BeforeValidateProcessor when xorm start to support validations //// Executed before an object is validated //type BeforeValidateProcessor interface { diff --git a/session.go b/session.go index 921849eb..f312e41b 100644 --- a/session.go +++ b/session.go @@ -883,7 +883,6 @@ func (session *Session) Rows(bean interface{}) (*Rows, error) { // are conditions. beans could be []Struct, []*Struct, map[int64]Struct // map[int64]*Struct func (session *Session) Iterate(bean interface{}, fun IterFunc) error { - rows, err := session.Rows(bean) if err != nil { return err @@ -982,24 +981,6 @@ func (session *Session) Get(bean interface{}) (bool, error) { } else { return false, nil } - - // resultsSlice, err := session.query(sqlStr, args...) - // if err != nil { - // return false, err - // } - // if len(resultsSlice) < 1 { - // return false, nil - // } - - // err = session.scanMapIntoStruct(bean, resultsSlice[0]) - // if err != nil { - // return true, err - // } - // if len(resultsSlice) == 1 { - // return true, nil - // } else { - // return true, errors.New("More than one record") - // } } // Count counts the records. bean's non-empty fields @@ -1441,6 +1422,8 @@ func (session *Session) getField(dataStruct *reflect.Value, key string, table *c return fieldValue } +type Cell *interface{} + func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error { dataStruct := rValue(bean) if dataStruct.Kind() != reflect.Struct { @@ -1449,18 +1432,24 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i table := session.Engine.autoMapType(dataStruct) - scanResultContainers := make([]interface{}, len(fields)) + scanResults := make([]interface{}, len(fields)) for i := 0; i < len(fields); i++ { - var scanResultContainer interface{} - scanResultContainers[i] = &scanResultContainer + var cell interface{} + scanResults[i] = &cell } - if err := rows.Scan(scanResultContainers...); err != nil { + if err := rows.Scan(scanResults...); err != nil { return err } + b, hasBeforeSet := bean.(BeforeSetProcessor) + for ii, key := range fields { + if hasBeforeSet { + b.BeforeSet(fields[ii], Cell(scanResults[ii].(*interface{}))) + } + if fieldValue := session.getField(&dataStruct, key, table); fieldValue != nil { - rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) + rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii])) //if row is null then ignore if rawValue.Interface() == nil { @@ -1468,7 +1457,18 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i continue } - if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { + if fieldValue.CanAddr() { + if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { + if data, err := value2Bytes(&rawValue); err == nil { + structConvert.FromDB(data) + } else { + session.Engine.LogError(err) + } + continue + } + } + + if structConvert, ok := fieldValue.Interface().(core.Conversion); ok { if data, err := value2Bytes(&rawValue); err == nil { structConvert.FromDB(data) } else { @@ -2451,6 +2451,16 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val } } } + + if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok { + data, err := fieldConvert.ToDB() + if err != nil { + return 0, err + } else { + return string(data), nil + } + } + fieldType := fieldValue.Type() k := fieldType.Kind() if k == reflect.Ptr { @@ -2470,11 +2480,6 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val switch k { case reflect.Bool: return fieldValue.Bool(), nil - /*if fieldValue.Bool() { - return 1, nil - } else { - return 0, nil - }*/ case reflect.String: return fieldValue.String(), nil case reflect.Struct: diff --git a/sqlite3_dialect.go b/sqlite3_dialect.go index 0e19f96c..a889a0ab 100644 --- a/sqlite3_dialect.go +++ b/sqlite3_dialect.go @@ -1,6 +1,7 @@ package xorm import ( + "fmt" "strings" "github.com/go-xorm/core" @@ -44,6 +45,10 @@ func (db *sqlite3) SqlType(c *core.Column) string { } } +func (db *sqlite3) FormatBytes(bs []byte) string { + return fmt.Sprintf("X'%x'", bs) +} + func (db *sqlite3) SupportInsertMany() bool { return true } diff --git a/statement.go b/statement.go index 1ae46f3d..0c2207d8 100644 --- a/statement.go +++ b/statement.go @@ -451,8 +451,11 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, } fieldValue := *fieldValuePtr - fieldType := reflect.TypeOf(fieldValue.Interface()) + if fieldValue.Interface() == nil { + continue + } + fieldType := reflect.TypeOf(fieldValue.Interface()) requiredField := useAllCols if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok { if b {