github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/pgwire/write_buffer.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package pgwire 12 13 import ( 14 "bytes" 15 "encoding/binary" 16 "io" 17 18 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgwirebase" 19 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 20 "github.com/cockroachdb/cockroach/pkg/util" 21 "github.com/cockroachdb/cockroach/pkg/util/metric" 22 ) 23 24 // writeBuffer is a wrapper around bytes.Buffer that provides a convenient interface 25 // for writing PGWire results. The buffer preserves any errors it encounters when writing, 26 // and will turn all subsequent write attempts into no-ops until finishMsg is called. 27 type writeBuffer struct { 28 _ util.NoCopy 29 30 wrapped bytes.Buffer 31 err error 32 33 // Buffer used for temporary storage. 34 putbuf [64]byte 35 36 textFormatter *tree.FmtCtx 37 simpleFormatter *tree.FmtCtx 38 39 // bytecount counts the number of bytes written across all pgwire connections, not just this 40 // buffer. This is passed in so that finishMsg can track all messages we've sent to a network 41 // socket, reducing the onus on the many callers of finishMsg. 42 bytecount *metric.Counter 43 } 44 45 func newWriteBuffer(bytecount *metric.Counter) *writeBuffer { 46 b := new(writeBuffer) 47 b.init(bytecount) 48 return b 49 } 50 51 // init exists to avoid the allocation imposed by newWriteBuffer. 52 func (b *writeBuffer) init(bytecount *metric.Counter) { 53 b.bytecount = bytecount 54 b.textFormatter = tree.NewFmtCtx(tree.FmtPgwireText) 55 b.simpleFormatter = tree.NewFmtCtx(tree.FmtSimple) 56 } 57 58 // Write implements the io.Write interface. 59 func (b *writeBuffer) Write(p []byte) (int, error) { 60 b.write(p) 61 return len(p), b.err 62 } 63 64 func (b *writeBuffer) writeByte(c byte) { 65 if b.err == nil { 66 b.err = b.wrapped.WriteByte(c) 67 } 68 } 69 70 func (b *writeBuffer) write(p []byte) { 71 if b.err == nil { 72 _, b.err = b.wrapped.Write(p) 73 } 74 } 75 76 func (b *writeBuffer) writeString(s string) { 77 if b.err == nil { 78 _, b.err = b.wrapped.WriteString(s) 79 } 80 } 81 82 func (b *writeBuffer) nullTerminate() { 83 if b.err == nil { 84 b.err = b.wrapped.WriteByte(0) 85 } 86 } 87 88 // WriteFromFmtCtx writes the current contents of 89 // the given formatter with a length prefix. 90 // The function resets the contents of the formatter. 91 func (b *writeBuffer) writeFromFmtCtx(fmtCtx *tree.FmtCtx) { 92 if b.err == nil { 93 b.putInt32(int32(fmtCtx.Buffer.Len())) 94 95 // bytes.Buffer.WriteTo resets the Buffer. 96 _, b.err = fmtCtx.Buffer.WriteTo(&b.wrapped) 97 } 98 } 99 100 // writeLengthPrefixedBuffer writes the contents of a bytes.Buffer with a 101 // length prefix. 102 func (b *writeBuffer) writeLengthPrefixedBuffer(buf *bytes.Buffer) { 103 if b.err == nil { 104 b.putInt32(int32(buf.Len())) 105 106 // bytes.Buffer.WriteTo resets the Buffer. 107 _, b.err = buf.WriteTo(&b.wrapped) 108 } 109 } 110 111 // writeLengthPrefixedString writes a length-prefixed string. The 112 // length is encoded as an int32. 113 func (b *writeBuffer) writeLengthPrefixedString(s string) { 114 b.putInt32(int32(len(s))) 115 b.writeString(s) 116 } 117 118 // writeLengthPrefixedDatum writes a length-prefixed Datum in its 119 // string representation. The length is encoded as an int32. 120 func (b *writeBuffer) writeLengthPrefixedDatum(d tree.Datum) { 121 b.simpleFormatter.FormatNode(d) 122 b.writeFromFmtCtx(b.simpleFormatter) 123 } 124 125 // writeTerminatedString writes a null-terminated string. 126 func (b *writeBuffer) writeTerminatedString(s string) { 127 b.writeString(s) 128 b.nullTerminate() 129 } 130 131 func (b *writeBuffer) putInt16(v int16) { 132 if b.err == nil { 133 binary.BigEndian.PutUint16(b.putbuf[:], uint16(v)) 134 _, b.err = b.wrapped.Write(b.putbuf[:2]) 135 } 136 } 137 138 func (b *writeBuffer) putInt32(v int32) { 139 if b.err == nil { 140 binary.BigEndian.PutUint32(b.putbuf[:], uint32(v)) 141 _, b.err = b.wrapped.Write(b.putbuf[:4]) 142 } 143 } 144 145 func (b *writeBuffer) putInt64(v int64) { 146 if b.err == nil { 147 binary.BigEndian.PutUint64(b.putbuf[:], uint64(v)) 148 _, b.err = b.wrapped.Write(b.putbuf[:8]) 149 } 150 } 151 152 func (b *writeBuffer) putErrFieldMsg(field pgwirebase.ServerErrFieldType) { 153 if b.err == nil { 154 b.err = b.wrapped.WriteByte(byte(field)) 155 } 156 } 157 158 func (b *writeBuffer) reset() { 159 b.wrapped.Reset() 160 b.err = nil 161 } 162 163 // initMsg begins writing a message into the writeBuffer with the provided type. 164 func (b *writeBuffer) initMsg(typ pgwirebase.ServerMessageType) { 165 b.reset() 166 b.putbuf[0] = byte(typ) 167 _, b.err = b.wrapped.Write(b.putbuf[:5]) // message type + message length 168 } 169 170 // finishMsg attempts to write the data it has accumulated to the provided io.Writer. 171 // If the writeBuffer previously encountered an error since the last call to initMsg, 172 // or if it encounters an error while writing to w, it will return an error. 173 func (b *writeBuffer) finishMsg(w io.Writer) error { 174 defer b.reset() 175 if b.err != nil { 176 return b.err 177 } 178 bytes := b.wrapped.Bytes() 179 binary.BigEndian.PutUint32(bytes[1:5], uint32(b.wrapped.Len()-1)) 180 n, err := w.Write(bytes) 181 b.bytecount.Inc(int64(n)) 182 return err 183 } 184 185 // setError sets the writeBuffer's error, if it does not already have one. 186 func (b *writeBuffer) setError(err error) { 187 if b.err == nil { 188 b.err = err 189 } 190 }