github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/flowinfra/stream_decoder.go (about)

     1  // Copyright 2016 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 flowinfra
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    19  	"github.com/cockroachdb/errors"
    20  )
    21  
    22  // StreamDecoder converts a sequence of ProducerMessage to rows and metadata
    23  // records.
    24  //
    25  // Sample usage:
    26  //   sd := StreamDecoder{}
    27  //   var row sqlbase.EncDatumRow
    28  //   for each message in stream {
    29  //       err := sd.AddMessage(msg)
    30  //       if err != nil { ... }
    31  //       for {
    32  //           row, meta, err := sd.GetRow(row)
    33  //           if err != nil { ... }
    34  //           if row == nil && meta.Empty() {
    35  //               // No more rows in this message.
    36  //               break
    37  //           }
    38  //           // Use <row>
    39  //           ...
    40  //       }
    41  //   }
    42  //
    43  // AddMessage can be called multiple times before getting the rows, but this
    44  // will cause data to accumulate internally.
    45  type StreamDecoder struct {
    46  	typing       []execinfrapb.DatumInfo
    47  	data         []byte
    48  	numEmptyRows int
    49  	metadata     []execinfrapb.ProducerMetadata
    50  	rowAlloc     sqlbase.EncDatumRowAlloc
    51  
    52  	headerReceived bool
    53  	typingReceived bool
    54  }
    55  
    56  // AddMessage adds the data in a ProducerMessage to the decoder.
    57  //
    58  // The StreamDecoder may keep a reference to msg.Data.RawBytes and
    59  // msg.Data.Metadata until all the rows in the message are retrieved with GetRow.
    60  //
    61  // If an error is returned, no records have been buffered in the StreamDecoder.
    62  func (sd *StreamDecoder) AddMessage(ctx context.Context, msg *execinfrapb.ProducerMessage) error {
    63  	if msg.Header != nil {
    64  		if sd.headerReceived {
    65  			return errors.Errorf("received multiple headers")
    66  		}
    67  		sd.headerReceived = true
    68  	}
    69  	if msg.Typing != nil {
    70  		if sd.typingReceived {
    71  			return errors.Errorf("typing information received multiple times")
    72  		}
    73  		sd.typingReceived = true
    74  		sd.typing = msg.Typing
    75  	}
    76  
    77  	if len(msg.Data.RawBytes) > 0 {
    78  		if !sd.headerReceived || !sd.typingReceived {
    79  			return errors.Errorf("received data before header and/or typing info")
    80  		}
    81  
    82  		if len(sd.data) == 0 {
    83  			// We limit the capacity of the slice (using "three-index slices") out of
    84  			// paranoia: if the slice is going to need to grow later, we don't want to
    85  			// clobber any memory outside what the protobuf allocated for us
    86  			// initially (in case this memory might be coming from some buffer).
    87  			sd.data = msg.Data.RawBytes[:len(msg.Data.RawBytes):len(msg.Data.RawBytes)]
    88  		} else {
    89  			// This can only happen if we don't retrieve all the rows before
    90  			// adding another message, which shouldn't be the normal case.
    91  			// TODO(radu): maybe don't support this case at all?
    92  			sd.data = append(sd.data, msg.Data.RawBytes...)
    93  		}
    94  	}
    95  	if msg.Data.NumEmptyRows > 0 {
    96  		if len(msg.Data.RawBytes) > 0 {
    97  			return errors.Errorf("received both data and empty rows")
    98  		}
    99  		sd.numEmptyRows += int(msg.Data.NumEmptyRows)
   100  	}
   101  	if len(msg.Data.Metadata) > 0 {
   102  		for _, md := range msg.Data.Metadata {
   103  			meta, ok := execinfrapb.RemoteProducerMetaToLocalMeta(ctx, md)
   104  			if !ok {
   105  				// Unknown metadata, ignore.
   106  				continue
   107  			}
   108  			sd.metadata = append(sd.metadata, meta)
   109  		}
   110  	}
   111  	return nil
   112  }
   113  
   114  // GetRow returns a row received in the stream. A row buffer can be provided
   115  // optionally.
   116  //
   117  // Returns an empty row if there are no more rows received so far.
   118  //
   119  // A decoding error may be returned. Note that these are separate from error
   120  // coming from the upstream (through ProducerMetadata.Err).
   121  func (sd *StreamDecoder) GetRow(
   122  	rowBuf sqlbase.EncDatumRow,
   123  ) (sqlbase.EncDatumRow, *execinfrapb.ProducerMetadata, error) {
   124  	if len(sd.metadata) != 0 {
   125  		r := &sd.metadata[0]
   126  		sd.metadata = sd.metadata[1:]
   127  		return nil, r, nil
   128  	}
   129  
   130  	if sd.numEmptyRows > 0 {
   131  		sd.numEmptyRows--
   132  		row := make(sqlbase.EncDatumRow, 0) // this doesn't actually allocate.
   133  		return row, nil, nil
   134  	}
   135  
   136  	if len(sd.data) == 0 {
   137  		return nil, nil, nil
   138  	}
   139  	rowLen := len(sd.typing)
   140  	if cap(rowBuf) >= rowLen {
   141  		rowBuf = rowBuf[:rowLen]
   142  	} else {
   143  		rowBuf = sd.rowAlloc.AllocRow(rowLen)
   144  	}
   145  	for i := range rowBuf {
   146  		var err error
   147  		rowBuf[i], sd.data, err = sqlbase.EncDatumFromBuffer(
   148  			sd.typing[i].Type, sd.typing[i].Encoding, sd.data,
   149  		)
   150  		if err != nil {
   151  			// Reset sd because it is no longer usable.
   152  			*sd = StreamDecoder{}
   153  			return nil, nil, err
   154  		}
   155  	}
   156  	return rowBuf, nil, nil
   157  }
   158  
   159  // Types returns the types of the columns; can only be used after we received at
   160  // least one row.
   161  func (sd *StreamDecoder) Types() []*types.T {
   162  	types := make([]*types.T, len(sd.typing))
   163  	for i := range types {
   164  		types[i] = sd.typing[i].Type
   165  	}
   166  	return types
   167  }