mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	fix migrate failed and org dashboard failed on MSSQL database (#1448)
This commit is contained in:
		
							
								
								
									
										673
									
								
								vendor/github.com/go-xorm/xorm/session_convert.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										673
									
								
								vendor/github.com/go-xorm/xorm/session_convert.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,673 @@ | ||||
| // Copyright 2017 The Xorm Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package xorm | ||||
|  | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"database/sql/driver" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/go-xorm/core" | ||||
| ) | ||||
|  | ||||
| func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) { | ||||
| 	sdata := strings.TrimSpace(data) | ||||
| 	var x time.Time | ||||
| 	var err error | ||||
|  | ||||
| 	if sdata == "0000-00-00 00:00:00" || | ||||
| 		sdata == "0001-01-01 00:00:00" { | ||||
| 	} else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column | ||||
| 		// time stamp | ||||
| 		sd, err := strconv.ParseInt(sdata, 10, 64) | ||||
| 		if err == nil { | ||||
| 			x = time.Unix(sd, 0) | ||||
| 			// !nashtsai! HACK mymysql driver is causing Local location being change to CHAT and cause wrong time conversion | ||||
| 			if col.TimeZone == nil { | ||||
| 				x = x.In(session.Engine.TZLocation) | ||||
| 			} else { | ||||
| 				x = x.In(col.TimeZone) | ||||
| 			} | ||||
| 			session.Engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) | ||||
| 		} else { | ||||
| 			session.Engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) | ||||
| 		} | ||||
| 	} else if len(sdata) > 19 && strings.Contains(sdata, "-") { | ||||
| 		x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation) | ||||
| 		session.Engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) | ||||
| 		if err != nil { | ||||
| 			x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation) | ||||
| 			session.Engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation) | ||||
| 			session.Engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) | ||||
| 		} | ||||
|  | ||||
| 	} else if len(sdata) == 19 && strings.Contains(sdata, "-") { | ||||
| 		x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation) | ||||
| 		session.Engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) | ||||
| 	} else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' { | ||||
| 		x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation) | ||||
| 		session.Engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) | ||||
| 	} else if col.SQLType.Name == core.Time { | ||||
| 		if strings.Contains(sdata, " ") { | ||||
| 			ssd := strings.Split(sdata, " ") | ||||
| 			sdata = ssd[1] | ||||
| 		} | ||||
|  | ||||
| 		sdata = strings.TrimSpace(sdata) | ||||
| 		if session.Engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 { | ||||
| 			sdata = sdata[len(sdata)-8:] | ||||
| 		} | ||||
|  | ||||
| 		st := fmt.Sprintf("2006-01-02 %v", sdata) | ||||
| 		x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation) | ||||
| 		session.Engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) | ||||
| 	} else { | ||||
| 		outErr = fmt.Errorf("unsupported time format %v", sdata) | ||||
| 		return | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err) | ||||
| 		return | ||||
| 	} | ||||
| 	outTime = x | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.Time, outErr error) { | ||||
| 	return session.str2Time(col, string(data)) | ||||
| } | ||||
|  | ||||
| // convert a db data([]byte) to a field value | ||||
| func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, data []byte) error { | ||||
| 	if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { | ||||
| 		return structConvert.FromDB(data) | ||||
| 	} | ||||
|  | ||||
| 	if structConvert, ok := fieldValue.Interface().(core.Conversion); ok { | ||||
| 		return structConvert.FromDB(data) | ||||
| 	} | ||||
|  | ||||
| 	var v interface{} | ||||
| 	key := col.Name | ||||
| 	fieldType := fieldValue.Type() | ||||
|  | ||||
| 	switch fieldType.Kind() { | ||||
| 	case reflect.Complex64, reflect.Complex128: | ||||
| 		x := reflect.New(fieldType) | ||||
| 		if len(data) > 0 { | ||||
| 			err := json.Unmarshal(data, x.Interface()) | ||||
| 			if err != nil { | ||||
| 				session.Engine.logger.Error(err) | ||||
| 				return err | ||||
| 			} | ||||
| 			fieldValue.Set(x.Elem()) | ||||
| 		} | ||||
| 	case reflect.Slice, reflect.Array, reflect.Map: | ||||
| 		v = data | ||||
| 		t := fieldType.Elem() | ||||
| 		k := t.Kind() | ||||
| 		if col.SQLType.IsText() { | ||||
| 			x := reflect.New(fieldType) | ||||
| 			if len(data) > 0 { | ||||
| 				err := json.Unmarshal(data, x.Interface()) | ||||
| 				if err != nil { | ||||
| 					session.Engine.logger.Error(err) | ||||
| 					return err | ||||
| 				} | ||||
| 				fieldValue.Set(x.Elem()) | ||||
| 			} | ||||
| 		} else if col.SQLType.IsBlob() { | ||||
| 			if k == reflect.Uint8 { | ||||
| 				fieldValue.Set(reflect.ValueOf(v)) | ||||
| 			} else { | ||||
| 				x := reflect.New(fieldType) | ||||
| 				if len(data) > 0 { | ||||
| 					err := json.Unmarshal(data, x.Interface()) | ||||
| 					if err != nil { | ||||
| 						session.Engine.logger.Error(err) | ||||
| 						return err | ||||
| 					} | ||||
| 					fieldValue.Set(x.Elem()) | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			return ErrUnSupportedType | ||||
| 		} | ||||
| 	case reflect.String: | ||||
| 		fieldValue.SetString(string(data)) | ||||
| 	case reflect.Bool: | ||||
| 		d := string(data) | ||||
| 		v, err := strconv.ParseBool(d) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("arg %v as bool: %s", key, err.Error()) | ||||
| 		} | ||||
| 		fieldValue.Set(reflect.ValueOf(v)) | ||||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||
| 		sdata := string(data) | ||||
| 		var x int64 | ||||
| 		var err error | ||||
| 		// for mysql, when use bit, it returned \x01 | ||||
| 		if col.SQLType.Name == core.Bit && | ||||
| 			session.Engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API | ||||
| 			if len(data) == 1 { | ||||
| 				x = int64(data[0]) | ||||
| 			} else { | ||||
| 				x = 0 | ||||
| 			} | ||||
| 		} else if strings.HasPrefix(sdata, "0x") { | ||||
| 			x, err = strconv.ParseInt(sdata, 16, 64) | ||||
| 		} else if strings.HasPrefix(sdata, "0") { | ||||
| 			x, err = strconv.ParseInt(sdata, 8, 64) | ||||
| 		} else if strings.EqualFold(sdata, "true") { | ||||
| 			x = 1 | ||||
| 		} else if strings.EqualFold(sdata, "false") { | ||||
| 			x = 0 | ||||
| 		} else { | ||||
| 			x, err = strconv.ParseInt(sdata, 10, 64) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 		} | ||||
| 		fieldValue.SetInt(x) | ||||
| 	case reflect.Float32, reflect.Float64: | ||||
| 		x, err := strconv.ParseFloat(string(data), 64) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("arg %v as float64: %s", key, err.Error()) | ||||
| 		} | ||||
| 		fieldValue.SetFloat(x) | ||||
| 	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: | ||||
| 		x, err := strconv.ParseUint(string(data), 10, 64) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 		} | ||||
| 		fieldValue.SetUint(x) | ||||
| 	//Currently only support Time type | ||||
| 	case reflect.Struct: | ||||
| 		// !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString | ||||
| 		if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { | ||||
| 			if err := nulVal.Scan(data); err != nil { | ||||
| 				return fmt.Errorf("sql.Scan(%v) failed: %s ", data, err.Error()) | ||||
| 			} | ||||
| 		} else { | ||||
| 			if fieldType.ConvertibleTo(core.TimeType) { | ||||
| 				x, err := session.byte2Time(col, data) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				v = x | ||||
| 				fieldValue.Set(reflect.ValueOf(v).Convert(fieldType)) | ||||
| 			} else if session.Statement.UseCascade { | ||||
| 				table, err := session.Engine.autoMapType(*fieldValue) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
|  | ||||
| 				// TODO: current only support 1 primary key | ||||
| 				if len(table.PrimaryKeys) > 1 { | ||||
| 					panic("unsupported composited primary key cascade") | ||||
| 				} | ||||
| 				var pk = make(core.PK, len(table.PrimaryKeys)) | ||||
| 				rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) | ||||
| 				pk[0], err = str2PK(string(data), rawValueType) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
|  | ||||
| 				if !isPKZero(pk) { | ||||
| 					// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch | ||||
| 					// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne | ||||
| 					// property to be fetched lazily | ||||
| 					structInter := reflect.New(fieldValue.Type()) | ||||
| 					newsession := session.Engine.NewSession() | ||||
| 					defer newsession.Close() | ||||
| 					has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) | ||||
| 					if err != nil { | ||||
| 						return err | ||||
| 					} | ||||
| 					if has { | ||||
| 						v = structInter.Elem().Interface() | ||||
| 						fieldValue.Set(reflect.ValueOf(v)) | ||||
| 					} else { | ||||
| 						return errors.New("cascade obj is not exist") | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	case reflect.Ptr: | ||||
| 		// !nashtsai! TODO merge duplicated codes above | ||||
| 		//typeStr := fieldType.String() | ||||
| 		switch fieldType.Elem().Kind() { | ||||
| 		// case "*string": | ||||
| 		case core.StringType.Kind(): | ||||
| 			x := string(data) | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*bool": | ||||
| 		case core.BoolType.Kind(): | ||||
| 			d := string(data) | ||||
| 			v, err := strconv.ParseBool(d) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as bool: %s", key, err.Error()) | ||||
| 			} | ||||
| 			fieldValue.Set(reflect.ValueOf(&v).Convert(fieldType)) | ||||
| 		// case "*complex64": | ||||
| 		case core.Complex64Type.Kind(): | ||||
| 			var x complex64 | ||||
| 			if len(data) > 0 { | ||||
| 				err := json.Unmarshal(data, &x) | ||||
| 				if err != nil { | ||||
| 					session.Engine.logger.Error(err) | ||||
| 					return err | ||||
| 				} | ||||
| 				fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 			} | ||||
| 		// case "*complex128": | ||||
| 		case core.Complex128Type.Kind(): | ||||
| 			var x complex128 | ||||
| 			if len(data) > 0 { | ||||
| 				err := json.Unmarshal(data, &x) | ||||
| 				if err != nil { | ||||
| 					session.Engine.logger.Error(err) | ||||
| 					return err | ||||
| 				} | ||||
| 				fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 			} | ||||
| 		// case "*float64": | ||||
| 		case core.Float64Type.Kind(): | ||||
| 			x, err := strconv.ParseFloat(string(data), 64) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as float64: %s", key, err.Error()) | ||||
| 			} | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*float32": | ||||
| 		case core.Float32Type.Kind(): | ||||
| 			var x float32 | ||||
| 			x1, err := strconv.ParseFloat(string(data), 32) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as float32: %s", key, err.Error()) | ||||
| 			} | ||||
| 			x = float32(x1) | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*uint64": | ||||
| 		case core.Uint64Type.Kind(): | ||||
| 			var x uint64 | ||||
| 			x, err := strconv.ParseUint(string(data), 10, 64) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 			} | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*uint": | ||||
| 		case core.UintType.Kind(): | ||||
| 			var x uint | ||||
| 			x1, err := strconv.ParseUint(string(data), 10, 64) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 			} | ||||
| 			x = uint(x1) | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*uint32": | ||||
| 		case core.Uint32Type.Kind(): | ||||
| 			var x uint32 | ||||
| 			x1, err := strconv.ParseUint(string(data), 10, 64) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 			} | ||||
| 			x = uint32(x1) | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*uint8": | ||||
| 		case core.Uint8Type.Kind(): | ||||
| 			var x uint8 | ||||
| 			x1, err := strconv.ParseUint(string(data), 10, 64) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 			} | ||||
| 			x = uint8(x1) | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*uint16": | ||||
| 		case core.Uint16Type.Kind(): | ||||
| 			var x uint16 | ||||
| 			x1, err := strconv.ParseUint(string(data), 10, 64) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 			} | ||||
| 			x = uint16(x1) | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*int64": | ||||
| 		case core.Int64Type.Kind(): | ||||
| 			sdata := string(data) | ||||
| 			var x int64 | ||||
| 			var err error | ||||
| 			// for mysql, when use bit, it returned \x01 | ||||
| 			if col.SQLType.Name == core.Bit && | ||||
| 				strings.Contains(session.Engine.DriverName(), "mysql") { | ||||
| 				if len(data) == 1 { | ||||
| 					x = int64(data[0]) | ||||
| 				} else { | ||||
| 					x = 0 | ||||
| 				} | ||||
| 			} else if strings.HasPrefix(sdata, "0x") { | ||||
| 				x, err = strconv.ParseInt(sdata, 16, 64) | ||||
| 			} else if strings.HasPrefix(sdata, "0") { | ||||
| 				x, err = strconv.ParseInt(sdata, 8, 64) | ||||
| 			} else { | ||||
| 				x, err = strconv.ParseInt(sdata, 10, 64) | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 			} | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*int": | ||||
| 		case core.IntType.Kind(): | ||||
| 			sdata := string(data) | ||||
| 			var x int | ||||
| 			var x1 int64 | ||||
| 			var err error | ||||
| 			// for mysql, when use bit, it returned \x01 | ||||
| 			if col.SQLType.Name == core.Bit && | ||||
| 				strings.Contains(session.Engine.DriverName(), "mysql") { | ||||
| 				if len(data) == 1 { | ||||
| 					x = int(data[0]) | ||||
| 				} else { | ||||
| 					x = 0 | ||||
| 				} | ||||
| 			} else if strings.HasPrefix(sdata, "0x") { | ||||
| 				x1, err = strconv.ParseInt(sdata, 16, 64) | ||||
| 				x = int(x1) | ||||
| 			} else if strings.HasPrefix(sdata, "0") { | ||||
| 				x1, err = strconv.ParseInt(sdata, 8, 64) | ||||
| 				x = int(x1) | ||||
| 			} else { | ||||
| 				x1, err = strconv.ParseInt(sdata, 10, 64) | ||||
| 				x = int(x1) | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 			} | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*int32": | ||||
| 		case core.Int32Type.Kind(): | ||||
| 			sdata := string(data) | ||||
| 			var x int32 | ||||
| 			var x1 int64 | ||||
| 			var err error | ||||
| 			// for mysql, when use bit, it returned \x01 | ||||
| 			if col.SQLType.Name == core.Bit && | ||||
| 				session.Engine.dialect.DBType() == core.MYSQL { | ||||
| 				if len(data) == 1 { | ||||
| 					x = int32(data[0]) | ||||
| 				} else { | ||||
| 					x = 0 | ||||
| 				} | ||||
| 			} else if strings.HasPrefix(sdata, "0x") { | ||||
| 				x1, err = strconv.ParseInt(sdata, 16, 64) | ||||
| 				x = int32(x1) | ||||
| 			} else if strings.HasPrefix(sdata, "0") { | ||||
| 				x1, err = strconv.ParseInt(sdata, 8, 64) | ||||
| 				x = int32(x1) | ||||
| 			} else { | ||||
| 				x1, err = strconv.ParseInt(sdata, 10, 64) | ||||
| 				x = int32(x1) | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 			} | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*int8": | ||||
| 		case core.Int8Type.Kind(): | ||||
| 			sdata := string(data) | ||||
| 			var x int8 | ||||
| 			var x1 int64 | ||||
| 			var err error | ||||
| 			// for mysql, when use bit, it returned \x01 | ||||
| 			if col.SQLType.Name == core.Bit && | ||||
| 				strings.Contains(session.Engine.DriverName(), "mysql") { | ||||
| 				if len(data) == 1 { | ||||
| 					x = int8(data[0]) | ||||
| 				} else { | ||||
| 					x = 0 | ||||
| 				} | ||||
| 			} else if strings.HasPrefix(sdata, "0x") { | ||||
| 				x1, err = strconv.ParseInt(sdata, 16, 64) | ||||
| 				x = int8(x1) | ||||
| 			} else if strings.HasPrefix(sdata, "0") { | ||||
| 				x1, err = strconv.ParseInt(sdata, 8, 64) | ||||
| 				x = int8(x1) | ||||
| 			} else { | ||||
| 				x1, err = strconv.ParseInt(sdata, 10, 64) | ||||
| 				x = int8(x1) | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 			} | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*int16": | ||||
| 		case core.Int16Type.Kind(): | ||||
| 			sdata := string(data) | ||||
| 			var x int16 | ||||
| 			var x1 int64 | ||||
| 			var err error | ||||
| 			// for mysql, when use bit, it returned \x01 | ||||
| 			if col.SQLType.Name == core.Bit && | ||||
| 				strings.Contains(session.Engine.DriverName(), "mysql") { | ||||
| 				if len(data) == 1 { | ||||
| 					x = int16(data[0]) | ||||
| 				} else { | ||||
| 					x = 0 | ||||
| 				} | ||||
| 			} else if strings.HasPrefix(sdata, "0x") { | ||||
| 				x1, err = strconv.ParseInt(sdata, 16, 64) | ||||
| 				x = int16(x1) | ||||
| 			} else if strings.HasPrefix(sdata, "0") { | ||||
| 				x1, err = strconv.ParseInt(sdata, 8, 64) | ||||
| 				x = int16(x1) | ||||
| 			} else { | ||||
| 				x1, err = strconv.ParseInt(sdata, 10, 64) | ||||
| 				x = int16(x1) | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("arg %v as int: %s", key, err.Error()) | ||||
| 			} | ||||
| 			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) | ||||
| 		// case "*SomeStruct": | ||||
| 		case reflect.Struct: | ||||
| 			switch fieldType { | ||||
| 			// case "*.time.Time": | ||||
| 			case core.PtrTimeType: | ||||
| 				x, err := session.byte2Time(col, data) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				v = x | ||||
| 				fieldValue.Set(reflect.ValueOf(&x)) | ||||
| 			default: | ||||
| 				if session.Statement.UseCascade { | ||||
| 					structInter := reflect.New(fieldType.Elem()) | ||||
| 					table, err := session.Engine.autoMapType(structInter.Elem()) | ||||
| 					if err != nil { | ||||
| 						return err | ||||
| 					} | ||||
|  | ||||
| 					if len(table.PrimaryKeys) > 1 { | ||||
| 						panic("unsupported composited primary key cascade") | ||||
| 					} | ||||
| 					var pk = make(core.PK, len(table.PrimaryKeys)) | ||||
| 					rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) | ||||
| 					pk[0], err = str2PK(string(data), rawValueType) | ||||
| 					if err != nil { | ||||
| 						return err | ||||
| 					} | ||||
|  | ||||
| 					if !isPKZero(pk) { | ||||
| 						// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch | ||||
| 						// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne | ||||
| 						// property to be fetched lazily | ||||
| 						newsession := session.Engine.NewSession() | ||||
| 						defer newsession.Close() | ||||
| 						has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) | ||||
| 						if err != nil { | ||||
| 							return err | ||||
| 						} | ||||
| 						if has { | ||||
| 							v = structInter.Interface() | ||||
| 							fieldValue.Set(reflect.ValueOf(v)) | ||||
| 						} else { | ||||
| 							return errors.New("cascade obj is not exist") | ||||
| 						} | ||||
| 					} | ||||
| 				} else { | ||||
| 					return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String()) | ||||
| 				} | ||||
| 			} | ||||
| 		default: | ||||
| 			return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String()) | ||||
| 		} | ||||
| 	default: | ||||
| 		return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String()) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // convert a field value of a struct to interface for put into db | ||||
| func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Value) (interface{}, error) { | ||||
| 	if fieldValue.CanAddr() { | ||||
| 		if fieldConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { | ||||
| 			data, err := fieldConvert.ToDB() | ||||
| 			if err != nil { | ||||
| 				return 0, err | ||||
| 			} | ||||
| 			if col.SQLType.IsBlob() { | ||||
| 				return data, nil | ||||
| 			} | ||||
| 			return string(data), nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok { | ||||
| 		data, err := fieldConvert.ToDB() | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		if col.SQLType.IsBlob() { | ||||
| 			return data, nil | ||||
| 		} | ||||
| 		return string(data), nil | ||||
| 	} | ||||
|  | ||||
| 	fieldType := fieldValue.Type() | ||||
| 	k := fieldType.Kind() | ||||
| 	if k == reflect.Ptr { | ||||
| 		if fieldValue.IsNil() { | ||||
| 			return nil, nil | ||||
| 		} else if !fieldValue.IsValid() { | ||||
| 			session.Engine.logger.Warn("the field[", col.FieldName, "] is invalid") | ||||
| 			return nil, nil | ||||
| 		} else { | ||||
| 			// !nashtsai! deference pointer type to instance type | ||||
| 			fieldValue = fieldValue.Elem() | ||||
| 			fieldType = fieldValue.Type() | ||||
| 			k = fieldType.Kind() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	switch k { | ||||
| 	case reflect.Bool: | ||||
| 		return fieldValue.Bool(), nil | ||||
| 	case reflect.String: | ||||
| 		return fieldValue.String(), nil | ||||
| 	case reflect.Struct: | ||||
| 		if fieldType.ConvertibleTo(core.TimeType) { | ||||
| 			t := fieldValue.Convert(core.TimeType).Interface().(time.Time) | ||||
| 			if session.Engine.dialect.DBType() == core.MSSQL { | ||||
| 				if t.IsZero() { | ||||
| 					return nil, nil | ||||
| 				} | ||||
| 			} | ||||
| 			tf := session.Engine.FormatTime(col.SQLType.Name, t) | ||||
| 			return tf, nil | ||||
| 		} | ||||
|  | ||||
| 		if !col.SQLType.IsJson() { | ||||
| 			// !<winxxp>! 增加支持driver.Valuer接口的结构,如sql.NullString | ||||
| 			if v, ok := fieldValue.Interface().(driver.Valuer); ok { | ||||
| 				return v.Value() | ||||
| 			} | ||||
|  | ||||
| 			fieldTable, err := session.Engine.autoMapType(fieldValue) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			if len(fieldTable.PrimaryKeys) == 1 { | ||||
| 				pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) | ||||
| 				return pkField.Interface(), nil | ||||
| 			} | ||||
| 			return 0, fmt.Errorf("no primary key for col %v", col.Name) | ||||
| 		} | ||||
|  | ||||
| 		if col.SQLType.IsText() { | ||||
| 			bytes, err := json.Marshal(fieldValue.Interface()) | ||||
| 			if err != nil { | ||||
| 				session.Engine.logger.Error(err) | ||||
| 				return 0, err | ||||
| 			} | ||||
| 			return string(bytes), nil | ||||
| 		} else if col.SQLType.IsBlob() { | ||||
| 			bytes, err := json.Marshal(fieldValue.Interface()) | ||||
| 			if err != nil { | ||||
| 				session.Engine.logger.Error(err) | ||||
| 				return 0, err | ||||
| 			} | ||||
| 			return bytes, nil | ||||
| 		} | ||||
| 		return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type()) | ||||
| 	case reflect.Complex64, reflect.Complex128: | ||||
| 		bytes, err := json.Marshal(fieldValue.Interface()) | ||||
| 		if err != nil { | ||||
| 			session.Engine.logger.Error(err) | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		return string(bytes), nil | ||||
| 	case reflect.Array, reflect.Slice, reflect.Map: | ||||
| 		if !fieldValue.IsValid() { | ||||
| 			return fieldValue.Interface(), nil | ||||
| 		} | ||||
|  | ||||
| 		if col.SQLType.IsText() { | ||||
| 			bytes, err := json.Marshal(fieldValue.Interface()) | ||||
| 			if err != nil { | ||||
| 				session.Engine.logger.Error(err) | ||||
| 				return 0, err | ||||
| 			} | ||||
| 			return string(bytes), nil | ||||
| 		} else if col.SQLType.IsBlob() { | ||||
| 			var bytes []byte | ||||
| 			var err error | ||||
| 			if (k == reflect.Array || k == reflect.Slice) && | ||||
| 				(fieldValue.Type().Elem().Kind() == reflect.Uint8) { | ||||
| 				bytes = fieldValue.Bytes() | ||||
| 			} else { | ||||
| 				bytes, err = json.Marshal(fieldValue.Interface()) | ||||
| 				if err != nil { | ||||
| 					session.Engine.logger.Error(err) | ||||
| 					return 0, err | ||||
| 				} | ||||
| 			} | ||||
| 			return bytes, nil | ||||
| 		} | ||||
| 		return nil, ErrUnSupportedType | ||||
| 	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: | ||||
| 		return int64(fieldValue.Uint()), nil | ||||
| 	default: | ||||
| 		return fieldValue.Interface(), nil | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user