...

Source file src/github.com/jackc/pgx/v5/pgtype/line.go

Documentation: github.com/jackc/pgx/v5/pgtype

     1  package pgtype
     2  
     3  import (
     4  	"database/sql/driver"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"math"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/jackc/pgx/v5/internal/pgio"
    12  )
    13  
    14  type LineScanner interface {
    15  	ScanLine(v Line) error
    16  }
    17  
    18  type LineValuer interface {
    19  	LineValue() (Line, error)
    20  }
    21  
    22  type Line struct {
    23  	A, B, C float64
    24  	Valid   bool
    25  }
    26  
    27  func (line *Line) ScanLine(v Line) error {
    28  	*line = v
    29  	return nil
    30  }
    31  
    32  func (line Line) LineValue() (Line, error) {
    33  	return line, nil
    34  }
    35  
    36  func (line *Line) Set(src any) error {
    37  	return fmt.Errorf("cannot convert %v to Line", src)
    38  }
    39  
    40  // Scan implements the database/sql Scanner interface.
    41  func (line *Line) Scan(src any) error {
    42  	if src == nil {
    43  		*line = Line{}
    44  		return nil
    45  	}
    46  
    47  	switch src := src.(type) {
    48  	case string:
    49  		return scanPlanTextAnyToLineScanner{}.Scan([]byte(src), line)
    50  	}
    51  
    52  	return fmt.Errorf("cannot scan %T", src)
    53  }
    54  
    55  // Value implements the database/sql/driver Valuer interface.
    56  func (line Line) Value() (driver.Value, error) {
    57  	if !line.Valid {
    58  		return nil, nil
    59  	}
    60  
    61  	buf, err := LineCodec{}.PlanEncode(nil, 0, TextFormatCode, line).Encode(line, nil)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	return string(buf), err
    66  }
    67  
    68  type LineCodec struct{}
    69  
    70  func (LineCodec) FormatSupported(format int16) bool {
    71  	return format == TextFormatCode || format == BinaryFormatCode
    72  }
    73  
    74  func (LineCodec) PreferredFormat() int16 {
    75  	return BinaryFormatCode
    76  }
    77  
    78  func (LineCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
    79  	if _, ok := value.(LineValuer); !ok {
    80  		return nil
    81  	}
    82  
    83  	switch format {
    84  	case BinaryFormatCode:
    85  		return encodePlanLineCodecBinary{}
    86  	case TextFormatCode:
    87  		return encodePlanLineCodecText{}
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  type encodePlanLineCodecBinary struct{}
    94  
    95  func (encodePlanLineCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {
    96  	line, err := value.(LineValuer).LineValue()
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	if !line.Valid {
   102  		return nil, nil
   103  	}
   104  
   105  	buf = pgio.AppendUint64(buf, math.Float64bits(line.A))
   106  	buf = pgio.AppendUint64(buf, math.Float64bits(line.B))
   107  	buf = pgio.AppendUint64(buf, math.Float64bits(line.C))
   108  	return buf, nil
   109  }
   110  
   111  type encodePlanLineCodecText struct{}
   112  
   113  func (encodePlanLineCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {
   114  	line, err := value.(LineValuer).LineValue()
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	if !line.Valid {
   120  		return nil, nil
   121  	}
   122  
   123  	buf = append(buf, fmt.Sprintf(`{%s,%s,%s}`,
   124  		strconv.FormatFloat(line.A, 'f', -1, 64),
   125  		strconv.FormatFloat(line.B, 'f', -1, 64),
   126  		strconv.FormatFloat(line.C, 'f', -1, 64),
   127  	)...)
   128  	return buf, nil
   129  }
   130  
   131  func (LineCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
   132  
   133  	switch format {
   134  	case BinaryFormatCode:
   135  		switch target.(type) {
   136  		case LineScanner:
   137  			return scanPlanBinaryLineToLineScanner{}
   138  		}
   139  	case TextFormatCode:
   140  		switch target.(type) {
   141  		case LineScanner:
   142  			return scanPlanTextAnyToLineScanner{}
   143  		}
   144  	}
   145  
   146  	return nil
   147  }
   148  
   149  type scanPlanBinaryLineToLineScanner struct{}
   150  
   151  func (scanPlanBinaryLineToLineScanner) Scan(src []byte, dst any) error {
   152  	scanner := (dst).(LineScanner)
   153  
   154  	if src == nil {
   155  		return scanner.ScanLine(Line{})
   156  	}
   157  
   158  	if len(src) != 24 {
   159  		return fmt.Errorf("invalid length for line: %v", len(src))
   160  	}
   161  
   162  	a := binary.BigEndian.Uint64(src)
   163  	b := binary.BigEndian.Uint64(src[8:])
   164  	c := binary.BigEndian.Uint64(src[16:])
   165  
   166  	return scanner.ScanLine(Line{
   167  		A:     math.Float64frombits(a),
   168  		B:     math.Float64frombits(b),
   169  		C:     math.Float64frombits(c),
   170  		Valid: true,
   171  	})
   172  }
   173  
   174  type scanPlanTextAnyToLineScanner struct{}
   175  
   176  func (scanPlanTextAnyToLineScanner) Scan(src []byte, dst any) error {
   177  	scanner := (dst).(LineScanner)
   178  
   179  	if src == nil {
   180  		return scanner.ScanLine(Line{})
   181  	}
   182  
   183  	if len(src) < 7 {
   184  		return fmt.Errorf("invalid length for line: %v", len(src))
   185  	}
   186  
   187  	parts := strings.SplitN(string(src[1:len(src)-1]), ",", 3)
   188  	if len(parts) < 3 {
   189  		return fmt.Errorf("invalid format for line")
   190  	}
   191  
   192  	a, err := strconv.ParseFloat(parts[0], 64)
   193  	if err != nil {
   194  		return err
   195  	}
   196  
   197  	b, err := strconv.ParseFloat(parts[1], 64)
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	c, err := strconv.ParseFloat(parts[2], 64)
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	return scanner.ScanLine(Line{A: a, B: b, C: c, Valid: true})
   208  }
   209  
   210  func (c LineCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
   211  	return codecDecodeToTextFormat(c, m, oid, format, src)
   212  }
   213  
   214  func (c LineCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
   215  	if src == nil {
   216  		return nil, nil
   217  	}
   218  
   219  	var line Line
   220  	err := codecScan(c, m, oid, format, src, &line)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  	return line, nil
   225  }
   226  

View as plain text