diff --git a/orm/db.go b/orm/db.go index 712ce654..9371cd8a 100644 --- a/orm/db.go +++ b/orm/db.go @@ -406,7 +406,7 @@ func (d *dbTables) getLimitSql(offset int64, limit int) (limits string) { if limit < 0 { // no limit if offset > 0 { - limits = fmt.Sprintf("OFFSET %d", offset) + limits = fmt.Sprintf("LIMIT 18446744073709551615 OFFSET %d", offset) } } else if offset <= 0 { limits = fmt.Sprintf("LIMIT %d", limit) @@ -732,7 +732,7 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con refs := make([]interface{}, colsNum) for i, _ := range refs { - var ref string + var ref interface{} refs[i] = &ref } @@ -842,7 +842,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi refs := make([]interface{}, colsNum) for i, _ := range refs { - var ref string + var ref interface{} refs[i] = &ref } @@ -882,18 +882,20 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi lastm := mmi mmi := fi.relModelInfo field := reflect.Indirect(last.Field(fi.fieldIndex)) - d.setColsValues(mmi, &field, mmi.fields.dbcols, trefs[:len(mmi.fields.dbcols)]) - for _, fi := range mmi.fields.fieldsReverse { - if fi.reverseFieldInfo.mi == lastm { - if fi.reverseFieldInfo != nil { - field.Field(fi.fieldIndex).Set(last.Addr()) + if field.IsValid() { + d.setColsValues(mmi, &field, mmi.fields.dbcols, trefs[:len(mmi.fields.dbcols)]) + for _, fi := range mmi.fields.fieldsReverse { + if fi.reverseFieldInfo.mi == lastm { + if fi.reverseFieldInfo != nil { + field.Field(fi.fieldIndex).Set(last.Addr()) + } } } + cacheV[names] = &field + cacheM[names] = mmi + last = field } trefs = trefs[len(mmi.fields.dbcols):] - cacheV[names] = &field - cacheM[names] = mmi - last = field } } } @@ -961,10 +963,15 @@ func (d *dbBase) GetOperatorSql(mi *modelInfo, operator string, args []interface sql = operatorsSQL[operator] arg := params[0] switch operator { + case "exact": + if arg == nil { + params[0] = "IS NULL" + } case "iexact", "contains", "icontains", "startswith", "endswith", "istartswith", "iendswith": param := strings.Replace(ToStr(arg), `%`, `\%`, -1) switch operator { - case "iexact", "contains", "icontains": + case "iexact": + case "contains", "icontains": param = fmt.Sprintf("%%%s%%", param) case "startswith", "istartswith": param = fmt.Sprintf("%s%%", param) @@ -1143,205 +1150,61 @@ setValue: switch { case fieldType == TypeBooleanField: if isNative { + if value == nil { + value = false + } field.SetBool(value.(bool)) } case fieldType == TypeCharField || fieldType == TypeTextField: if isNative { + if value == nil { + value = "" + } field.SetString(value.(string)) } case fieldType == TypeDateField || fieldType == TypeDateTimeField: if isNative { + if value == nil { + value = time.Time{} + } field.Set(reflect.ValueOf(value)) } case fieldType&IsIntegerField > 0: if fieldType&IsPostiveIntegerField > 0 { if isNative { + if value == nil { + value = uint64(0) + } field.SetUint(value.(uint64)) } } else { if isNative { + if value == nil { + value = int64(0) + } field.SetInt(value.(int64)) } } case fieldType == TypeFloatField || fieldType == TypeDecimalField: if isNative { + if value == nil { + value = float64(0) + } field.SetFloat(value.(float64)) } case fieldType&IsRelField > 0: - fieldType = fi.relModelInfo.fields.pk[0].fieldType - mf := reflect.New(fi.relModelInfo.addrField.Elem().Type()) - md := mf.Interface().(Modeler) - md.Init(md) - field.Set(mf) - f := mf.Elem().Field(fi.relModelInfo.fields.pk[0].fieldIndex) - field = &f - goto setValue - } - - if isNative == false { - fd := field.Addr().Interface().(Fielder) - err := fd.SetRaw(value) - if err != nil { - return nil, err + if value != nil { + fieldType = fi.relModelInfo.fields.pk[0].fieldType + mf := reflect.New(fi.relModelInfo.addrField.Elem().Type()) + md := mf.Interface().(Modeler) + md.Init(md) + field.Set(mf) + f := mf.Elem().Field(fi.relModelInfo.fields.pk[0].fieldIndex) + field = &f + goto setValue } } - return value, nil -} - -func (d *dbBase) xsetValue(fi *fieldInfo, val interface{}, field *reflect.Value) (interface{}, error) { - if val == nil { - return nil, nil - } - - var value interface{} - - var str *StrTo - switch v := val.(type) { - case []byte: - s := StrTo(string(v)) - str = &s - case string: - s := StrTo(v) - str = &s - } - - fieldType := fi.fieldType - isNative := fi.isFielder == false - -setValue: - switch { - case fieldType == TypeBooleanField: - if str == nil { - switch v := val.(type) { - case int64: - b := v == 1 - if isNative { - field.SetBool(b) - } - value = b - default: - s := StrTo(ToStr(v)) - str = &s - } - } - if str != nil { - b, err := str.Bool() - if err != nil { - return nil, err - } - if isNative { - field.SetBool(b) - } - value = b - } - case fieldType == TypeCharField || fieldType == TypeTextField: - s := str.String() - if str == nil { - s = ToStr(val) - } - if isNative { - field.SetString(s) - } - value = s - case fieldType == TypeDateField || fieldType == TypeDateTimeField: - if str == nil { - switch v := val.(type) { - case time.Time: - if isNative { - field.Set(reflect.ValueOf(v)) - } - value = v - default: - s := StrTo(ToStr(v)) - str = &s - } - } - if str != nil { - format := format_DateTime - if fi.fieldType == TypeDateField { - format = format_Date - } - - t, err := timeParse(str.String(), format) - if err != nil { - return nil, err - } - if isNative { - field.Set(reflect.ValueOf(t)) - } - value = t - } - case fieldType&IsIntegerField > 0: - if str == nil { - s := StrTo(ToStr(val)) - str = &s - } - if str != nil { - var err error - switch fieldType { - case TypeSmallIntegerField: - value, err = str.Int16() - case TypeIntegerField: - value, err = str.Int32() - case TypeBigIntegerField: - value, err = str.Int64() - case TypePositiveSmallIntegerField: - value, err = str.Uint16() - case TypePositiveIntegerField: - value, err = str.Uint32() - case TypePositiveBigIntegerField: - value, err = str.Uint64() - } - if err != nil { - return nil, err - } - if fieldType&IsPostiveIntegerField > 0 { - v, _ := str.Uint64() - if isNative { - field.SetUint(v) - } - } else { - v, _ := str.Int64() - if isNative { - field.SetInt(v) - } - } - } - case fieldType == TypeFloatField || fieldType == TypeDecimalField: - if str == nil { - switch v := val.(type) { - case float64: - if isNative { - field.SetFloat(v) - } - value = v - default: - s := StrTo(ToStr(v)) - str = &s - } - } - if str != nil { - v, err := str.Float64() - if err != nil { - return nil, err - } - if isNative { - field.SetFloat(v) - } - value = v - } - case fieldType&IsRelField > 0: - fieldType = fi.relModelInfo.fields.pk[0].fieldType - mf := reflect.New(fi.relModelInfo.addrField.Elem().Type()) - md := mf.Interface().(Modeler) - md.Init(md) - field.Set(mf) - f := mf.Elem().Field(fi.relModelInfo.fields.pk[0].fieldIndex) - field = &f - goto setValue - } - if isNative == false { fd := field.Addr().Interface().(Fielder) err := fd.SetRaw(value) @@ -1420,7 +1283,7 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond refs := make([]interface{}, len(cols)) for i, _ := range refs { - var ref string + var ref interface{} refs[i] = &ref } diff --git a/orm/db_alias.go b/orm/db_alias.go index eb3103e8..7cd29f03 100644 --- a/orm/db_alias.go +++ b/orm/db_alias.go @@ -113,15 +113,18 @@ end: } func RegisterDriver(name string, typ driverType) { - if _, ok := drivers[name]; ok == false { + if t, ok := drivers[name]; ok == false { drivers[name] = typ } else { - fmt.Println("name `%s` db driver already registered") - os.Exit(2) + if t != typ { + fmt.Println("name `%s` db driver already registered and is other type") + os.Exit(2) + } } } func init() { - // RegisterDriver("mysql", DR_MySQL) - RegisterDriver("mymysql", DR_MySQL) + RegisterDriver("mysql", DR_MySQL) + RegisterDriver("postgres", DR_Postgres) + RegisterDriver("sqlite3", DR_Sqlite) } diff --git a/orm/models.go b/orm/models.go index 53fcb150..8770b1ec 100644 --- a/orm/models.go +++ b/orm/models.go @@ -26,7 +26,7 @@ var ( "auto": 1, "auto_now": 1, "auto_now_add": 1, - "max_length": 2, + "size": 2, "choices": 2, "column": 2, "default": 2, @@ -37,6 +37,7 @@ var ( "digits": 2, "decimals": 2, "on_delete": 2, + "type": 2, } ) diff --git a/orm/models_fields.go b/orm/models_fields.go index 432505f2..0f09c1e9 100644 --- a/orm/models_fields.go +++ b/orm/models_fields.go @@ -93,10 +93,16 @@ func (e *BooleanField) RawValue() interface{} { return e.Value() } +func (e *BooleanField) Clean() error { + return nil +} + +var _ Fielder = new(BooleanField) + // A string field -// required values tag: max_length -// The max_length is enforced at the database level and in models’s validation. -// eg: `max_length:"120"` +// required values tag: size +// The size is enforced at the database level and in models’s validation. +// eg: `orm:"size(120)"` type CharField string func (e CharField) Value() string { @@ -129,6 +135,12 @@ func (e *CharField) RawValue() interface{} { return e.Value() } +func (e *CharField) Clean() error { + return nil +} + +var _ Fielder = new(CharField) + // A date, represented in go by a time.Time instance. // only date values like 2006-01-02 // Has a few extra, optional attr tag: @@ -141,7 +153,7 @@ func (e *CharField) RawValue() interface{} { // Automatically set the field to now when the object is first created. Useful for creation of timestamps. // Note that the current date is always used; it’s not just a default value that you can override. // -// eg: `attr:"auto_now"` or `attr:"auto_now_add"` +// eg: `orm:"auto_now"` or `orm:"auto_now_add"` type DateField time.Time func (e DateField) Value() time.Time { @@ -180,6 +192,12 @@ func (e *DateField) RawValue() interface{} { return e.Value() } +func (e *DateField) Clean() error { + return nil +} + +var _ Fielder = new(DateField) + // A date, represented in go by a time.Time instance. // datetime values like 2006-01-02 15:04:05 // Takes the same extra arguments as DateField. @@ -221,6 +239,12 @@ func (e *DateTimeField) RawValue() interface{} { return e.Value() } +func (e *DateTimeField) Clean() error { + return nil +} + +var _ Fielder = new(DateTimeField) + // A floating-point number represented in go by a float32 value. type FloatField float64 @@ -261,6 +285,12 @@ func (e *FloatField) RawValue() interface{} { return e.Value() } +func (e *FloatField) Clean() error { + return nil +} + +var _ Fielder = new(FloatField) + // -32768 to 32767 type SmallIntegerField int16 @@ -299,6 +329,12 @@ func (e *SmallIntegerField) RawValue() interface{} { return e.Value() } +func (e *SmallIntegerField) Clean() error { + return nil +} + +var _ Fielder = new(SmallIntegerField) + // -2147483648 to 2147483647 type IntegerField int32 @@ -337,6 +373,12 @@ func (e *IntegerField) RawValue() interface{} { return e.Value() } +func (e *IntegerField) Clean() error { + return nil +} + +var _ Fielder = new(IntegerField) + // -9223372036854775808 to 9223372036854775807. type BigIntegerField int64 @@ -375,6 +417,12 @@ func (e *BigIntegerField) RawValue() interface{} { return e.Value() } +func (e *BigIntegerField) Clean() error { + return nil +} + +var _ Fielder = new(BigIntegerField) + // 0 to 65535 type PositiveSmallIntegerField uint16 @@ -413,6 +461,12 @@ func (e *PositiveSmallIntegerField) RawValue() interface{} { return e.Value() } +func (e *PositiveSmallIntegerField) Clean() error { + return nil +} + +var _ Fielder = new(PositiveSmallIntegerField) + // 0 to 4294967295 type PositiveIntegerField uint32 @@ -451,6 +505,12 @@ func (e *PositiveIntegerField) RawValue() interface{} { return e.Value() } +func (e *PositiveIntegerField) Clean() error { + return nil +} + +var _ Fielder = new(PositiveIntegerField) + // 0 to 18446744073709551615 type PositiveBigIntegerField uint64 @@ -489,6 +549,12 @@ func (e *PositiveBigIntegerField) RawValue() interface{} { return e.Value() } +func (e *PositiveBigIntegerField) Clean() error { + return nil +} + +var _ Fielder = new(PositiveBigIntegerField) + // A large text field. type TextField string @@ -521,3 +587,9 @@ func (e *TextField) SetRaw(value interface{}) error { func (e *TextField) RawValue() interface{} { return e.Value() } + +func (e *TextField) Clean() error { + return nil +} + +var _ Fielder = new(TextField) diff --git a/orm/models_info_f.go b/orm/models_info_f.go index 429eef2e..9dee8a78 100644 --- a/orm/models_info_f.go +++ b/orm/models_info_f.go @@ -145,7 +145,7 @@ type fieldInfo struct { unique bool initial StrTo choices fieldChoices - maxLength int + size int auto_now bool auto_now_add bool rel bool @@ -188,7 +188,7 @@ func newFieldInfo(mi *modelInfo, field reflect.Value, sf reflect.StructField) (f digits := tags["digits"] decimals := tags["decimals"] - maxLength := tags["max_length"] + size := tags["size"] onDelete := tags["on_delete"] checkType: @@ -248,13 +248,13 @@ checkType: if err != nil { goto end } - if fieldType == TypeTextField && maxLength != "" { + if fieldType == TypeTextField && size != "" { fieldType = TypeCharField } if fieldType == TypeFloatField && (digits != "" || decimals != "") { fieldType = TypeDecimalField } - if fieldType == TypeDateTimeField && attrs["date"] { + if fieldType == TypeDateTimeField && tags["type"] == "date" { fieldType = TypeDateField } } @@ -354,15 +354,15 @@ checkType: switch fieldType { case TypeBooleanField: case TypeCharField: - if maxLength != "" { - v, e := StrTo(maxLength).Int32() + if size != "" { + v, e := StrTo(size).Int32() if e != nil { - err = fmt.Errorf("wrong maxLength value `%s`", maxLength) + err = fmt.Errorf("wrong size value `%s`", size) } else { - fi.maxLength = int(v) + fi.size = int(v) } } else { - err = fmt.Errorf("maxLength must be specify") + err = fmt.Errorf("size must be specify") } case TypeTextField: fi.index = false @@ -417,7 +417,6 @@ checkType: } if fi.unique { - fi.null = false fi.blank = false fi.index = false } @@ -484,7 +483,6 @@ checkType: fi.choices = choices fi.initial = initial - end: if err != nil { return nil, err diff --git a/orm/models_manager.go b/orm/models_manager.go index 46659811..b4d05219 100644 --- a/orm/models_manager.go +++ b/orm/models_manager.go @@ -2,15 +2,43 @@ package orm import () -// non cleaned field errors -type FieldErrors map[string]error - -func (fe FieldErrors) Get(name string) error { - return fe[name] +type fieldError struct { + name string + err error } -func (fe FieldErrors) Set(name string, value error) { - fe[name] = value +func (f *fieldError) Name() string { + return f.name +} + +func (f *fieldError) Error() error { + return f.err +} + +func NewFieldError(name string, err error) IFieldError { + return &fieldError{name, err} +} + +// non cleaned field errors +type fieldErrors struct { + errors map[string]IFieldError + errorList []IFieldError +} + +func (fe *fieldErrors) Get(name string) IFieldError { + return fe.errors[name] +} + +func (fe *fieldErrors) Set(name string, value IFieldError) { + fe.errors[name] = value +} + +func (fe *fieldErrors) List() []IFieldError { + return fe.errorList +} + +func NewFieldErrors() IFieldErrors { + return &fieldErrors{errors: make(map[string]IFieldError)} } type Manager struct { @@ -43,11 +71,11 @@ func (m *Manager) IsInited() bool { return m.inited } -func (m *Manager) Clean() FieldErrors { +func (m *Manager) Clean() IFieldErrors { return nil } -func (m *Manager) CleanFields(name string) FieldErrors { +func (m *Manager) CleanFields(name string) IFieldErrors { return nil } diff --git a/orm/orm_object.go b/orm/orm_object.go index 1bcb5595..341a86e2 100644 --- a/orm/orm_object.go +++ b/orm/orm_object.go @@ -75,11 +75,20 @@ func (o *object) Update() (int64, error) { if err != nil { return num, err } - return 0, nil + return num, nil } func (o *object) Delete() (int64, error) { - return o.orm.alias.DbBaser.Delete(o.orm.db, o.mi, o.ind) + num, err := o.orm.alias.DbBaser.Delete(o.orm.db, o.mi, o.ind) + if err != nil { + return num, err + } + if num > 0 { + if o.mi.fields.auto != nil { + o.ind.Field(o.mi.fields.auto.fieldIndex).SetInt(0) + } + } + return num, nil } func newObject(orm *orm, mi *modelInfo, md Modeler) ObjectSeter { diff --git a/orm/orm_queryset.go b/orm/orm_queryset.go index b9fd324a..2d78c312 100644 --- a/orm/orm_queryset.go +++ b/orm/orm_queryset.go @@ -16,40 +16,46 @@ type querySet struct { } func (o *querySet) Filter(expr string, args ...interface{}) QuerySeter { + o = o.clone() if o.cond == nil { o.cond = NewCondition() } o.cond.And(expr, args...) - return o.Clone() + return o } func (o *querySet) Exclude(expr string, args ...interface{}) QuerySeter { + o = o.clone() if o.cond == nil { o.cond = NewCondition() } o.cond.AndNot(expr, args...) - return o.Clone() + return o } func (o *querySet) Limit(limit int, args ...int64) QuerySeter { + o = o.clone() o.limit = limit if len(args) > 0 { o.offset = args[0] } - return o.Clone() + return o } func (o *querySet) Offset(offset int64) QuerySeter { + o = o.clone() o.offset = offset - return o.Clone() + return o } -func (o *querySet) OrderBy(orders ...string) QuerySeter { - o.orders = orders - return o.Clone() +func (o *querySet) OrderBy(exprs ...string) QuerySeter { + o = o.clone() + o.orders = exprs + return o } func (o *querySet) RelatedSel(params ...interface{}) QuerySeter { + o = o.clone() var related []string if len(params) == 0 { o.relDepth = DefaultRelsDepth @@ -66,19 +72,19 @@ func (o *querySet) RelatedSel(params ...interface{}) QuerySeter { } } o.related = related - return o.Clone() + return o } -func (o querySet) Clone() QuerySeter { +func (o querySet) clone() *querySet { if o.cond != nil { o.cond = o.cond.Clone() } return &o } -func (o *querySet) SetCond(cond *Condition) error { +func (o querySet) SetCond(cond *Condition) QuerySeter { o.cond = cond - return nil + return &o } func (o *querySet) Count() (int64, error) { @@ -112,16 +118,16 @@ func (o *querySet) One(container Modeler) error { return nil } -func (o *querySet) Values(results *[]Params, args ...string) (int64, error) { - return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, args, results) +func (o *querySet) Values(results *[]Params, exprs ...string) (int64, error) { + return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, exprs, results) } -func (o *querySet) ValuesList(results *[]ParamsList, args ...string) (int64, error) { - return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, args, results) +func (o *querySet) ValuesList(results *[]ParamsList, exprs ...string) (int64, error) { + return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, exprs, results) } -func (o *querySet) ValuesFlat(result *ParamsList, arg string) (int64, error) { - return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, []string{arg}, result) +func (o *querySet) ValuesFlat(result *ParamsList, expr string) (int64, error) { + return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, []string{expr}, result) } func newQuerySet(orm *orm, mi *modelInfo) QuerySeter { diff --git a/orm/types.go b/orm/types.go index 50217246..c819da2f 100644 --- a/orm/types.go +++ b/orm/types.go @@ -10,13 +10,14 @@ type Fielder interface { FieldType() int SetRaw(interface{}) error RawValue() interface{} + Clean() error } type Modeler interface { Init(Modeler) Modeler IsInited() bool - Clean() FieldErrors - CleanFields(string) FieldErrors + Clean() IFieldErrors + CleanFields(string) IFieldErrors GetTableName() string } @@ -48,8 +49,7 @@ type QuerySeter interface { Offset(int64) QuerySeter OrderBy(...string) QuerySeter RelatedSel(...interface{}) QuerySeter - Clone() QuerySeter - SetCond(*Condition) error + SetCond(*Condition) QuerySeter Count() (int64, error) Update(Params) (int64, error) Delete() (int64, error) @@ -75,6 +75,17 @@ type RawSeter interface { Prepare() (RawPreparer, error) } +type IFieldError interface { + Name() string + Error() error +} + +type IFieldErrors interface { + Get(string) IFieldError + Set(string, IFieldError) + List() []IFieldError +} + type dbQuerier interface { Prepare(query string) (*sql.Stmt, error) Exec(query string, args ...interface{}) (sql.Result, error)