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  }