Last active
June 25, 2024 06:51
-
-
Save alexisvisco/158d368b16faf2864f0485929a4cd38a to your computer and use it in GitHub Desktop.
Revisions
-
alexisvisco revised this gist
Jan 26, 2024 . 1 changed file with 26 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,15 +4,28 @@ import ( "context" "database/sql" "database/sql/driver" "encoding/json" "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/schema" "strings" ) // Hstore is a wrapper for transferring Hstore values back and forth easily. type Hstore map[string]sql.NullString func (h Hstore) ToMap() map[string]string { m := make(map[string]string) for k, v := range h { if v.Valid { m[k] = v.String } } return m } func (h Hstore) MarshalJSON() ([]byte, error) { return json.Marshal(h.ToMap()) } // escapes and quotes hstore keys/values @@ -41,18 +54,19 @@ func hQuote(s interface{}) string { // hstore column's database value is NULL, then h.Map is set to nil instead. func (h *Hstore) Scan(value interface{}) error { if value == nil { h = nil return nil } *h = make(map[string]sql.NullString) var b byte pair := [][]byte{{}, {}} pi := 0 inQuote := false didQuote := false sawSlash := false bindex := 0 for bindex, b = range []byte(value.(string)) { if sawSlash { pair[pi] = append(pair[pi], b) sawSlash = false @@ -83,9 +97,9 @@ func (h *Hstore) Scan(value interface{}) error { case ',': s := string(pair[1]) if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { (*h)[string(pair[0])] = sql.NullString{String: "", Valid: false} } else { (*h)[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} } pair[0] = []byte{} pair[1] = []byte{} @@ -99,9 +113,9 @@ func (h *Hstore) Scan(value interface{}) error { if bindex > 0 { s := string(pair[1]) if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { (*h)[string(pair[0])] = sql.NullString{String: "", Valid: false} } else { (*h)[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} } } return nil @@ -110,11 +124,11 @@ func (h *Hstore) Scan(value interface{}) error { // Value implements the driver Valuer interface. Note if h.Map is nil, the // database column value will be set to NULL. func (h Hstore) Value() (driver.Value, error) { if h == nil { return nil, nil } parts := []string{} for key, val := range h { thispart := hQuote(key) + "=>" + hQuote(val) parts = append(parts, thispart) } @@ -136,7 +150,7 @@ func (h Hstore) GormDBDataType(db *gorm.DB, field *schema.Field) string { } func (h Hstore) GormValue(_ context.Context, db *gorm.DB) clause.Expr { if len(h) == 0 { return gorm.Expr("NULL") } -
alexisvisco revised this gist
Dec 20, 2023 . No changes.There are no files selected for viewing
-
alexisvisco created this gist
Dec 20, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,146 @@ package hstore import ( "context" "database/sql" "database/sql/driver" "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/schema" "strings" ) // Hstore is a wrapper for transferring Hstore values back and forth easily. type Hstore struct { Map map[string]sql.NullString } // escapes and quotes hstore keys/values // s should be a sql.NullString or string func hQuote(s interface{}) string { var str string switch v := s.(type) { case sql.NullString: if !v.Valid { return "NULL" } str = v.String case string: str = v default: panic("not a string or sql.NullString") } str = strings.Replace(str, "\\", "\\\\", -1) return `"` + strings.Replace(str, "\"", "\\\"", -1) + `"` } // Scan implements the Scanner interface. // // Note h.Map is reallocated before the scan to clear existing values. If the // hstore column's database value is NULL, then h.Map is set to nil instead. func (h *Hstore) Scan(value interface{}) error { if value == nil { h.Map = nil return nil } h.Map = make(map[string]sql.NullString) var b byte pair := [][]byte{{}, {}} pi := 0 inQuote := false didQuote := false sawSlash := false bindex := 0 for bindex, b = range value.([]byte) { if sawSlash { pair[pi] = append(pair[pi], b) sawSlash = false continue } switch b { case '\\': sawSlash = true continue case '"': inQuote = !inQuote if !didQuote { didQuote = true } continue default: if !inQuote { switch b { case ' ', '\t', '\n', '\r': continue case '=': continue case '>': pi = 1 didQuote = false continue case ',': s := string(pair[1]) if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} } else { h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} } pair[0] = []byte{} pair[1] = []byte{} pi = 0 continue } } } pair[pi] = append(pair[pi], b) } if bindex > 0 { s := string(pair[1]) if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} } else { h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} } } return nil } // Value implements the driver Valuer interface. Note if h.Map is nil, the // database column value will be set to NULL. func (h Hstore) Value() (driver.Value, error) { if h.Map == nil { return nil, nil } parts := []string{} for key, val := range h.Map { thispart := hQuote(key) + "=>" + hQuote(val) parts = append(parts, thispart) } return []byte(strings.Join(parts, ",")), nil } // GormDataType gorm common data type func (h Hstore) GormDataType() string { return "hstore" } // GormDBDataType gorm db data type func (h Hstore) GormDBDataType(db *gorm.DB, field *schema.Field) string { switch db.Dialector.Name() { case "postgres": return "HSTORE" } return "" } func (h Hstore) GormValue(_ context.Context, db *gorm.DB) clause.Expr { if len(h.Map) == 0 { return gorm.Expr("NULL") } data, _ := h.Value() return gorm.Expr("?", string(data.([]byte))) }