5d9ff5df03
Bun's pgdialect scans/appends native slices directly, so array columns
(text[], integer[], uuid[], ...) always generate as plain []string,
[]int32, etc. with an explicit "array" bun tag, regardless of --types
(sqltypes/stdlib/baselib). The SqlXxxArray wrapper types are no longer
used for Bun array columns (gorm is unaffected and keeps using them).
Adds --array-nullable pointer_slice to represent nullable array columns
as *[]T instead of []T, so callers can distinguish SQL NULL (nil) from
'{}' (pointer to an empty slice). Verified end-to-end against a live
PostgreSQL instance for NULL/{}/populated arrays in every --types mode.
Closes #13
120 lines
1.6 KiB
Go
120 lines
1.6 KiB
Go
package pgdialect
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
type arrayParser struct {
|
|
p pgparser
|
|
|
|
elem []byte
|
|
err error
|
|
|
|
isJson bool
|
|
}
|
|
|
|
func newArrayParser(b []byte) *arrayParser {
|
|
p := new(arrayParser)
|
|
|
|
if b[0] == 'n' {
|
|
p.p.Reset(nil)
|
|
return p
|
|
}
|
|
|
|
if len(b) < 2 || (b[0] != '{' && b[0] != '[') || (b[len(b)-1] != '}' && b[len(b)-1] != ']') {
|
|
p.err = fmt.Errorf("pgdialect: can't parse array: %q", b)
|
|
return p
|
|
}
|
|
p.isJson = b[0] == '['
|
|
|
|
p.p.Reset(b[1 : len(b)-1])
|
|
return p
|
|
}
|
|
|
|
func (p *arrayParser) Next() bool {
|
|
if p.err != nil {
|
|
return false
|
|
}
|
|
p.err = p.readNext()
|
|
return p.err == nil
|
|
}
|
|
|
|
func (p *arrayParser) Err() error {
|
|
if p.err != io.EOF {
|
|
return p.err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *arrayParser) Elem() []byte {
|
|
return p.elem
|
|
}
|
|
|
|
func (p *arrayParser) readNext() error {
|
|
ch := p.p.Read()
|
|
if ch == 0 {
|
|
return io.EOF
|
|
}
|
|
|
|
switch ch {
|
|
case '}', ']':
|
|
return io.EOF
|
|
case '"':
|
|
b, err := p.p.ReadSubstring(ch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if p.p.Peek() == ',' {
|
|
p.p.Advance()
|
|
}
|
|
|
|
p.elem = b
|
|
return nil
|
|
case '[', '(':
|
|
rng, err := p.p.ReadRange(ch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if p.p.Peek() == ',' {
|
|
p.p.Advance()
|
|
}
|
|
|
|
p.elem = rng
|
|
return nil
|
|
default:
|
|
if ch == '{' && p.isJson {
|
|
json, err := p.p.ReadJSON()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for {
|
|
if p.p.Peek() == ',' || p.p.Peek() == ' ' {
|
|
p.p.Advance()
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
p.elem = json
|
|
return nil
|
|
} else {
|
|
lit := p.p.ReadLiteral(ch)
|
|
if bytes.Equal(lit, []byte("NULL")) {
|
|
lit = nil
|
|
}
|
|
|
|
if p.p.Peek() == ',' {
|
|
p.p.Advance()
|
|
}
|
|
|
|
p.elem = lit
|
|
return nil
|
|
}
|
|
}
|
|
}
|