2
votes

I have created a custom Set Data type in go, which i am using to define one to many relationships. For example in my Schema, i have the following struct definition

type Doctor struct {
  firstName string
  lastName string
  capabilities commons.Set
 }

Here capabilities is a set of strings which have the following values chat, audio, video, with this setup i am trying to persist the above struct into MySQL using the GORM library, but when i do this i get the following error

panic: invalid sql type Set (interface) for mysql

goroutine 6 [running]:
catalog/vendor/github.com/jinzhu/gorm.(*mysql).DataTypeOf(0xc00027e8a0, 0xc00024d680, 0x8, 0x8)
    /home/kumard/go/src/catalog/vendor/github.com/jinzhu/gorm/dialect_mysql.go:123 +0xce9
catalog/vendor/github.com/jinzhu/gorm.(*Scope).createTable(0xc000169400, 0xc14e60)

I know that i have to implement certain methods in order to achieve this, but i am not able to figure out which method/callback to implement here.

ThreadUnsafeSet Definition:

type threadUnsafeSet map[interface{}]struct{}    

type OrderedPair struct {
    First interface{}
    Second interface{}
}    

func newThreadUnsafeSet() threadUnsafeSet {
    return make(threadUnsafeSet)
}    

func (pair *OrderedPair) Equal(other OrderedPair) bool {
    return pair.First == other.First && pair.Second == other.Second
}    

func (set *threadUnsafeSet) Add(i interface{}) bool {
    _, found := (*set)[i]
    if found {
        return false
    }
    (*set)[i] = struct{}{}
    return true
}    

func (set *threadUnsafeSet) Contains(i ...interface{}) bool {
    for _, val := range i {
        if _, ok := (*set)[val]; !ok {
            return false
        }
    }
    return true
}    

func (set *threadUnsafeSet) Cardinality() int {
    return len(*set)
}    

func (set *threadUnsafeSet) Equal(other Set) bool {
    _ = other.(*threadUnsafeSet)
    if set.Cardinality() != other.Cardinality() {
        return false
    }
    for elem := range *set {
        if !other.Contains(elem){
            return false
        }
    }
    return true
}    

func (set *threadUnsafeSet) IsSubSet(other Set) bool {
    _ = other.(*threadUnsafeSet)
    if set.Cardinality() > other.Cardinality() {
        return false
    }
    for elem := range *set {
        if !other.Contains(elem) {
            return false
        }
    }
    return true
}    

func (set *threadUnsafeSet) IsProperSubSet(other Set) bool {
    return set.IsSubSet(other) && !set.Equal(other)
}    

func (set *threadUnsafeSet) IsSuperSet(other Set) bool {
    return other.IsSubSet(set)
}    

func (set *threadUnsafeSet) IsProperSuperSet(other Set) bool {
    return set.IsSuperSet(other) && !set.Equal(other)
}    

func (set *threadUnsafeSet) Union(other Set) Set {
    o := other.(*threadUnsafeSet)
    result := newThreadUnsafeSet()
    for elem := range *set {
        result.Add(elem)
    }
    for elem := range *o {
        result.Add(elem)
    }
    return &result
}    


func (set *threadUnsafeSet) Intersect(other Set) Set {
    o := other.(*threadUnsafeSet)    

    intersection := newThreadUnsafeSet()    

    if set.Cardinality() < other.Cardinality() {
        for elem := range *set {
            if other.Contains(elem) {
                intersection.Add(elem)
            }
        }
    } else {
        for elem := range *o {
            if set.Contains(elem) {
                intersection.Add(elem)
            }
        }
    }
    return &intersection
}    

func (set *threadUnsafeSet) Difference(other Set) Set {
    _ = other.(*threadUnsafeSet)
    difference := newThreadUnsafeSet()
    for elem := range *set {
        if !other.Contains(elem) {
            difference.Add(elem)
        }
    }
    return &difference
}    

func (set *threadUnsafeSet) SymmetricDifference(other Set) Set {
    _ = other.(*threadUnsafeSet)
    aDiff := set.Difference(other)
    bDiff := other.Difference(set)
    return aDiff.Difference(bDiff)
}    

func (set *threadUnsafeSet) Clear(){
    *set = newThreadUnsafeSet()
}    

func (set *threadUnsafeSet) Remove(i interface{}) {
    delete(*set, i)
}    

func (set *threadUnsafeSet) Each(cb func(interface{}) bool) {
    for elem := range *set {
        if cb(elem) {
            break
        }
    }
}    

func (set *threadUnsafeSet) Iter() <-chan interface{} {
    ch := make(chan interface{})
    go func() {
        for elem := range *set {
            ch <- elem
        }
        close(ch)
    }()
    return ch
}    

func (set *threadUnsafeSet) Iterator() *commons.Iterator {
    iterator, ch, stopCh := commons.NewIterator()
    go func (){
        L:
            for elem := range *set {
                select {
                    case <-stopCh: {
                        break L
                    }
                    case ch <- elem:
                }
                close(ch)
            }
    }()
    return iterator
}    

func (set *threadUnsafeSet) Clone() Set {
    clonedSet := newThreadUnsafeSet()
    for elem := range *set {
        clonedSet.Add(elem)
    }
    return &clonedSet
}    

func (set *threadUnsafeSet) String() string {
    items := make([]string, 0, len(*set))    

    for elem := range *set {
        items = append(items, fmt.Sprintf("%v", elem))
    }
    return fmt.Sprintf("Set{%s}", strings.Join(items, ","))
}    

func (pair OrderedPair) String() string {
    return fmt.Sprintf("(%v, %v)", pair.First, pair.Second)
}    

func (set *threadUnsafeSet) Pop() interface{} {
    for item := range *set {
        delete (*set, item)
        return item
    }
    return nil
}    

func (set *threadUnsafeSet) PowerSet() Set {
    powSet := NewThreadUnsafeSet()
    nullSet := newThreadUnsafeSet()
    powSet.Add(&nullSet)    

    for _, v := range i {
        switch t := v.(type) {
        case []interface{}, map[string]interface{}:
            continue
        default:
            set.Add(t)
        }
    }
    return nil
}    

func (set *threadUnsafeSet) FindAny() fi.Optional {
    for elem := range *set {
        return fi.MakeNullable(elem)
    }
    return fi.MakeNullable(nil)
}    

func (set *threadUnsafeSet) FindFirst(predicate func(interface{}) bool) fi.Optional {
    for elem := range *set {
        if predicate(elem) {
            return fi.MakeNullable(elem)
        }
    }
    return fi.MakeNullable(nil)
}    

func (set *threadUnsafeSet) RemoveAll(elementsToRemove ... interface{}) {
    for _, elem := range elementsToRemove {
        if set.Contains(elem) {
            set.Remove(elem)
        }
    }
}
    for es := range *set {
        u := newThreadUnsafeSet()
        j := powSet.Iter()
        for err := range j {
            p := newThreadUnsafeSet()
            if reflect.TypeOf(err).Name() == "" {
                k := err.(*threadUnsafeSet)
                for ek := range *(k){
                    p.Add(ek)
                }
            }else {
                p.Add(err)
            }
            p.Add(es)
            u.Add(&p)
        }
        powSet = powSet.Union(&u)
    }
    return powSet
}    

func (set *threadUnsafeSet) CartesianProduct(other Set) Set {
    o := other.(*threadUnsafeSet)
    cartProduct := NewThreadUnsafeSet()
    for i := range *set {
        for j := range *o {
            elem := OrderedPair{First: i, Second: j}
            cartProduct.Add(elem)
        }
    }
    return cartProduct
}    

func (set *threadUnsafeSet) ToSlice() []interface{}{
    keys := make([]interface{}, 0, set.Cardinality())
    for elem := range *set {
        keys = append(keys, elem)
    }
    return keys
}    

func (set *threadUnsafeSet) MarshalJSON() ([]byte, error) {
    items := make([]string, 0, set.Cardinality())    

    for elem := range *set {
        b, err := json.Marshal(elem)
        if err != nil {
            return nil, err
        }
        items = append(items, string(b))
    }
    return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil
}    

func (set *threadUnsafeSet) UnMarshalJSON(b []byte) error {
    var i []interface{}    

    d := json.NewDecoder(bytes.NewReader(b))
    d.UseNumber()
    err := d.Decode(&i)
    if err != nil {
        return err
    }

Set Interface definition

type Set interface {
    Add(i interface{}) bool    

    Cardinality() int    

    Clear()    

    Clone() Set    

    Contains(i ...interface{}) bool    

    Difference(other Set) Set    

    Equal(other Set) bool    

    Intersect(other Set) Set    

    IsProperSubSet(other Set) bool    

    IsSubSet(other Set) bool    

    IsSuperSet(other Set) bool    

    Each(func(interface{}) bool)    

    Iter() <-chan interface{}    

    Iterator() *commons.Iterator    

    Remove(i interface{})    

    String() string    

    SymmetricDifference(other Set) Set    

    Union(other Set) Set    

    Pop() interface{}    

    PowerSet() Set    

    CartesianProduct(other Set) Set    

    ToSlice() []interface{}    

    FindAny() fi.Optional    

    FindFirst(predicate func(interface{}) bool) fi.Optional    

    RemoveAll(...interface{})
}    

// NewSet creates and returns a reference to an empty set.  Operations
// on the resulting set are thread-safe.
func NewSet(s ...interface{}) Set {
    set := newThreadSafeSet()
    for _, item := range s {
        set.Add(item)
    }
    return &set
}    

// NewSetWith creates and returns a new set with the given elements.
// Operations on the resulting set are thread-safe.
func NewSetWith(elts ...interface{}) Set {
    return NewSetFromSlice(elts)
}    

// NewSetFromSlice creates and returns a reference to a set from an
// existing slice.  Operations on the resulting set are thread-safe.
func NewSetFromSlice(s []interface{}) Set {
    a := NewSet(s...)
    return a
}    

// NewThreadUnsafeSet creates and returns a reference to an empty set.
// Operations on the resulting set are not thread-safe.
func NewThreadUnsafeSet() Set {
    set := newThreadUnsafeSet()
    return &set
}    

// NewThreadUnsafeSetFromSlice creates and returns a reference to a
// set from an existing slice.  Operations on the resulting set are
// not thread-safe.
func NewThreadUnsafeSetFromSlice(s []interface{}) Set {
    a := NewThreadUnsafeSet()
    for _, item := range s {
        a.Add(item)
    }
    return a
}
1
@AbinashGhosh threadUnsafeSet is an implmentation of the Set interfacekumarD

1 Answers

4
votes

You need to implement Scanner & Driver Valuer interface for the custom type then database driver could know how to store the data in the database and how to get the data from the database.

func (data *CustomType) Value() (driver.Value, error) {
    ...
}
func (data *CustomType) Scan(value interface{}) error {
    ...
}

Example: Suppose UserAccess is map[interface{}]struct{} type.

type UserAccess map[interface{}]struct{}

func (data *UserAccess) Value() (driver.Value, error) {
    return data.ConvertJSONToString(), nil
}
func (data *UserAccess) Scan(value interface{}) error {
    *data = data.ConvertStringToJson(valueString)
}

Here ConvertStringToJson and ConvertJSONToString used to convert custom datatype value into database compatible type like json-string.