685 lines
22 KiB
Go
685 lines
22 KiB
Go
// Copyright 2021 Dolthub, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package val
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/cockroachdb/apd/v3"
|
|
"github.com/dolthub/go-mysql-server/sql/analyzer/analyzererrors"
|
|
|
|
"github.com/dolthub/dolt/go/store/hash"
|
|
"github.com/dolthub/dolt/go/store/pool"
|
|
)
|
|
|
|
const (
|
|
builderBufferSize = 128
|
|
)
|
|
|
|
// OrdinalMapping is a mapping from one field ordering to another.
|
|
// It's used to construct index tuples from another index's tuples.
|
|
type OrdinalMapping []int
|
|
|
|
// NewIdentityOrdinalMapping returns a new OrdinalMapping that maps every ordinal to itself.
|
|
func NewIdentityOrdinalMapping(size int) OrdinalMapping {
|
|
newMapping := make(OrdinalMapping, size)
|
|
for i := 0; i < size; i++ {
|
|
newMapping[i] = i
|
|
}
|
|
return newMapping
|
|
}
|
|
|
|
// MapOrdinal returns the ordinal of the field in the source tuple that maps to the |to| ordinal in the destination tuple.
|
|
func (om OrdinalMapping) MapOrdinal(to int) (from int) {
|
|
from = om[to]
|
|
return
|
|
}
|
|
|
|
// IsIdentityMapping returns true if this mapping is the identity mapping (i.e. every position is mapped
|
|
// to the same position and no columns are reordered).
|
|
func (om OrdinalMapping) IsIdentityMapping() bool {
|
|
for i, mapping := range om {
|
|
if i != mapping {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
var DefaultTupleLengthTarget uint16 = (1 << 11)
|
|
|
|
type TupleBuilder struct {
|
|
vs ValueStore
|
|
Desc *TupleDesc
|
|
fields [][]byte
|
|
buf []byte
|
|
pos int64
|
|
tupleLengthTarget uint16 // The max tuple length before the tuple builder attempts to store values out-of-band.
|
|
outOfBandSize int64 // The size of the tuple if every adaptive value is stored out-of-band
|
|
inlineSize int64 // The size of the tuple if every adaptive value is inlined
|
|
}
|
|
|
|
func NewTupleBuilder(desc *TupleDesc, vs ValueStore) *TupleBuilder {
|
|
return &TupleBuilder{
|
|
Desc: desc,
|
|
fields: make([][]byte, len(desc.Types)),
|
|
buf: make([]byte, builderBufferSize),
|
|
vs: vs,
|
|
tupleLengthTarget: DefaultTupleLengthTarget,
|
|
}
|
|
}
|
|
|
|
func (tb *TupleBuilder) WithMaxRowSize(target uint16) *TupleBuilder {
|
|
other := *tb
|
|
other.tupleLengthTarget = target
|
|
return &other
|
|
}
|
|
|
|
// Build materializes a Tuple from the fields written to the TupleBuilder.
|
|
func (tb *TupleBuilder) Build(ctx context.Context, pool pool.BuffPool) (tup Tuple, err error) {
|
|
for i, typ := range tb.Desc.Types {
|
|
if !typ.Nullable && tb.fields[i] == nil {
|
|
panic("cannot write NULL to non-NULL field: " + strconv.Itoa(i))
|
|
}
|
|
}
|
|
return tb.BuildPermissive(ctx, pool)
|
|
}
|
|
|
|
// BuildPermissive materializes a Tuple from the fields
|
|
// written to the TupleBuilder without validating nullability.
|
|
func (tb *TupleBuilder) BuildPermissive(ctx context.Context, pool pool.BuffPool) (tup Tuple, err error) {
|
|
// Values may get passed into the tuple builder in either in-band or out-of-band form.
|
|
// In the best case, we don't need to convert any of them, so the TupleBuilder initially stores them in the form they're given.
|
|
// But we track the tuple size if they're all inlined vs the tuple size if they're all out-of-band,
|
|
// Then use this to determine which values need to be stored out of band.
|
|
|
|
type adaptiveCandidate struct {
|
|
columnIndex int
|
|
savings int64 // inlineSize - outOfBandSize
|
|
}
|
|
totalSize := tb.inlineSize
|
|
if totalSize > int64(tb.tupleLengthTarget) {
|
|
// Collect all adaptive columns that would benefit from out-of-band storage,
|
|
// then sort by savings descending so we move the largest values out-of-band first.
|
|
var candidates []adaptiveCandidate
|
|
for i, descType := range tb.Desc.Types {
|
|
if IsAdaptiveEncoding(descType.Enc) {
|
|
adaptiveValue := AdaptiveValue(tb.fields[i])
|
|
if adaptiveValue.IsNull() {
|
|
continue
|
|
}
|
|
outOfBandSize := adaptiveValue.outOfBandSize()
|
|
inlineSize := adaptiveValue.inlineSize()
|
|
savings := inlineSize - outOfBandSize
|
|
if savings > 0 {
|
|
candidates = append(candidates, adaptiveCandidate{columnIndex: i, savings: savings})
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort candidates by savings descending (largest values first).
|
|
// Use stable sort to preserve column order for equal-sized values.
|
|
sort.SliceStable(candidates, func(a, b int) bool {
|
|
return candidates[a].savings > candidates[b].savings
|
|
})
|
|
|
|
// Convert to out-of-band in order of largest savings until we're under the target.
|
|
outOfBandSet := make(map[int]bool)
|
|
for _, c := range candidates {
|
|
adaptiveValue := AdaptiveValue(tb.fields[c.columnIndex])
|
|
if !adaptiveValue.IsOutOfBand() {
|
|
outOfBandValue, err := adaptiveValue.convertToOutOfBand(ctx, tb.vs, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tb.PutRaw(c.columnIndex, outOfBandValue)
|
|
}
|
|
outOfBandSet[c.columnIndex] = true
|
|
totalSize -= c.savings
|
|
if totalSize <= int64(tb.tupleLengthTarget) {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Ensure any remaining adaptive columns that were not selected for out-of-band are inline.
|
|
for i, descType := range tb.Desc.Types {
|
|
if IsAdaptiveEncoding(descType.Enc) && !outOfBandSet[i] {
|
|
adaptiveValue := AdaptiveValue(tb.fields[i])
|
|
if adaptiveValue.IsOutOfBand() {
|
|
inline, err := adaptiveValue.convertToInline(ctx, tb.vs, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tb.PutRaw(i, inline)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
values := tb.fields[:tb.Desc.Count()]
|
|
tup = NewTuple(pool, values...)
|
|
tb.Recycle()
|
|
return tup, nil
|
|
}
|
|
|
|
// BuildPrefix materializes a prefix Tuple from the first |k| fields written to the TupleBuilder.
|
|
func (tb *TupleBuilder) BuildPrefix(pool pool.BuffPool, k int) (tup Tuple) {
|
|
for i, typ := range tb.Desc.Types[:k] {
|
|
if !typ.Nullable && tb.fields[i] == nil {
|
|
panic("cannot write NULL to non-NULL field")
|
|
}
|
|
}
|
|
values := tb.fields[:k]
|
|
tup = NewTuple(pool, values...)
|
|
tb.Recycle()
|
|
return
|
|
}
|
|
|
|
// BuildPrefixNoRecycle materializes a prefix Tuple from the first |k| fields
|
|
// but does not call Recycle.
|
|
func (tb *TupleBuilder) BuildPrefixNoRecycle(pool pool.BuffPool, k int) (tup Tuple) {
|
|
for i, typ := range tb.Desc.Types[:k] {
|
|
if !typ.Nullable && tb.fields[i] == nil {
|
|
panic("cannot write NULL to non-NULL field")
|
|
}
|
|
}
|
|
values := tb.fields[:k]
|
|
tup = NewTuple(pool, values...)
|
|
return
|
|
}
|
|
|
|
// Recycle resets the TupleBuilder so it can build a new Tuple.
|
|
func (tb *TupleBuilder) Recycle() {
|
|
for i := 0; i < tb.Desc.Count(); i++ {
|
|
tb.fields[i] = nil
|
|
}
|
|
tb.pos = 0
|
|
tb.inlineSize = 0
|
|
tb.outOfBandSize = 0
|
|
}
|
|
|
|
func (tb *TupleBuilder) addSize(sz ByteSize) {
|
|
tb.inlineSize += int64(sz)
|
|
tb.outOfBandSize += int64(sz)
|
|
}
|
|
|
|
// PutBool writes a bool to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutBool(i int, v bool) {
|
|
tb.Desc.ExpectEncoding(i, Int8Enc)
|
|
tb.ensureCapacity(int8Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(int8Size)]
|
|
writeBool(tb.fields[i], v)
|
|
tb.pos += int64(int8Size)
|
|
tb.addSize(int8Size)
|
|
}
|
|
|
|
// PutInt8 writes an int8 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutInt8(i int, v int8) {
|
|
tb.Desc.ExpectEncoding(i, Int8Enc)
|
|
tb.ensureCapacity(int8Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(int8Size)]
|
|
writeInt8(tb.fields[i], v)
|
|
tb.pos += int64(int8Size)
|
|
tb.addSize(int8Size)
|
|
}
|
|
|
|
// PutUint8 writes a uint8 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutUint8(i int, v uint8) {
|
|
tb.Desc.ExpectEncoding(i, Uint8Enc)
|
|
tb.ensureCapacity(uint8Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(1)]
|
|
writeUint8(tb.fields[i], v)
|
|
tb.pos += int64(uint8Size)
|
|
tb.addSize(int8Size)
|
|
}
|
|
|
|
// PutInt16 writes an int16 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutInt16(i int, v int16) {
|
|
tb.Desc.ExpectEncoding(i, Int16Enc)
|
|
tb.ensureCapacity(int16Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(int16Size)]
|
|
writeInt16(tb.fields[i], v)
|
|
tb.pos += int64(int16Size)
|
|
tb.addSize(int16Size)
|
|
}
|
|
|
|
// PutUint16 writes a uint16 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutUint16(i int, v uint16) {
|
|
tb.Desc.ExpectEncoding(i, Uint16Enc)
|
|
tb.ensureCapacity(uint16Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(uint16Size)]
|
|
WriteUint16(tb.fields[i], v)
|
|
tb.pos += int64(uint16Size)
|
|
tb.addSize(int16Size)
|
|
}
|
|
|
|
// PutInt32 writes an int32 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutInt32(i int, v int32) {
|
|
tb.Desc.ExpectEncoding(i, Int32Enc)
|
|
tb.ensureCapacity(int32Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(int32Size)]
|
|
writeInt32(tb.fields[i], v)
|
|
tb.pos += int64(int32Size)
|
|
tb.addSize(int32Size)
|
|
}
|
|
|
|
// PutUint32 writes a uint32 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutUint32(i int, v uint32) {
|
|
tb.Desc.ExpectEncoding(i, Uint32Enc)
|
|
tb.ensureCapacity(uint32Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(uint32Size)]
|
|
writeUint32(tb.fields[i], v)
|
|
tb.pos += int64(uint32Size)
|
|
tb.addSize(int32Size)
|
|
}
|
|
|
|
// PutInt64 writes an int64 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutInt64(i int, v int64) {
|
|
tb.Desc.ExpectEncoding(i, Int64Enc)
|
|
tb.ensureCapacity(int64Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(int64Size)]
|
|
writeInt64(tb.fields[i], v)
|
|
tb.pos += int64(int64Size)
|
|
tb.addSize(int64Size)
|
|
}
|
|
|
|
// PutUint64 writes a uint64 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutUint64(i int, v uint64) {
|
|
tb.Desc.ExpectEncoding(i, Uint64Enc)
|
|
tb.ensureCapacity(uint64Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(uint64Size)]
|
|
writeUint64(tb.fields[i], v)
|
|
tb.pos += int64(uint64Size)
|
|
tb.addSize(int64Size)
|
|
}
|
|
|
|
// PutFloat32 writes a float32 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutFloat32(i int, v float32) {
|
|
tb.Desc.ExpectEncoding(i, Float32Enc)
|
|
tb.ensureCapacity(float32Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(float32Size)]
|
|
writeFloat32(tb.fields[i], v)
|
|
tb.pos += int64(float32Size)
|
|
tb.addSize(float32Size)
|
|
}
|
|
|
|
// PutFloat64 writes a float64 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutFloat64(i int, v float64) {
|
|
tb.Desc.ExpectEncoding(i, Float64Enc)
|
|
tb.ensureCapacity(float64Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(float64Size)]
|
|
writeFloat64(tb.fields[i], v)
|
|
tb.pos += int64(float64Size)
|
|
tb.addSize(float64Size)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutBit(i int, v uint64) {
|
|
tb.Desc.ExpectEncoding(i, Bit64Enc)
|
|
tb.ensureCapacity(bit64Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(bit64Size)]
|
|
writeBit64(tb.fields[i], v)
|
|
tb.pos += int64(bit64Size)
|
|
tb.addSize(bit64Size)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutDecimal(i int, v *apd.Decimal) {
|
|
tb.Desc.ExpectEncoding(i, DecimalEnc)
|
|
sz := sizeOfDecimal(v)
|
|
tb.ensureCapacity(sz)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(sz)]
|
|
writeDecimal(tb.fields[i], v)
|
|
tb.pos += int64(sz)
|
|
tb.addSize(sz)
|
|
}
|
|
|
|
// PutYear writes an int16-encoded year to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutYear(i int, v int16) {
|
|
tb.Desc.ExpectEncoding(i, YearEnc)
|
|
tb.ensureCapacity(yearSize)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(yearSize)]
|
|
writeYear(tb.fields[i], v)
|
|
tb.pos += int64(int16Size)
|
|
tb.addSize(int16Size)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutDate(i int, v time.Time) {
|
|
tb.Desc.ExpectEncoding(i, DateEnc)
|
|
tb.ensureCapacity(dateSize)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(dateSize)]
|
|
writeDate(tb.fields[i], v)
|
|
tb.pos += int64(dateSize)
|
|
tb.addSize(dateSize)
|
|
}
|
|
|
|
// PutSqlTime writes a string to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutSqlTime(i int, v int64) {
|
|
tb.Desc.ExpectEncoding(i, TimeEnc)
|
|
tb.ensureCapacity(timeSize)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(timeSize)]
|
|
writeTime(tb.fields[i], v)
|
|
tb.pos += int64(timeSize)
|
|
tb.addSize(timeSize)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutDatetime(i int, v time.Time) {
|
|
tb.Desc.ExpectEncoding(i, DatetimeEnc)
|
|
tb.ensureCapacity(datetimeSize)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(datetimeSize)]
|
|
writeDatetime(tb.fields[i], v)
|
|
tb.pos += int64(datetimeSize)
|
|
tb.addSize(datetimeSize)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutEnum(i int, v uint16) {
|
|
tb.Desc.ExpectEncoding(i, EnumEnc)
|
|
tb.ensureCapacity(enumSize)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(enumSize)]
|
|
writeEnum(tb.fields[i], v)
|
|
tb.pos += int64(enumSize)
|
|
tb.addSize(enumSize)
|
|
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutSet(i int, v uint64) {
|
|
tb.Desc.ExpectEncoding(i, SetEnc)
|
|
tb.ensureCapacity(setSize)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(setSize)]
|
|
writeSet(tb.fields[i], v)
|
|
tb.pos += int64(setSize)
|
|
tb.addSize(setSize)
|
|
|
|
}
|
|
|
|
// PutString writes a string to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutString(i int, v string) error {
|
|
tb.Desc.ExpectEncoding(i, StringEnc)
|
|
sz := ByteSize(len(v)) + 1
|
|
offSz := 0
|
|
if i > 0 {
|
|
offSz = 2 * int(uint16Size)
|
|
}
|
|
if int(tb.pos)+len(v)+offSz > int(MaxTupleDataSize) {
|
|
return analyzererrors.ErrInvalidRowLength.New(MaxTupleDataSize, int(tb.pos)+len(v)+int(offsetsSize(i)))
|
|
}
|
|
tb.ensureCapacity(sz)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(sz)]
|
|
writeString(tb.fields[i], v)
|
|
tb.pos += int64(sz)
|
|
tb.addSize(sz)
|
|
return nil
|
|
}
|
|
|
|
// PutByteString writes a []byte to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutByteString(i int, v []byte) {
|
|
tb.Desc.ExpectEncoding(i, ByteStringEnc)
|
|
sz := ByteSize(len(v)) + 1
|
|
tb.ensureCapacity(sz)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(sz)]
|
|
writeByteString(tb.fields[i], v)
|
|
tb.pos += int64(sz)
|
|
tb.addSize(sz)
|
|
}
|
|
|
|
// PutJSON writes a []byte to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutJSON(i int, v []byte) {
|
|
tb.Desc.ExpectEncoding(i, JSONEnc)
|
|
sz := ByteSize(len(v)) + 1
|
|
tb.ensureCapacity(sz)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(sz)]
|
|
writeByteString(tb.fields[i], v)
|
|
tb.pos += int64(sz)
|
|
tb.addSize(sz)
|
|
}
|
|
|
|
// PutGeometry writes a []byte to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutGeometry(i int, v []byte) {
|
|
tb.Desc.ExpectEncoding(i, GeometryEnc)
|
|
sz := ByteSize(len(v)) + 1
|
|
tb.ensureCapacity(sz)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(sz)]
|
|
writeByteString(tb.fields[i], v)
|
|
tb.pos += int64(sz)
|
|
tb.addSize(sz)
|
|
}
|
|
|
|
// PutGeometryAddr writes a Geometry's address ref to the ith field
|
|
func (tb *TupleBuilder) PutGeometryAddr(i int, v hash.Hash) {
|
|
tb.Desc.ExpectEncoding(i, GeomAddrEnc)
|
|
tb.ensureCapacity(hash.ByteLen)
|
|
tb.putAddr(i, v)
|
|
}
|
|
|
|
// PutHash128 writes a hash128 to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutHash128(i int, v []byte) {
|
|
tb.Desc.ExpectEncoding(i, Hash128Enc)
|
|
tb.ensureCapacity(hash128Size)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(hash128Size)]
|
|
writeHash128(tb.fields[i], v)
|
|
tb.pos += int64(hash128Size)
|
|
tb.addSize(hash128Size)
|
|
}
|
|
|
|
// PutExtended writes a []byte to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutExtended(i int, v []byte) {
|
|
tb.Desc.ExpectEncoding(i, ExtendedEnc)
|
|
sz := ByteSize(len(v))
|
|
tb.ensureCapacity(sz)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(sz)]
|
|
writeExtended(tb.Desc.Handlers[i], tb.fields[i], v)
|
|
tb.pos += int64(sz)
|
|
tb.addSize(sz)
|
|
}
|
|
|
|
// PutExtendedAddr writes a []byte to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutExtendedAddr(i int, v hash.Hash) {
|
|
tb.Desc.ExpectEncoding(i, ExtendedAddrEnc)
|
|
tb.ensureCapacity(hash.ByteLen)
|
|
tb.putAddr(i, v)
|
|
}
|
|
|
|
// PutRaw writes a []byte to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutRaw(i int, buf []byte) {
|
|
if buf == nil {
|
|
// todo(andy): does it make sense to
|
|
// allow/expect nulls here?
|
|
return
|
|
}
|
|
sz := ByteSize(len(buf))
|
|
tb.ensureCapacity(sz)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(sz)]
|
|
writeRaw(tb.fields[i], buf)
|
|
tb.pos += int64(sz)
|
|
tb.addSize(sz)
|
|
}
|
|
|
|
// PutCommitAddr writes a commit's address ref to the ith field
|
|
// of the Tuple being built.
|
|
func (tb *TupleBuilder) PutCommitAddr(i int, v hash.Hash) {
|
|
tb.Desc.ExpectEncoding(i, CommitAddrEnc)
|
|
tb.ensureCapacity(hash.ByteLen)
|
|
tb.putAddr(i, v)
|
|
}
|
|
|
|
// PutBytesAddr writes a blob's address ref to the ith field
|
|
// of the Tuple being built.
|
|
func (tb *TupleBuilder) PutBytesAddr(i int, v hash.Hash) {
|
|
tb.Desc.ExpectEncoding(i, BytesAddrEnc)
|
|
tb.ensureCapacity(hash.ByteLen)
|
|
tb.putAddr(i, v)
|
|
}
|
|
|
|
// PutStringAddr writes a string's address ref to the ith field
|
|
// of the Tuple being built.
|
|
func (tb *TupleBuilder) PutStringAddr(i int, v hash.Hash) {
|
|
tb.Desc.ExpectEncoding(i, StringAddrEnc)
|
|
tb.ensureCapacity(hash.ByteLen)
|
|
tb.putAddr(i, v)
|
|
}
|
|
|
|
// PutJSONAddr writes a JSON string's address ref to the ith field
|
|
// of the Tuple being built.
|
|
func (tb *TupleBuilder) PutJSONAddr(i int, v hash.Hash) {
|
|
tb.Desc.ExpectEncoding(i, JSONAddrEnc)
|
|
tb.ensureCapacity(hash.ByteLen)
|
|
tb.putAddr(i, v)
|
|
}
|
|
|
|
func (tb *TupleBuilder) putAddr(i int, v hash.Hash) {
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(hash.ByteLen)]
|
|
writeAddr(tb.fields[i], v[:])
|
|
tb.pos += int64(hash.ByteLen)
|
|
tb.addSize(hash.ByteLen)
|
|
}
|
|
|
|
func (tb *TupleBuilder) ensureCapacity(sz ByteSize) {
|
|
need := int(tb.pos+int64(sz)) - len(tb.buf)
|
|
if need > 0 {
|
|
for i := 0; i < need; i++ {
|
|
tb.buf = append(tb.buf, byte(0))
|
|
}
|
|
}
|
|
}
|
|
|
|
// PutCell writes a Cell to the ith field of the Tuple being built.
|
|
func (tb *TupleBuilder) PutCell(i int, v Cell) {
|
|
tb.Desc.ExpectEncoding(i, CellEnc)
|
|
tb.ensureCapacity(cellSize)
|
|
tb.fields[i] = tb.buf[tb.pos : tb.pos+int64(cellSize)]
|
|
writeCell(tb.fields[i], v)
|
|
tb.pos += int64(cellSize)
|
|
tb.addSize(cellSize)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveBytesFromInline(ctx context.Context, i int, v []byte) error {
|
|
tb.Desc.ExpectEncoding(i, BytesAdaptiveEnc)
|
|
return tb.PutAdaptiveFromInline(ctx, i, v)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveStringFromInline(ctx context.Context, i int, s string) error {
|
|
tb.Desc.ExpectEncoding(i, StringAdaptiveEnc)
|
|
return tb.PutAdaptiveFromInline(ctx, i, []byte(s))
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveGeomFromInline(ctx context.Context, i int, v []byte) error {
|
|
tb.Desc.ExpectEncoding(i, GeomAdaptiveEnc)
|
|
return tb.PutAdaptiveFromInline(ctx, i, v)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveJsonFromInline(ctx context.Context, i int, v []byte) error {
|
|
tb.Desc.ExpectEncoding(i, JsonAdaptiveEnc)
|
|
return tb.PutAdaptiveFromInline(ctx, i, v)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveExtendedFromInline(ctx context.Context, i int, v []byte) error {
|
|
tb.Desc.ExpectEncoding(i, ExtendedAdaptiveEnc)
|
|
return tb.PutAdaptiveFromInline(ctx, i, v)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveFromInline(ctx context.Context, i int, v []byte) error {
|
|
|
|
inlineSize := int64(len(v) + 1) // include extra header byte
|
|
if inlineSize > int64(tb.tupleLengthTarget) {
|
|
// Inline value is too large. We must store it out-of-band.
|
|
tb.ensureCapacity(maxOutOfBandAdaptiveValueLength)
|
|
blobLength := uint64(len(v))
|
|
lengthSize, _ := makeVarInt(blobLength, tb.buf[tb.pos:])
|
|
outOfBandSize := lengthSize + hash.ByteLen
|
|
|
|
blobHash, err := tb.vs.WriteBytes(ctx, []byte(v))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
copy(tb.buf[tb.pos+int64(lengthSize):], blobHash[:])
|
|
field := tb.buf[tb.pos : tb.pos+int64(outOfBandSize)]
|
|
tb.fields[i] = field
|
|
tb.pos += int64(outOfBandSize)
|
|
tb.inlineSize += inlineSize
|
|
tb.outOfBandSize += int64(outOfBandSize)
|
|
return nil
|
|
}
|
|
|
|
// inlineSize <= tb.tupleLengthTarget, so it must fit within a 16-bit ByteSize
|
|
tb.ensureCapacity(ByteSize(inlineSize))
|
|
field := AdaptiveValue(tb.buf[tb.pos : tb.pos+inlineSize])
|
|
tb.fields[i] = field
|
|
field[0] = 0 // Mark this as inline
|
|
copy(field[1:], v)
|
|
tb.pos += inlineSize
|
|
tb.inlineSize += inlineSize
|
|
tb.outOfBandSize += field.outOfBandSize()
|
|
return nil
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveValue(ctx context.Context, vs ValueStore, i int, v AdaptiveValue) error {
|
|
if v.getMessageLength() > int64(tb.tupleLengthTarget) {
|
|
// The message won't fit inlined, so premptively store it out-of-band
|
|
byteArray, err := v.convertToByteArray(ctx, vs, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tb.PutAdaptiveFromOutline(i, byteArray.maxByteLength, byteArray.Addr)
|
|
return nil
|
|
} else {
|
|
bytes, err := v.getUnderlyingBytes(ctx, vs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return tb.PutAdaptiveFromInline(ctx, i, bytes)
|
|
}
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveExtendedFromOutline(i int, v *ExtendedValueWrapper) {
|
|
tb.Desc.ExpectEncoding(i, ExtendedAdaptiveEnc)
|
|
tb.PutAdaptiveFromOutline(i, v.outOfBandLength, v.Addr)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveGeomFromOutOfBand(i int, maxByteLength int64, addr hash.Hash) {
|
|
tb.Desc.ExpectEncoding(i, GeomAdaptiveEnc)
|
|
tb.PutAdaptiveFromOutline(i, maxByteLength, addr)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveJsonFromOutline(i int, v *JsonAdaptiveStorage) {
|
|
tb.Desc.ExpectEncoding(i, JsonAdaptiveEnc)
|
|
tb.PutAdaptiveFromOutline(i, v.MaxByteLength(), v.Addr())
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveBytesFromOutline(i int, v *ByteArray) {
|
|
tb.Desc.ExpectEncoding(i, BytesAdaptiveEnc)
|
|
tb.PutAdaptiveFromOutline(i, v.maxByteLength, v.Addr)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveStringFromOutline(i int, v *TextStorage) {
|
|
tb.Desc.ExpectEncoding(i, StringAdaptiveEnc)
|
|
tb.PutAdaptiveFromOutline(i, v.maxByteLength, v.Addr)
|
|
}
|
|
|
|
func (tb *TupleBuilder) PutAdaptiveFromOutline(i int, maxByteLength int64, addr hash.Hash) {
|
|
|
|
maxLengthBytes := 9
|
|
tb.ensureCapacity(ByteSize(hash.ByteLen + maxLengthBytes))
|
|
blobLength := uint64(maxByteLength)
|
|
lengthSize, _ := makeVarInt(blobLength, tb.buf[tb.pos:])
|
|
outOfBandSize := int64(lengthSize) + hash.ByteLen
|
|
|
|
copy(tb.buf[tb.pos+int64(lengthSize):], addr[:])
|
|
field := tb.buf[tb.pos : tb.pos+outOfBandSize]
|
|
tb.fields[i] = field
|
|
tb.pos += outOfBandSize
|
|
tb.inlineSize += int64(blobLength) + 1
|
|
tb.outOfBandSize += outOfBandSize
|
|
}
|