Skip to content

Instantly share code, notes, and snippets.

@pkieltyka
Last active July 11, 2016 11:38
Show Gist options
  • Save pkieltyka/06b5f25295bf24d7a5de to your computer and use it in GitHub Desktop.
Save pkieltyka/06b5f25295bf24d7a5de to your computer and use it in GitHub Desktop.
// PARSING ARRAYS
// SEE http://www.postgresql.org/docs/9.1/static/arrays.html#ARRAYS-IO
// Arrays are output within {} and a delimiter, which is a comma for most
// postgres types (; for box)
//
// Individual values are surrounded by quotes:
// The array output routine will put double quotes around element values if
// they are empty strings, contain curly braces, delimiter characters,
// double quotes, backslashes, or white space, or match the word NULL.
// Double quotes and backslashes embedded in element values will be
// backslash-escaped. For numeric data types it is safe to assume that double
// quotes will never appear, but for textual data types one should be prepared
// to cope with either the presence or absence of quotes.
// construct a regexp to extract values:
var (
// unquoted array values must not contain: (" , \ { } whitespace NULL)
// and must be at least one char
unquotedChar = `[^",\\{}\s(NULL)]`
unquotedValue = fmt.Sprintf("(%s)+", unquotedChar)
// quoted array values are surrounded by double quotes, can be any
// character except " or \, which must be backslash escaped:
quotedChar = `[^"\\]|\\"|\\\\`
quotedValue = fmt.Sprintf("\"(%s)*\"", quotedChar)
// an array value may be either quoted or unquoted:
arrayValue = fmt.Sprintf("(?P<value>(%s|%s))", unquotedValue, quotedValue)
// Array values are separated with a comma IF there is more than one value:
arrayExp = regexp.MustCompile(fmt.Sprintf("((%s)(,)?)", arrayValue))
)
type StringArray []string
func (a *StringArray) Scan(src interface{}) error {
asBytes, ok := src.([]byte)
if !ok {
return errors.New("Scan source was not []bytes")
}
asString := string(asBytes)
results := make([]string, 0)
matches := arrayExp.FindAllStringSubmatch(asString, -1)
for _, match := range matches {
s := match[0]
// the string _might_ be wrapped in quotes, so trim them:
s = strings.Trim(s, "\"")
results = append(results, s)
}
*a = StringArray(results)
return nil
}
func (a StringArray) Value() (driver.Value, error) {
if len(a) == 0 {
return nil, nil
}
var buffer bytes.Buffer
buffer.WriteString("{")
last := len(a) - 1
for i, val := range a {
buffer.WriteString(strconv.Quote(val))
if i != last {
buffer.WriteString(",")
}
}
buffer.WriteString("}")
return string(buffer.Bytes()), nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment