mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-18 08:26:45 +02:00
119 lines
2.6 KiB
Go
119 lines
2.6 KiB
Go
|
package hstore
|
||
|
|
||
|
import (
|
||
|
"database/sql"
|
||
|
"database/sql/driver"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// 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
|
||
|
}
|