package pgtype import ( "database/sql/driver" "encoding/binary" "encoding/json" "fmt" "math" "strconv" "github.com/jackc/pgx/v5/internal/pgio" ) type Int64Scanner interface { ScanInt64(Int8) error } type Int64Valuer interface { Int64Value() (Int8, error) } <% [2, 4, 8].each do |pg_byte_size| %> <% pg_bit_size = pg_byte_size * 8 %> type Int<%= pg_byte_size %> struct { Int<%= pg_bit_size %> int<%= pg_bit_size %> Valid bool } // ScanInt64 implements the Int64Scanner interface. func (dst *Int<%= pg_byte_size %>) ScanInt64(n Int8) error { if !n.Valid { *dst = Int<%= pg_byte_size %>{} return nil } if n.Int64 < math.MinInt<%= pg_bit_size %> { return fmt.Errorf("%d is less than minimum value for Int<%= pg_byte_size %>", n.Int64) } if n.Int64 > math.MaxInt<%= pg_bit_size %> { return fmt.Errorf("%d is greater than maximum value for Int<%= pg_byte_size %>", n.Int64) } *dst = Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: int<%= pg_bit_size %>(n.Int64), Valid: true} return nil } func (n Int<%= pg_byte_size %>) Int64Value() (Int8, error) { return Int8{Int64: int64(n.Int<%= pg_bit_size %>), Valid: n.Valid}, nil } // Scan implements the database/sql Scanner interface. func (dst *Int<%= pg_byte_size %>) Scan(src any) error { if src == nil { *dst = Int<%= pg_byte_size %>{} return nil } var n int64 switch src := src.(type) { case int64: n = src case string: var err error n, err = strconv.ParseInt(src, 10, <%= pg_bit_size %>) if err != nil { return err } case []byte: var err error n, err = strconv.ParseInt(string(src), 10, <%= pg_bit_size %>) if err != nil { return err } default: return fmt.Errorf("cannot scan %T", src) } if n < math.MinInt<%= pg_bit_size %> { return fmt.Errorf("%d is greater than maximum value for Int<%= pg_byte_size %>", n) } if n > math.MaxInt<%= pg_bit_size %> { return fmt.Errorf("%d is greater than maximum value for Int<%= pg_byte_size %>", n) } *dst = Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: int<%= pg_bit_size %>(n), Valid: true} return nil } // Value implements the database/sql/driver Valuer interface. func (src Int<%= pg_byte_size %>) Value() (driver.Value, error) { if !src.Valid { return nil, nil } return int64(src.Int<%= pg_bit_size %>), nil } func (src Int<%= pg_byte_size %>) MarshalJSON() ([]byte, error) { if !src.Valid { return []byte("null"), nil } return []byte(strconv.FormatInt(int64(src.Int<%= pg_bit_size %>), 10)), nil } func (dst *Int<%= pg_byte_size %>) UnmarshalJSON(b []byte) error { var n *int<%= pg_bit_size %> err := json.Unmarshal(b, &n) if err != nil { return err } if n == nil { *dst = Int<%= pg_byte_size %>{} } else { *dst = Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: *n, Valid: true} } return nil } type Int<%= pg_byte_size %>Codec struct{} func (Int<%= pg_byte_size %>Codec) FormatSupported(format int16) bool { return format == TextFormatCode || format == BinaryFormatCode } func (Int<%= pg_byte_size %>Codec) PreferredFormat() int16 { return BinaryFormatCode } func (Int<%= pg_byte_size %>Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan { switch format { case BinaryFormatCode: switch value.(type) { case int<%= pg_bit_size %>: return encodePlanInt<%= pg_byte_size %>CodecBinaryInt<%= pg_bit_size %>{} case Int64Valuer: return encodePlanInt<%= pg_byte_size %>CodecBinaryInt64Valuer{} } case TextFormatCode: switch value.(type) { case int<%= pg_bit_size %>: return encodePlanInt<%= pg_byte_size %>CodecTextInt<%= pg_bit_size %>{} case Int64Valuer: return encodePlanInt<%= pg_byte_size %>CodecTextInt64Valuer{} } } return nil } type encodePlanInt<%= pg_byte_size %>CodecBinaryInt<%= pg_bit_size %> struct{} func (encodePlanInt<%= pg_byte_size %>CodecBinaryInt<%= pg_bit_size %>) Encode(value any, buf []byte) (newBuf []byte, err error) { n := value.(int<%= pg_bit_size %>) return pgio.AppendInt<%= pg_bit_size %>(buf, int<%= pg_bit_size %>(n)), nil } type encodePlanInt<%= pg_byte_size %>CodecTextInt<%= pg_bit_size %> struct{} func (encodePlanInt<%= pg_byte_size %>CodecTextInt<%= pg_bit_size %>) Encode(value any, buf []byte) (newBuf []byte, err error) { n := value.(int<%= pg_bit_size %>) return append(buf, strconv.FormatInt(int64(n), 10)...), nil } type encodePlanInt<%= pg_byte_size %>CodecBinaryInt64Valuer struct{} func (encodePlanInt<%= pg_byte_size %>CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) { n, err := value.(Int64Valuer).Int64Value() if err != nil { return nil, err } if !n.Valid { return nil, nil } if n.Int64 > math.MaxInt<%= pg_bit_size %> { return nil, fmt.Errorf("%d is greater than maximum value for int<%= pg_byte_size %>", n.Int64) } if n.Int64 < math.MinInt<%= pg_bit_size %> { return nil, fmt.Errorf("%d is less than minimum value for int<%= pg_byte_size %>", n.Int64) } return pgio.AppendInt<%= pg_bit_size %>(buf, int<%= pg_bit_size %>(n.Int64)), nil } type encodePlanInt<%= pg_byte_size %>CodecTextInt64Valuer struct{} func (encodePlanInt<%= pg_byte_size %>CodecTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) { n, err := value.(Int64Valuer).Int64Value() if err != nil { return nil, err } if !n.Valid { return nil, nil } if n.Int64 > math.MaxInt<%= pg_bit_size %> { return nil, fmt.Errorf("%d is greater than maximum value for int<%= pg_byte_size %>", n.Int64) } if n.Int64 < math.MinInt<%= pg_bit_size %> { return nil, fmt.Errorf("%d is less than minimum value for int<%= pg_byte_size %>", n.Int64) } return append(buf, strconv.FormatInt(n.Int64, 10)...), nil } func (Int<%= pg_byte_size %>Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { switch format { case BinaryFormatCode: switch target.(type) { case *int8: return scanPlanBinaryInt<%= pg_byte_size %>ToInt8{} case *int16: return scanPlanBinaryInt<%= pg_byte_size %>ToInt16{} case *int32: return scanPlanBinaryInt<%= pg_byte_size %>ToInt32{} case *int64: return scanPlanBinaryInt<%= pg_byte_size %>ToInt64{} case *int: return scanPlanBinaryInt<%= pg_byte_size %>ToInt{} case *uint8: return scanPlanBinaryInt<%= pg_byte_size %>ToUint8{} case *uint16: return scanPlanBinaryInt<%= pg_byte_size %>ToUint16{} case *uint32: return scanPlanBinaryInt<%= pg_byte_size %>ToUint32{} case *uint64: return scanPlanBinaryInt<%= pg_byte_size %>ToUint64{} case *uint: return scanPlanBinaryInt<%= pg_byte_size %>ToUint{} case Int64Scanner: return scanPlanBinaryInt<%= pg_byte_size %>ToInt64Scanner{} case TextScanner: return scanPlanBinaryInt<%= pg_byte_size %>ToTextScanner{} } case TextFormatCode: switch target.(type) { case *int8: return scanPlanTextAnyToInt8{} case *int16: return scanPlanTextAnyToInt16{} case *int32: return scanPlanTextAnyToInt32{} case *int64: return scanPlanTextAnyToInt64{} case *int: return scanPlanTextAnyToInt{} case *uint8: return scanPlanTextAnyToUint8{} case *uint16: return scanPlanTextAnyToUint16{} case *uint32: return scanPlanTextAnyToUint32{} case *uint64: return scanPlanTextAnyToUint64{} case *uint: return scanPlanTextAnyToUint{} case Int64Scanner: return scanPlanTextAnyToInt64Scanner{} } } return nil } func (c Int<%= pg_byte_size %>Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) { if src == nil { return nil, nil } var n int64 err := codecScan(c, m, oid, format, src, &n) if err != nil { return nil, err } return n, nil } func (c Int<%= pg_byte_size %>Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) { if src == nil { return nil, nil } var n int<%= pg_bit_size %> err := codecScan(c, m, oid, format, src, &n) if err != nil { return nil, err } return n, nil } <%# PostgreSQL binary format integer to fixed size Go integers %> <% [8, 16, 32, 64].each do |dst_bit_size| %> type scanPlanBinaryInt<%= pg_byte_size %>ToInt<%= dst_bit_size %> struct{} func (scanPlanBinaryInt<%= pg_byte_size %>ToInt<%= dst_bit_size %>) Scan(src []byte, dst any) error { if src == nil { return fmt.Errorf("cannot scan NULL into %T", dst) } if len(src) != <%= pg_byte_size %> { return fmt.Errorf("invalid length for int<%= pg_byte_size %>: %v", len(src)) } p, ok := (dst).(*int<%= dst_bit_size %>) if !ok { return ErrScanTargetTypeChanged } <% if dst_bit_size < pg_bit_size %> n := int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src)) if n < math.MinInt<%= dst_bit_size %> { return fmt.Errorf("%d is less than minimum value for int<%= dst_bit_size %>", n) } else if n > math.MaxInt<%= dst_bit_size %> { return fmt.Errorf("%d is greater than maximum value for int<%= dst_bit_size %>", n) } *p = int<%= dst_bit_size %>(n) <% elsif dst_bit_size == pg_bit_size %> *p = int<%= dst_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src)) <% else %> *p = int<%= dst_bit_size %>(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src))) <% end %> return nil } type scanPlanBinaryInt<%= pg_byte_size %>ToUint<%= dst_bit_size %> struct{} func (scanPlanBinaryInt<%= pg_byte_size %>ToUint<%= dst_bit_size %>) Scan(src []byte, dst any) error { if src == nil { return fmt.Errorf("cannot scan NULL into %T", dst) } if len(src) != <%= pg_byte_size %> { return fmt.Errorf("invalid length for uint<%= pg_byte_size %>: %v", len(src)) } p, ok := (dst).(*uint<%= dst_bit_size %>) if !ok { return ErrScanTargetTypeChanged } n := int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src)) if n < 0 { return fmt.Errorf("%d is less than minimum value for uint<%= dst_bit_size %>", n) } <% if dst_bit_size < pg_bit_size %> if n > math.MaxUint<%= dst_bit_size %> { return fmt.Errorf("%d is greater than maximum value for uint<%= dst_bit_size %>", n) } <% end %> *p = uint<%= dst_bit_size %>(n) return nil } <% end %> <%# PostgreSQL binary format integer to Go machine integers %> type scanPlanBinaryInt<%= pg_byte_size %>ToInt struct{} func (scanPlanBinaryInt<%= pg_byte_size %>ToInt) Scan(src []byte, dst any) error { if src == nil { return fmt.Errorf("cannot scan NULL into %T", dst) } if len(src) != <%= pg_byte_size %> { return fmt.Errorf("invalid length for int<%= pg_byte_size %>: %v", len(src)) } p, ok := (dst).(*int) if !ok { return ErrScanTargetTypeChanged } <% if 32 < pg_bit_size %> n := int64(binary.BigEndian.Uint<%= pg_bit_size %>(src)) if n < math.MinInt { return fmt.Errorf("%d is less than minimum value for int", n) } else if n > math.MaxInt { return fmt.Errorf("%d is greater than maximum value for int", n) } *p = int(n) <% else %> *p = int(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src))) <% end %> return nil } type scanPlanBinaryInt<%= pg_byte_size %>ToUint struct{} func (scanPlanBinaryInt<%= pg_byte_size %>ToUint) Scan(src []byte, dst any) error { if src == nil { return fmt.Errorf("cannot scan NULL into %T", dst) } if len(src) != <%= pg_byte_size %> { return fmt.Errorf("invalid length for uint<%= pg_byte_size %>: %v", len(src)) } p, ok := (dst).(*uint) if !ok { return ErrScanTargetTypeChanged } n := int64(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src))) if n < 0 { return fmt.Errorf("%d is less than minimum value for uint", n) } <% if 32 < pg_bit_size %> if uint64(n) > math.MaxUint { return fmt.Errorf("%d is greater than maximum value for uint", n) } <% end %> *p = uint(n) return nil } <%# PostgreSQL binary format integer to Go Int64Scanner %> type scanPlanBinaryInt<%= pg_byte_size %>ToInt64Scanner struct{} func (scanPlanBinaryInt<%= pg_byte_size %>ToInt64Scanner) Scan(src []byte, dst any) error { s, ok := (dst).(Int64Scanner) if !ok { return ErrScanTargetTypeChanged } if src == nil { return s.ScanInt64(Int8{}) } if len(src) != <%= pg_byte_size %> { return fmt.Errorf("invalid length for int<%= pg_byte_size %>: %v", len(src)) } n := int64(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src))) return s.ScanInt64(Int8{Int64: n, Valid: true}) } <%# PostgreSQL binary format integer to Go TextScanner %> type scanPlanBinaryInt<%= pg_byte_size %>ToTextScanner struct{} func (scanPlanBinaryInt<%= pg_byte_size %>ToTextScanner) Scan(src []byte, dst any) error { s, ok := (dst).(TextScanner) if !ok { return ErrScanTargetTypeChanged } if src == nil { return s.ScanText(Text{}) } if len(src) != <%= pg_byte_size %> { return fmt.Errorf("invalid length for int<%= pg_byte_size %>: %v", len(src)) } n := int64(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src))) return s.ScanText(Text{String: strconv.FormatInt(n, 10), Valid: true}) } <% end %> <%# Any text to all integer types %> <% [ ["8", 8], ["16", 16], ["32", 32], ["64", 64], ["", 0] ].each do |type_suffix, bit_size| %> type scanPlanTextAnyToInt<%= type_suffix %> struct{} func (scanPlanTextAnyToInt<%= type_suffix %>) Scan(src []byte, dst any) error { if src == nil { return fmt.Errorf("cannot scan NULL into %T", dst) } p, ok := (dst).(*int<%= type_suffix %>) if !ok { return ErrScanTargetTypeChanged } n, err := strconv.ParseInt(string(src), 10, <%= bit_size %>) if err != nil { return err } *p = int<%= type_suffix %>(n) return nil } type scanPlanTextAnyToUint<%= type_suffix %> struct{} func (scanPlanTextAnyToUint<%= type_suffix %>) Scan(src []byte, dst any) error { if src == nil { return fmt.Errorf("cannot scan NULL into %T", dst) } p, ok := (dst).(*uint<%= type_suffix %>) if !ok { return ErrScanTargetTypeChanged } n, err := strconv.ParseUint(string(src), 10, <%= bit_size %>) if err != nil { return err } *p = uint<%= type_suffix %>(n) return nil } <% end %> type scanPlanTextAnyToInt64Scanner struct{} func (scanPlanTextAnyToInt64Scanner) Scan(src []byte, dst any) error { s, ok := (dst).(Int64Scanner) if !ok { return ErrScanTargetTypeChanged } if src == nil { return s.ScanInt64(Int8{}) } n, err := strconv.ParseInt(string(src), 10, 64) if err != nil { return err } err = s.ScanInt64(Int8{Int64: n, Valid: true}) if err != nil { return err } return nil }