github.com/Ali-iotechsys/sqlboiler/v4@v4.0.0-20221208124957-6aec9a5f1f71/types/hstore.go (about) 1 // Copyright (c) 2011-2013, 'pq' Contributors Portions Copyright (C) 2011 Blake Mizerany. MIT license. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining 4 // a copy of this software and associated documentation files (the "Software"), 5 // to deal in the Software without restriction, including without limitation the 6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software 8 // is furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included 11 // in all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 14 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 15 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 16 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 18 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 20 package types 21 22 import ( 23 "database/sql" 24 "database/sql/driver" 25 "strings" 26 27 "github.com/volatiletech/null/v8" 28 "github.com/volatiletech/randomize" 29 ) 30 31 // HStore is a wrapper for transferring HStore values back and forth easily. 32 type HStore map[string]null.String 33 34 // escapes and quotes hstore keys/values 35 // s should be a sql.NullString or string 36 func hQuote(s interface{}) string { 37 var str string 38 switch v := s.(type) { 39 case null.String: 40 if !v.Valid { 41 return "NULL" 42 } 43 str = v.String 44 case sql.NullString: 45 if !v.Valid { 46 return "NULL" 47 } 48 str = v.String 49 case string: 50 str = v 51 default: 52 panic("not a string or sql.NullString") 53 } 54 55 str = strings.ReplaceAll(str, "\\", "\\\\") 56 return `"` + strings.ReplaceAll(str, "\"", "\\\"") + `"` 57 } 58 59 // Scan implements the Scanner interface. 60 // 61 // Note h is reallocated before the scan to clear existing values. If the 62 // hstore column's database value is NULL, then h is set to nil instead. 63 func (h *HStore) Scan(value interface{}) error { 64 if value == nil { 65 h = nil 66 return nil 67 } 68 *h = make(map[string]null.String) 69 var b byte 70 pair := [][]byte{{}, {}} 71 pi := 0 72 inQuote := false 73 didQuote := false 74 sawSlash := false 75 bindex := 0 76 for bindex, b = range value.([]byte) { 77 if sawSlash { 78 pair[pi] = append(pair[pi], b) 79 sawSlash = false 80 continue 81 } 82 83 switch b { 84 case '\\': 85 sawSlash = true 86 continue 87 case '"': 88 inQuote = !inQuote 89 if !didQuote { 90 didQuote = true 91 } 92 continue 93 default: 94 if !inQuote { 95 switch b { 96 case ' ', '\t', '\n', '\r': 97 continue 98 case '=': 99 continue 100 case '>': 101 pi = 1 102 didQuote = false 103 continue 104 case ',': 105 s := string(pair[1]) 106 if !didQuote && len(s) == 4 && strings.EqualFold(s, "null") { 107 (*h)[string(pair[0])] = null.String{String: "", Valid: false} 108 } else { 109 (*h)[string(pair[0])] = null.String{String: string(pair[1]), Valid: true} 110 } 111 pair[0] = []byte{} 112 pair[1] = []byte{} 113 pi = 0 114 continue 115 } 116 } 117 } 118 pair[pi] = append(pair[pi], b) 119 } 120 if bindex > 0 { 121 s := string(pair[1]) 122 if !didQuote && len(s) == 4 && strings.EqualFold(s, "null") { 123 (*h)[string(pair[0])] = null.String{String: "", Valid: false} 124 } else { 125 (*h)[string(pair[0])] = null.String{String: string(pair[1]), Valid: true} 126 } 127 } 128 return nil 129 } 130 131 // Value implements the driver Valuer interface. Note if h is nil, the 132 // database column value will be set to NULL. 133 func (h HStore) Value() (driver.Value, error) { 134 if h == nil { 135 return nil, nil 136 } 137 parts := []string{} 138 for key, val := range h { 139 thispart := hQuote(key) + "=>" + hQuote(val) 140 parts = append(parts, thispart) 141 } 142 return []byte(strings.Join(parts, ",")), nil 143 } 144 145 // Randomize for sqlboiler 146 func (h *HStore) Randomize(nextInt func() int64, fieldType string, shouldBeNull bool) { 147 if shouldBeNull { 148 *h = nil 149 return 150 } 151 152 *h = make(map[string]null.String) 153 (*h)[randomize.Str(nextInt, 3)] = null.String{String: randomize.Str(nextInt, 3), Valid: nextInt()%3 == 0} 154 (*h)[randomize.Str(nextInt, 3)] = null.String{String: randomize.Str(nextInt, 3), Valid: nextInt()%3 == 0} 155 }