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 }