143 lines
2.0 KiB
Go
143 lines
2.0 KiB
Go
package pgdialect
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
)
|
|
|
|
type hstoreParser struct {
|
|
*streamParser
|
|
err error
|
|
}
|
|
|
|
func newHStoreParser(b []byte) *hstoreParser {
|
|
p := &hstoreParser{
|
|
streamParser: newStreamParser(b, 0),
|
|
}
|
|
if len(b) < 6 || b[0] != '"' {
|
|
p.err = fmt.Errorf("bun: can't parse hstore: %q", b)
|
|
}
|
|
return p
|
|
}
|
|
|
|
func (p *hstoreParser) NextKey() (string, error) {
|
|
if p.err != nil {
|
|
return "", p.err
|
|
}
|
|
|
|
err := p.skipByte('"')
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
key, err := p.readSubstring()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
const separator = "=>"
|
|
|
|
for i := range separator {
|
|
err = p.skipByte(separator[i])
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
return string(key), nil
|
|
}
|
|
|
|
func (p *hstoreParser) NextValue() (string, error) {
|
|
if p.err != nil {
|
|
return "", p.err
|
|
}
|
|
|
|
c, err := p.readByte()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
switch c {
|
|
case '"':
|
|
value, err := p.readSubstring()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if p.peek() == ',' {
|
|
p.skipNext()
|
|
}
|
|
|
|
if p.peek() == ' ' {
|
|
p.skipNext()
|
|
}
|
|
|
|
return string(value), nil
|
|
default:
|
|
value := p.readSimple()
|
|
if bytes.Equal(value, []byte("NULL")) {
|
|
value = nil
|
|
}
|
|
|
|
if p.peek() == ',' {
|
|
p.skipNext()
|
|
}
|
|
|
|
return string(value), nil
|
|
}
|
|
}
|
|
|
|
func (p *hstoreParser) readSimple() []byte {
|
|
p.unreadByte()
|
|
|
|
if i := bytes.IndexByte(p.b[p.i:], ','); i >= 0 {
|
|
b := p.b[p.i : p.i+i]
|
|
p.i += i
|
|
return b
|
|
}
|
|
|
|
b := p.b[p.i:len(p.b)]
|
|
p.i = len(p.b)
|
|
return b
|
|
}
|
|
|
|
func (p *hstoreParser) readSubstring() ([]byte, error) {
|
|
c, err := p.readByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.buf = p.buf[:0]
|
|
for {
|
|
if c == '"' {
|
|
break
|
|
}
|
|
|
|
next, err := p.readByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if c == '\\' {
|
|
switch next {
|
|
case '\\', '"':
|
|
p.buf = append(p.buf, next)
|
|
|
|
c, err = p.readByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
default:
|
|
p.buf = append(p.buf, '\\')
|
|
c = next
|
|
}
|
|
continue
|
|
}
|
|
|
|
p.buf = append(p.buf, c)
|
|
c = next
|
|
}
|
|
|
|
return p.buf, nil
|
|
}
|