github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/lib/pq/hstore/hstore.go (about) 1 package hstore 2 3 import ( 4 "database/sql" 5 "database/sql/driver" 6 "strings" 7 ) 8 9 // A wrapper for transferring Hstore values back and forth easily. 10 type Hstore struct { 11 Map map[string]sql.NullString 12 } 13 14 // escapes and quotes hstore keys/values 15 // s should be a sql.NullString or string 16 func hQuote(s interface{}) string { 17 var str string 18 switch v := s.(type) { 19 case sql.NullString: 20 if !v.Valid { 21 return "NULL" 22 } 23 str = v.String 24 case string: 25 str = v 26 default: 27 panic("not a string or sql.NullString") 28 } 29 30 str = strings.Replace(str, "\\", "\\\\", -1) 31 return `"` + strings.Replace(str, "\"", "\\\"", -1) + `"` 32 } 33 34 // Scan implements the Scanner interface. 35 // 36 // Note h.Map is reallocated before the scan to clear existing values. If the 37 // hstore column's database value is NULL, then h.Map is set to nil instead. 38 func (h *Hstore) Scan(value interface{}) error { 39 if value == nil { 40 h.Map = nil 41 return nil 42 } 43 h.Map = make(map[string]sql.NullString) 44 var b byte 45 pair := [][]byte{{}, {}} 46 pi := 0 47 inQuote := false 48 didQuote := false 49 sawSlash := false 50 bindex := 0 51 for bindex, b = range value.([]byte) { 52 if sawSlash { 53 pair[pi] = append(pair[pi], b) 54 sawSlash = false 55 continue 56 } 57 58 switch b { 59 case '\\': 60 sawSlash = true 61 continue 62 case '"': 63 inQuote = !inQuote 64 if !didQuote { 65 didQuote = true 66 } 67 continue 68 default: 69 if !inQuote { 70 switch b { 71 case ' ', '\t', '\n', '\r': 72 continue 73 case '=': 74 continue 75 case '>': 76 pi = 1 77 didQuote = false 78 continue 79 case ',': 80 s := string(pair[1]) 81 if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { 82 h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} 83 } else { 84 h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} 85 } 86 pair[0] = []byte{} 87 pair[1] = []byte{} 88 pi = 0 89 continue 90 } 91 } 92 } 93 pair[pi] = append(pair[pi], b) 94 } 95 if bindex > 0 { 96 s := string(pair[1]) 97 if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { 98 h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} 99 } else { 100 h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} 101 } 102 } 103 return nil 104 } 105 106 // Value implements the driver Valuer interface. Note if h.Map is nil, the 107 // database column value will be set to NULL. 108 func (h Hstore) Value() (driver.Value, error) { 109 if h.Map == nil { 110 return nil, nil 111 } 112 parts := []string{} 113 for key, val := range h.Map { 114 thispart := hQuote(key) + "=>" + hQuote(val) 115 parts = append(parts, thispart) 116 } 117 return []byte(strings.Join(parts, ",")), nil 118 }