diff options
Diffstat (limited to 'vendor/github.com/uptrace/bun/dialect')
12 files changed, 430 insertions, 41 deletions
diff --git a/vendor/github.com/uptrace/bun/dialect/feature/feature.go b/vendor/github.com/uptrace/bun/dialect/feature/feature.go index 510d6e5de..956dc4985 100644 --- a/vendor/github.com/uptrace/bun/dialect/feature/feature.go +++ b/vendor/github.com/uptrace/bun/dialect/feature/feature.go @@ -29,4 +29,6 @@ const ( OffsetFetch SelectExists UpdateFromTable + MSSavepoint + GeneratedIdentity ) diff --git a/vendor/github.com/uptrace/bun/dialect/pgdialect/append.go b/vendor/github.com/uptrace/bun/dialect/pgdialect/append.go index d5e0d0a57..a60bf5de2 100644 --- a/vendor/github.com/uptrace/bun/dialect/pgdialect/append.go +++ b/vendor/github.com/uptrace/bun/dialect/pgdialect/append.go @@ -307,3 +307,58 @@ func arrayAppendString(b []byte, s string) []byte { b = append(b, '"') return b } + +//------------------------------------------------------------------------------ + +var mapStringStringType = reflect.TypeOf(map[string]string(nil)) + +func (d *Dialect) hstoreAppender(typ reflect.Type) schema.AppenderFunc { + kind := typ.Kind() + + switch kind { + case reflect.Ptr: + if fn := d.hstoreAppender(typ.Elem()); fn != nil { + return schema.PtrAppender(fn) + } + case reflect.Map: + // ok: + default: + return nil + } + + if typ.Key() == stringType && typ.Elem() == stringType { + return appendMapStringStringValue + } + + return func(fmter schema.Formatter, b []byte, v reflect.Value) []byte { + err := fmt.Errorf("bun: Hstore(unsupported %s)", v.Type()) + return dialect.AppendError(b, err) + } +} + +func appendMapStringString(b []byte, m map[string]string) []byte { + if m == nil { + return dialect.AppendNull(b) + } + + b = append(b, '\'') + + for key, value := range m { + b = arrayAppendString(b, key) + b = append(b, '=', '>') + b = arrayAppendString(b, value) + b = append(b, ',') + } + if len(m) > 0 { + b = b[:len(b)-1] // Strip trailing comma. + } + + b = append(b, '\'') + + return b +} + +func appendMapStringStringValue(fmter schema.Formatter, b []byte, v reflect.Value) []byte { + m := v.Convert(mapStringStringType).Interface().(map[string]string) + return appendMapStringString(b, m) +} diff --git a/vendor/github.com/uptrace/bun/dialect/pgdialect/array_parser.go b/vendor/github.com/uptrace/bun/dialect/pgdialect/array_parser.go index 0dff754f8..a8358337e 100644 --- a/vendor/github.com/uptrace/bun/dialect/pgdialect/array_parser.go +++ b/vendor/github.com/uptrace/bun/dialect/pgdialect/array_parser.go @@ -8,17 +8,13 @@ import ( ) type arrayParser struct { - b []byte - i int - - buf []byte + *streamParser err error } func newArrayParser(b []byte) *arrayParser { p := &arrayParser{ - b: b, - i: 1, + streamParser: newStreamParser(b, 1), } if len(b) < 2 || b[0] != '{' || b[len(b)-1] != '}' { p.err = fmt.Errorf("bun: can't parse array: %q", b) @@ -135,31 +131,3 @@ func (p *arrayParser) readSubstring() ([]byte, error) { return p.buf, nil } - -func (p *arrayParser) valid() bool { - return p.i < len(p.b) -} - -func (p *arrayParser) readByte() (byte, error) { - if p.valid() { - c := p.b[p.i] - p.i++ - return c, nil - } - return 0, io.EOF -} - -func (p *arrayParser) unreadByte() { - p.i-- -} - -func (p *arrayParser) peek() byte { - if p.valid() { - return p.b[p.i] - } - return 0 -} - -func (p *arrayParser) skipNext() { - p.i++ -} diff --git a/vendor/github.com/uptrace/bun/dialect/pgdialect/dialect.go b/vendor/github.com/uptrace/bun/dialect/pgdialect/dialect.go index 852132b7f..d524f0a1a 100644 --- a/vendor/github.com/uptrace/bun/dialect/pgdialect/dialect.go +++ b/vendor/github.com/uptrace/bun/dialect/pgdialect/dialect.go @@ -46,7 +46,8 @@ func New() *Dialect { feature.TableTruncate | feature.TableNotExists | feature.InsertOnConflict | - feature.SelectExists + feature.SelectExists | + feature.GeneratedIdentity return d } @@ -73,7 +74,7 @@ func (d *Dialect) OnTable(table *schema.Table) { func (d *Dialect) onField(field *schema.Field) { field.DiscoveredSQLType = fieldSQLType(field) - if field.AutoIncrement { + if field.AutoIncrement && !field.Identity { switch field.DiscoveredSQLType { case sqltype.SmallInt: field.CreateTableSQLType = pgTypeSmallSerial @@ -88,6 +89,11 @@ func (d *Dialect) onField(field *schema.Field) { field.Append = d.arrayAppender(field.StructField.Type) field.Scan = arrayScanner(field.StructField.Type) } + + if field.DiscoveredSQLType == sqltype.HSTORE { + field.Append = d.hstoreAppender(field.StructField.Type) + field.Scan = hstoreScanner(field.StructField.Type) + } } func (d *Dialect) IdentQuote() byte { diff --git a/vendor/github.com/uptrace/bun/dialect/pgdialect/hstore.go b/vendor/github.com/uptrace/bun/dialect/pgdialect/hstore.go new file mode 100644 index 000000000..029f7cb6d --- /dev/null +++ b/vendor/github.com/uptrace/bun/dialect/pgdialect/hstore.go @@ -0,0 +1,73 @@ +package pgdialect + +import ( + "database/sql" + "fmt" + "reflect" + + "github.com/uptrace/bun/schema" +) + +type HStoreValue struct { + v reflect.Value + + append schema.AppenderFunc + scan schema.ScannerFunc +} + +// HStore accepts a map[string]string and returns a wrapper for working with PostgreSQL +// hstore data type. +// +// For struct fields you can use hstore tag: +// +// Attrs map[string]string `bun:",hstore"` +func HStore(vi interface{}) *HStoreValue { + v := reflect.ValueOf(vi) + if !v.IsValid() { + panic(fmt.Errorf("bun: HStore(nil)")) + } + + typ := v.Type() + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + if typ.Kind() != reflect.Map { + panic(fmt.Errorf("bun: Hstore(unsupported %s)", typ)) + } + + return &HStoreValue{ + v: v, + + append: pgDialect.hstoreAppender(v.Type()), + scan: hstoreScanner(v.Type()), + } +} + +var ( + _ schema.QueryAppender = (*HStoreValue)(nil) + _ sql.Scanner = (*HStoreValue)(nil) +) + +func (h *HStoreValue) AppendQuery(fmter schema.Formatter, b []byte) ([]byte, error) { + if h.append == nil { + panic(fmt.Errorf("bun: HStore(unsupported %s)", h.v.Type())) + } + return h.append(fmter, b, h.v), nil +} + +func (h *HStoreValue) Scan(src interface{}) error { + if h.scan == nil { + return fmt.Errorf("bun: HStore(unsupported %s)", h.v.Type()) + } + if h.v.Kind() != reflect.Ptr { + return fmt.Errorf("bun: HStore(non-pointer %s)", h.v.Type()) + } + return h.scan(h.v.Elem(), src) +} + +func (h *HStoreValue) Value() interface{} { + if h.v.IsValid() { + return h.v.Interface() + } + return nil +} diff --git a/vendor/github.com/uptrace/bun/dialect/pgdialect/hstore_parser.go b/vendor/github.com/uptrace/bun/dialect/pgdialect/hstore_parser.go new file mode 100644 index 000000000..7a18b50b1 --- /dev/null +++ b/vendor/github.com/uptrace/bun/dialect/pgdialect/hstore_parser.go @@ -0,0 +1,142 @@ +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 +} diff --git a/vendor/github.com/uptrace/bun/dialect/pgdialect/hstore_scan.go b/vendor/github.com/uptrace/bun/dialect/pgdialect/hstore_scan.go new file mode 100644 index 000000000..b10b06b8d --- /dev/null +++ b/vendor/github.com/uptrace/bun/dialect/pgdialect/hstore_scan.go @@ -0,0 +1,82 @@ +package pgdialect + +import ( + "fmt" + "io" + "reflect" + + "github.com/uptrace/bun/schema" +) + +func hstoreScanner(typ reflect.Type) schema.ScannerFunc { + kind := typ.Kind() + + switch kind { + case reflect.Ptr: + if fn := hstoreScanner(typ.Elem()); fn != nil { + return schema.PtrScanner(fn) + } + case reflect.Map: + // ok: + default: + return nil + } + + if typ.Key() == stringType && typ.Elem() == stringType { + return scanMapStringStringValue + } + return func(dest reflect.Value, src interface{}) error { + return fmt.Errorf("bun: Hstore(unsupported %s)", dest.Type()) + } +} + +func scanMapStringStringValue(dest reflect.Value, src interface{}) error { + dest = reflect.Indirect(dest) + if !dest.CanSet() { + return fmt.Errorf("bun: Scan(non-settable %s)", dest.Type()) + } + + m, err := decodeMapStringString(src) + if err != nil { + return err + } + + dest.Set(reflect.ValueOf(m)) + return nil +} + +func decodeMapStringString(src interface{}) (map[string]string, error) { + if src == nil { + return nil, nil + } + + b, err := toBytes(src) + if err != nil { + return nil, err + } + + m := make(map[string]string) + + p := newHStoreParser(b) + for { + key, err := p.NextKey() + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + + value, err := p.NextValue() + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + + m[key] = value + } + + return m, nil +} diff --git a/vendor/github.com/uptrace/bun/dialect/pgdialect/sqltype.go b/vendor/github.com/uptrace/bun/dialect/pgdialect/sqltype.go index bfef89fa1..6c6294d71 100644 --- a/vendor/github.com/uptrace/bun/dialect/pgdialect/sqltype.go +++ b/vendor/github.com/uptrace/bun/dialect/pgdialect/sqltype.go @@ -53,11 +53,11 @@ func fieldSQLType(field *schema.Field) string { if v, ok := field.Tag.Option("composite"); ok { return v } - if _, ok := field.Tag.Option("hstore"); ok { - return "hstore" + if field.Tag.HasOption("hstore") { + return sqltype.HSTORE } - if _, ok := field.Tag.Options["array"]; ok { + if field.Tag.HasOption("array") { switch field.IndirectType.Kind() { case reflect.Slice, reflect.Array: sqlType := sqlType(field.IndirectType.Elem()) diff --git a/vendor/github.com/uptrace/bun/dialect/pgdialect/stream_parser.go b/vendor/github.com/uptrace/bun/dialect/pgdialect/stream_parser.go new file mode 100644 index 000000000..7b9a15f62 --- /dev/null +++ b/vendor/github.com/uptrace/bun/dialect/pgdialect/stream_parser.go @@ -0,0 +1,60 @@ +package pgdialect + +import ( + "fmt" + "io" +) + +type streamParser struct { + b []byte + i int + + buf []byte +} + +func newStreamParser(b []byte, start int) *streamParser { + return &streamParser{ + b: b, + i: start, + } +} + +func (p *streamParser) valid() bool { + return p.i < len(p.b) +} + +func (p *streamParser) skipByte(skip byte) error { + c, err := p.readByte() + if err != nil { + return err + } + if c == skip { + return nil + } + p.unreadByte() + return fmt.Errorf("got %q, wanted %q", c, skip) +} + +func (p *streamParser) readByte() (byte, error) { + if p.valid() { + c := p.b[p.i] + p.i++ + return c, nil + } + return 0, io.EOF +} + +func (p *streamParser) unreadByte() { + p.i-- +} + +func (p *streamParser) peek() byte { + if p.valid() { + return p.b[p.i] + } + return 0 +} + +func (p *streamParser) skipNext() { + p.i++ +} diff --git a/vendor/github.com/uptrace/bun/dialect/pgdialect/version.go b/vendor/github.com/uptrace/bun/dialect/pgdialect/version.go index a3adb36aa..8ab18b7a7 100644 --- a/vendor/github.com/uptrace/bun/dialect/pgdialect/version.go +++ b/vendor/github.com/uptrace/bun/dialect/pgdialect/version.go @@ -2,5 +2,5 @@ package pgdialect // Version is the current release version. func Version() string { - return "1.1.3" + return "1.1.7" } diff --git a/vendor/github.com/uptrace/bun/dialect/sqlitedialect/version.go b/vendor/github.com/uptrace/bun/dialect/sqlitedialect/version.go index 76a755a86..8f9def8d0 100644 --- a/vendor/github.com/uptrace/bun/dialect/sqlitedialect/version.go +++ b/vendor/github.com/uptrace/bun/dialect/sqlitedialect/version.go @@ -2,5 +2,5 @@ package sqlitedialect // Version is the current release version. func Version() string { - return "1.1.3" + return "1.1.7" } diff --git a/vendor/github.com/uptrace/bun/dialect/sqltype/sqltype.go b/vendor/github.com/uptrace/bun/dialect/sqltype/sqltype.go index f58b2f1d1..1031fd352 100644 --- a/vendor/github.com/uptrace/bun/dialect/sqltype/sqltype.go +++ b/vendor/github.com/uptrace/bun/dialect/sqltype/sqltype.go @@ -12,4 +12,5 @@ const ( Timestamp = "TIMESTAMP" JSON = "JSON" JSONB = "JSONB" + HSTORE = "HSTORE" ) |