1package pgproto3
2
3import (
4	"bytes"
5	"encoding/binary"
6	"encoding/json"
7
8	"github.com/jackc/pgx/pgio"
9)
10
11const (
12	TextFormat   = 0
13	BinaryFormat = 1
14)
15
16type FieldDescription struct {
17	Name                 string
18	TableOID             uint32
19	TableAttributeNumber uint16
20	DataTypeOID          uint32
21	DataTypeSize         int16
22	TypeModifier         int32
23	Format               int16
24}
25
26type RowDescription struct {
27	Fields []FieldDescription
28}
29
30func (*RowDescription) Backend() {}
31
32func (dst *RowDescription) Decode(src []byte) error {
33	buf := bytes.NewBuffer(src)
34
35	if buf.Len() < 2 {
36		return &invalidMessageFormatErr{messageType: "RowDescription"}
37	}
38	fieldCount := int(binary.BigEndian.Uint16(buf.Next(2)))
39
40	*dst = RowDescription{Fields: make([]FieldDescription, fieldCount)}
41
42	for i := 0; i < fieldCount; i++ {
43		var fd FieldDescription
44		bName, err := buf.ReadBytes(0)
45		if err != nil {
46			return err
47		}
48		fd.Name = string(bName[:len(bName)-1])
49
50		// Since buf.Next() doesn't return an error if we hit the end of the buffer
51		// check Len ahead of time
52		if buf.Len() < 18 {
53			return &invalidMessageFormatErr{messageType: "RowDescription"}
54		}
55
56		fd.TableOID = binary.BigEndian.Uint32(buf.Next(4))
57		fd.TableAttributeNumber = binary.BigEndian.Uint16(buf.Next(2))
58		fd.DataTypeOID = binary.BigEndian.Uint32(buf.Next(4))
59		fd.DataTypeSize = int16(binary.BigEndian.Uint16(buf.Next(2)))
60		fd.TypeModifier = int32(binary.BigEndian.Uint32(buf.Next(4)))
61		fd.Format = int16(binary.BigEndian.Uint16(buf.Next(2)))
62
63		dst.Fields[i] = fd
64	}
65
66	return nil
67}
68
69func (src *RowDescription) Encode(dst []byte) []byte {
70	dst = append(dst, 'T')
71	sp := len(dst)
72	dst = pgio.AppendInt32(dst, -1)
73
74	dst = pgio.AppendUint16(dst, uint16(len(src.Fields)))
75	for _, fd := range src.Fields {
76		dst = append(dst, fd.Name...)
77		dst = append(dst, 0)
78
79		dst = pgio.AppendUint32(dst, fd.TableOID)
80		dst = pgio.AppendUint16(dst, fd.TableAttributeNumber)
81		dst = pgio.AppendUint32(dst, fd.DataTypeOID)
82		dst = pgio.AppendInt16(dst, fd.DataTypeSize)
83		dst = pgio.AppendInt32(dst, fd.TypeModifier)
84		dst = pgio.AppendInt16(dst, fd.Format)
85	}
86
87	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
88
89	return dst
90}
91
92func (src *RowDescription) MarshalJSON() ([]byte, error) {
93	return json.Marshal(struct {
94		Type   string
95		Fields []FieldDescription
96	}{
97		Type:   "RowDescription",
98		Fields: src.Fields,
99	})
100}
101