dbmeta/delete.go

148 lines
4.1 KiB
Go

package dbmeta
import (
"reflect"
"git.bit5.ru/backend/db"
"git.bit5.ru/backend/errors"
"git.bit5.ru/backend/meta"
)
func DeleteMetaStruct(db *db.DBC, metaStruct interface{}, ownerId uint32) error {
metaStructValue, metaStructType, err := getValueAndType(metaStruct)
if err != nil {
return err
}
fieldsProps, err := getMetastructFieldProps(metaStructValue)
if err != nil {
return err
}
// Iterate through struct fields to find field type struct or slice
for i := 0; i < metaStructType.NumField(); i++ {
field := metaStructType.Field(i)
if fieldsProps != nil {
if props, ok := fieldsProps[field.Name]; ok {
if _, ok := props["db_skip_save"]; ok {
continue
}
}
}
fieldType := field.Type
fieldValue := metaStructValue.Field(i)
// skip unexported fields
if len(field.PkgPath) > 0 {
continue
}
switch fieldType.Kind() {
case reflect.Slice:
// Try to get reflect.Value of slice underlying element
sliceElement := getSliceElementValue(fieldValue)
if err := DeleteMetaStruct(db, sliceElement.Addr().Interface(), ownerId); err != nil {
return err
}
case reflect.Struct:
if err := DeleteMetaStruct(db, fieldValue.Addr().Interface(), ownerId); err != nil {
return err
}
break
case reflect.UnsafePointer:
// Now we don't use these types in meta. If we will - fix it.
return errors.Errorf("I don't know what I should do with it: %s", fieldType.Kind())
case reflect.Map, reflect.Ptr, reflect.Array:
default:
break
}
}
if !isMetaDataItem(metaStructValue) {
return nil
}
table, ownerColumn, err := getTableAndOwnerColumn(metaStructValue)
if err != nil {
return err
}
err = deleteFromTable(db, table, ownerColumn, ownerId)
return err
}
func isMetaDataItem(metaStructValue reflect.Value) bool {
if !metaStructValue.CanInterface() {
return false
}
_, ok := metaStructValue.Addr().Interface().(meta.IMetaDataItem)
return ok
}
// Get value and type of MetaStruct. Only pointer is allowed.
func getValueAndType(metaStruct interface{}) (reflect.Value, reflect.Type, error) {
metaStructValue := reflect.ValueOf(metaStruct)
metaStructType := metaStructValue.Type()
if metaStructType.Kind() != reflect.Ptr {
return metaStructValue, metaStructType, errors.New("Value must be a pointer to a struct")
}
metaStructValue = metaStructValue.Elem()
metaStructType = metaStructType.Elem()
return metaStructValue, metaStructType, nil
}
// Get fieldsProps from metastruct
func getMetastructFieldProps(metaStructValue reflect.Value) (map[string]map[string]string, error) {
fieldsProps := make(map[string]map[string]string)
// Check if value is addressable, because meta.IClassProps' method CLASS_FIELDS_PROPS() has a pointer reciever
if !metaStructValue.CanAddr() {
return fieldsProps, errors.New("value is not addressable!")
}
if !metaStructValue.CanInterface() {
return fieldsProps, errors.New("Interface cannot used!")
}
iMetaClassFieldProps, ok := metaStructValue.Addr().Interface().(meta.IClassProps)
if !ok {
return fieldsProps, errors.New("value is not meta.IClassProps interface!")
}
fieldsProps = *iMetaClassFieldProps.CLASS_FIELDS_PROPS()
return fieldsProps, nil
}
// Returns slice element value to get table data
func getSliceElementValue(slice reflect.Value) reflect.Value {
// Slice Kind here is a pointer.
// First call of Elem() returns type from pointer(?).
// Second returns type of slice element
sliceElementType := slice.Type().Elem().Elem()
// Call Elem() on new value, because type of element type is pointer(?)
sliceElementValue := reflect.New(sliceElementType).Elem()
return sliceElementValue
}
func getTableAndOwnerColumn(metaStructValue reflect.Value) (string, string, error) {
iDataItem, ok := metaStructValue.Addr().Interface().(meta.IMetaDataItem)
if !ok {
return "", "", errors.New("Can't convert to interface meta.IMetaDataItem!")
}
return iDataItem.GetDbTableName(), iDataItem.GetOwnerFieldName(), nil
}
func deleteFromTable(db *db.DBC, table string, ownerColumnName string, ownerId uint32) error {
_, err := db.DeleteFrom(table).Where(ownerColumnName+" = ?", ownerId).Exec()
return err
}