diff options
Diffstat (limited to 'vendor/github.com/go-xorm/xorm/engine.go')
-rw-r--r-- | vendor/github.com/go-xorm/xorm/engine.go | 305 |
1 files changed, 128 insertions, 177 deletions
diff --git a/vendor/github.com/go-xorm/xorm/engine.go b/vendor/github.com/go-xorm/xorm/engine.go index be90ddbe4e..a27b6bdf2f 100644 --- a/vendor/github.com/go-xorm/xorm/engine.go +++ b/vendor/github.com/go-xorm/xorm/engine.go @@ -44,6 +44,8 @@ type Engine struct { DatabaseTZ *time.Location // The timezone of the database disableGlobalCache bool + + tagHandlers map[string]tagHandler } // ShowSQL show SQL statement or not on logger if log level is great than INFO @@ -215,10 +217,15 @@ func (engine *Engine) NoCascade() *Session { } // MapCacher Set a table use a special cacher -func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) { +func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) error { v := rValue(bean) - tb := engine.autoMapType(v) + tb, err := engine.autoMapType(v) + if err != nil { + return err + } + tb.Cacher = cacher + return nil } // NewDB provides an interface to operate database directly @@ -260,9 +267,9 @@ func (engine *Engine) Ping() error { func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) { if engine.showSQL && !engine.showExecTime { if len(sqlArgs) > 0 { - engine.logger.Infof("[sql] %v [args] %v", sqlStr, sqlArgs) + engine.logger.Infof("[SQL] %v %v", sqlStr, sqlArgs) } else { - engine.logger.Infof("[sql] %v", sqlStr) + engine.logger.Infof("[SQL] %v", sqlStr) } } } @@ -273,9 +280,9 @@ func (engine *Engine) logSQLQueryTime(sqlStr string, args []interface{}, executi stmt, res, err := executionBlock() execDuration := time.Since(b4ExecTime) if len(args) > 0 { - engine.logger.Infof("[sql] %s [args] %v - took: %v", sqlStr, args, execDuration) + engine.logger.Infof("[SQL] %s %v - took: %v", sqlStr, args, execDuration) } else { - engine.logger.Infof("[sql] %s - took: %v", sqlStr, execDuration) + engine.logger.Infof("[SQL] %s - took: %v", sqlStr, execDuration) } return stmt, res, err } @@ -774,13 +781,18 @@ func (engine *Engine) Having(conditions string) *Session { return session.Having(conditions) } -func (engine *Engine) autoMapType(v reflect.Value) *core.Table { +func (engine *Engine) autoMapType(v reflect.Value) (*core.Table, error) { t := v.Type() engine.mutex.Lock() defer engine.mutex.Unlock() table, ok := engine.Tables[t] if !ok { - table = engine.mapType(v) + var err error + table, err = engine.mapType(v) + if err != nil { + return nil, err + } + engine.Tables[t] = table if engine.Cacher != nil { if v.CanAddr() { @@ -790,12 +802,11 @@ func (engine *Engine) autoMapType(v reflect.Value) *core.Table { } } } - return table + return table, nil } // GobRegister register one struct to gob for cache use func (engine *Engine) GobRegister(v interface{}) *Engine { - //fmt.Printf("Type: %[1]T => Data: %[1]#v\n", v) gob.Register(v) return engine } @@ -806,10 +817,19 @@ type Table struct { Name string } +// IsValid if table is valid +func (t *Table) IsValid() bool { + return t.Table != nil && len(t.Name) > 0 +} + // TableInfo get table info according to bean's content func (engine *Engine) TableInfo(bean interface{}) *Table { v := rValue(bean) - return &Table{engine.autoMapType(v), engine.tbName(v)} + tb, err := engine.autoMapType(v) + if err != nil { + engine.logger.Error(err) + } + return &Table{tb, engine.tbName(v)} } func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) { @@ -842,7 +862,7 @@ var ( tpTableName = reflect.TypeOf((*TableName)(nil)).Elem() ) -func (engine *Engine) mapType(v reflect.Value) *core.Table { +func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) { t := v.Type() table := engine.newTable() if tb, ok := v.Interface().(TableName); ok { @@ -861,7 +881,6 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { table.Type = t var idFieldColName string - var err error var hasCacheTag, hasNoCacheTag bool for i := 0; i < t.NumField(); i++ { @@ -881,186 +900,94 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { if tags[0] == "-" { continue } + + var ctx = tagContext{ + table: table, + col: col, + fieldValue: fieldValue, + indexNames: make(map[string]int), + engine: engine, + } + if strings.ToUpper(tags[0]) == "EXTENDS" { - switch fieldValue.Kind() { - case reflect.Ptr: - f := fieldValue.Type().Elem() - if f.Kind() == reflect.Struct { - fieldPtr := fieldValue - fieldValue = fieldValue.Elem() - if !fieldValue.IsValid() || fieldPtr.IsNil() { - fieldValue = reflect.New(f).Elem() - } - } - fallthrough - case reflect.Struct: - parentTable := engine.mapType(fieldValue) - for _, col := range parentTable.Columns() { - col.FieldName = fmt.Sprintf("%v.%v", t.Field(i).Name, col.FieldName) - table.AddColumn(col) - for indexName, indexType := range col.Indexes { - addIndex(indexName, table, col, indexType) - } - } - continue - default: - //TODO: warning + if err := ExtendsTagHandler(&ctx); err != nil { + return nil, err } + continue } - indexNames := make(map[string]int) - var isIndex, isUnique bool - var preKey string for j, key := range tags { + if ctx.ignoreNext { + ctx.ignoreNext = false + continue + } + k := strings.ToUpper(key) - switch { - case k == "<-": - col.MapType = core.ONLYFROMDB - case k == "->": - col.MapType = core.ONLYTODB - case k == "PK": - col.IsPrimaryKey = true - col.Nullable = false - case k == "NULL": - if j == 0 { - col.Nullable = true - } else { - col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT") - } - // TODO: for postgres how add autoincr? - /*case strings.HasPrefix(k, "AUTOINCR(") && strings.HasSuffix(k, ")"): - col.IsAutoIncrement = true + ctx.tagName = k - autoStart := k[len("AUTOINCR")+1 : len(k)-1] - autoStartInt, err := strconv.Atoi(autoStart) - if err != nil { - engine.LogError(err) + pStart := strings.Index(k, "(") + if pStart == 0 { + return nil, errors.New("( could not be the first charactor") } - col.AutoIncrStart = autoStartInt*/ - case k == "AUTOINCR": - col.IsAutoIncrement = true - //col.AutoIncrStart = 1 - case k == "DEFAULT": - col.Default = tags[j+1] - case k == "CREATED": - col.IsCreated = true - case k == "VERSION": - col.IsVersion = true - col.Default = "1" - case k == "UTC": - col.TimeZone = time.UTC - case k == "LOCAL": - col.TimeZone = time.Local - case strings.HasPrefix(k, "LOCALE(") && strings.HasSuffix(k, ")"): - location := k[len("LOCALE")+1 : len(k)-1] - col.TimeZone, err = time.LoadLocation(location) - if err != nil { - engine.logger.Error(err) - } - case k == "UPDATED": - col.IsUpdated = true - case k == "DELETED": - col.IsDeleted = true - case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"): - indexName := k[len("INDEX")+1 : len(k)-1] - indexNames[indexName] = core.IndexType - case k == "INDEX": - isIndex = true - case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"): - indexName := k[len("UNIQUE")+1 : len(k)-1] - indexNames[indexName] = core.UniqueType - case k == "UNIQUE": - isUnique = true - case k == "NOTNULL": - col.Nullable = false - case k == "CACHE": - if !hasCacheTag { - hasCacheTag = true + if pStart > -1 { + if !strings.HasSuffix(k, ")") { + return nil, errors.New("cannot match ) charactor") } - case k == "NOCACHE": - if !hasNoCacheTag { - hasNoCacheTag = true + + ctx.tagName = k[:pStart] + ctx.params = strings.Split(k[pStart+1:len(k)-1], ",") + } + + if j > 0 { + ctx.preTag = strings.ToUpper(tags[j-1]) + } + if j < len(tags)-1 { + ctx.nextTag = strings.ToUpper(tags[j+1]) + } else { + ctx.nextTag = "" + } + + if h, ok := engine.tagHandlers[ctx.tagName]; ok { + if err := h(&ctx); err != nil { + return nil, err } - case k == "NOT": - default: - if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") { - if preKey != "DEFAULT" { - col.Name = key[1 : len(key)-1] - } - } else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") { - fs := strings.Split(k, "(") - - if _, ok := core.SqlTypes[fs[0]]; !ok { - preKey = k - continue - } - col.SQLType = core.SQLType{Name: fs[0]} - if fs[0] == core.Enum && fs[1][0] == '\'' { //enum - options := strings.Split(fs[1][0:len(fs[1])-1], ",") - col.EnumOptions = make(map[string]int) - for k, v := range options { - v = strings.TrimSpace(v) - v = strings.Trim(v, "'") - col.EnumOptions[v] = k - } - } else if fs[0] == core.Set && fs[1][0] == '\'' { //set - options := strings.Split(fs[1][0:len(fs[1])-1], ",") - col.SetOptions = make(map[string]int) - for k, v := range options { - v = strings.TrimSpace(v) - v = strings.Trim(v, "'") - col.SetOptions[v] = k - } - } else { - fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",") - if len(fs2) == 2 { - col.Length, err = strconv.Atoi(fs2[0]) - if err != nil { - engine.logger.Error(err) - } - col.Length2, err = strconv.Atoi(fs2[1]) - if err != nil { - engine.logger.Error(err) - } - } else if len(fs2) == 1 { - col.Length, err = strconv.Atoi(fs2[0]) - if err != nil { - engine.logger.Error(err) - } - } - } + } else { + if strings.HasPrefix(key, "'") && strings.HasSuffix(key, "'") { + col.Name = key[1 : len(key)-1] } else { - if _, ok := core.SqlTypes[k]; ok { - col.SQLType = core.SQLType{Name: k} - } else if key != col.Default { - col.Name = key - } + col.Name = key } - engine.dialect.SqlType(col) } - preKey = k + + if ctx.hasCacheTag { + hasCacheTag = true + } + if ctx.hasNoCacheTag { + hasNoCacheTag = true + } } + if col.SQLType.Name == "" { col.SQLType = core.Type2SQLType(fieldType) } + engine.dialect.SqlType(col) if col.Length == 0 { col.Length = col.SQLType.DefaultLength } if col.Length2 == 0 { col.Length2 = col.SQLType.DefaultLength2 } - if col.Name == "" { col.Name = engine.ColumnMapper.Obj2Table(t.Field(i).Name) } - if isUnique { - indexNames[col.Name] = core.UniqueType - } else if isIndex { - indexNames[col.Name] = core.IndexType + if ctx.isUnique { + ctx.indexNames[col.Name] = core.UniqueType + } else if ctx.isIndex { + ctx.indexNames[col.Name] = core.IndexType } - for indexName, indexType := range indexNames { + for indexName, indexType := range ctx.indexNames { addIndex(indexName, table, col, indexType) } } @@ -1114,7 +1041,7 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { table.Cacher = nil } - return table + return table, nil } // IsTableEmpty if a table has any reocrd @@ -1152,8 +1079,21 @@ func (engine *Engine) IdOfV(rv reflect.Value) core.PK { // IDOfV get id from one value of struct func (engine *Engine) IDOfV(rv reflect.Value) core.PK { + pk, err := engine.idOfV(rv) + if err != nil { + engine.logger.Error(err) + return nil + } + return pk +} + +func (engine *Engine) idOfV(rv reflect.Value) (core.PK, error) { v := reflect.Indirect(rv) - table := engine.autoMapType(v) + table, err := engine.autoMapType(v) + if err != nil { + return nil, err + } + pk := make([]interface{}, len(table.PrimaryKeys)) for i, col := range table.PKColumns() { pkField := v.FieldByName(col.FieldName) @@ -1166,7 +1106,7 @@ func (engine *Engine) IDOfV(rv reflect.Value) core.PK { pk[i] = pkField.Uint() } } - return core.PK(pk) + return core.PK(pk), nil } // CreateIndexes create indexes @@ -1187,13 +1127,6 @@ func (engine *Engine) getCacher2(table *core.Table) core.Cacher { return table.Cacher } -func (engine *Engine) getCacher(v reflect.Value) core.Cacher { - if table := engine.autoMapType(v); table != nil { - return table.Cacher - } - return engine.Cacher -} - // ClearCacheBean if enabled cache, clear the cache bean func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { v := rValue(bean) @@ -1202,7 +1135,10 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { return errors.New("error params") } tableName := engine.tbName(v) - table := engine.autoMapType(v) + table, err := engine.autoMapType(v) + if err != nil { + return err + } cacher := table.Cacher if cacher == nil { cacher = engine.Cacher @@ -1223,7 +1159,11 @@ func (engine *Engine) ClearCache(beans ...interface{}) error { return errors.New("error params") } tableName := engine.tbName(v) - table := engine.autoMapType(v) + table, err := engine.autoMapType(v) + if err != nil { + return err + } + cacher := table.Cacher if cacher == nil { cacher = engine.Cacher @@ -1243,7 +1183,11 @@ func (engine *Engine) Sync(beans ...interface{}) error { for _, bean := range beans { v := rValue(bean) tableName := engine.tbName(v) - table := engine.autoMapType(v) + table, err := engine.autoMapType(v) + fmt.Println(v, table, err) + if err != nil { + return err + } s := engine.NewSession() defer s.Close() @@ -1426,6 +1370,13 @@ func (engine *Engine) Query(sql string, paramStr ...interface{}) (resultsSlice [ return session.Query(sql, paramStr...) } +// QueryString runs a raw sql and return records as []map[string]string +func (engine *Engine) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { + session := engine.NewSession() + defer session.Close() + return session.QueryString(sqlStr, args...) +} + // Insert one or more records func (engine *Engine) Insert(beans ...interface{}) (int64, error) { session := engine.NewSession() |