mirror of
https://github.com/dolthub/dolt.git
synced 2026-03-03 10:08:59 -06:00
New format blobs (#3588)
* [no-release-notes] New format blob read/write Use blob chunker to write blobs out of band. Tests for round tripping and address walking at the tree level. Run Blob enginetests and extra large file blob tests. * NodeStore ref refactors * fix index * run verify * fix more bats * small PR cleanup * more changes * last bats errors
This commit is contained in:
committed by
GitHub
parent
dbaaa8c516
commit
ac80ba7770
@@ -23,87 +23,90 @@ import (
|
||||
type Encoding byte
|
||||
|
||||
const (
|
||||
EncodingNull Encoding = 0
|
||||
EncodingInt8 Encoding = 1
|
||||
EncodingUint8 Encoding = 2
|
||||
EncodingInt16 Encoding = 3
|
||||
EncodingUint16 Encoding = 4
|
||||
EncodingInt32 Encoding = 7
|
||||
EncodingUint32 Encoding = 8
|
||||
EncodingInt64 Encoding = 9
|
||||
EncodingUint64 Encoding = 10
|
||||
EncodingFloat32 Encoding = 11
|
||||
EncodingFloat64 Encoding = 12
|
||||
EncodingBit64 Encoding = 13
|
||||
EncodingHash128 Encoding = 14
|
||||
EncodingYear Encoding = 15
|
||||
EncodingDate Encoding = 16
|
||||
EncodingTime Encoding = 17
|
||||
EncodingDatetime Encoding = 18
|
||||
EncodingEnum Encoding = 19
|
||||
EncodingSet Encoding = 20
|
||||
EncodingAddress Encoding = 21
|
||||
EncodingString Encoding = 128
|
||||
EncodingBytes Encoding = 129
|
||||
EncodingDecimal Encoding = 130
|
||||
EncodingJSON Encoding = 131
|
||||
EncodingGeometry Encoding = 133
|
||||
EncodingNull Encoding = 0
|
||||
EncodingInt8 Encoding = 1
|
||||
EncodingUint8 Encoding = 2
|
||||
EncodingInt16 Encoding = 3
|
||||
EncodingUint16 Encoding = 4
|
||||
EncodingInt32 Encoding = 7
|
||||
EncodingUint32 Encoding = 8
|
||||
EncodingInt64 Encoding = 9
|
||||
EncodingUint64 Encoding = 10
|
||||
EncodingFloat32 Encoding = 11
|
||||
EncodingFloat64 Encoding = 12
|
||||
EncodingBit64 Encoding = 13
|
||||
EncodingHash128 Encoding = 14
|
||||
EncodingYear Encoding = 15
|
||||
EncodingDate Encoding = 16
|
||||
EncodingTime Encoding = 17
|
||||
EncodingDatetime Encoding = 18
|
||||
EncodingEnum Encoding = 19
|
||||
EncodingSet Encoding = 20
|
||||
EncodingBytesAddr Encoding = 21
|
||||
EncodingCommitAddr Encoding = 22
|
||||
EncodingString Encoding = 128
|
||||
EncodingBytes Encoding = 129
|
||||
EncodingDecimal Encoding = 130
|
||||
EncodingJSON Encoding = 131
|
||||
EncodingGeometry Encoding = 133
|
||||
)
|
||||
|
||||
var EnumNamesEncoding = map[Encoding]string{
|
||||
EncodingNull: "Null",
|
||||
EncodingInt8: "Int8",
|
||||
EncodingUint8: "Uint8",
|
||||
EncodingInt16: "Int16",
|
||||
EncodingUint16: "Uint16",
|
||||
EncodingInt32: "Int32",
|
||||
EncodingUint32: "Uint32",
|
||||
EncodingInt64: "Int64",
|
||||
EncodingUint64: "Uint64",
|
||||
EncodingFloat32: "Float32",
|
||||
EncodingFloat64: "Float64",
|
||||
EncodingBit64: "Bit64",
|
||||
EncodingHash128: "Hash128",
|
||||
EncodingYear: "Year",
|
||||
EncodingDate: "Date",
|
||||
EncodingTime: "Time",
|
||||
EncodingDatetime: "Datetime",
|
||||
EncodingEnum: "Enum",
|
||||
EncodingSet: "Set",
|
||||
EncodingAddress: "Address",
|
||||
EncodingString: "String",
|
||||
EncodingBytes: "Bytes",
|
||||
EncodingDecimal: "Decimal",
|
||||
EncodingJSON: "JSON",
|
||||
EncodingGeometry: "Geometry",
|
||||
EncodingNull: "Null",
|
||||
EncodingInt8: "Int8",
|
||||
EncodingUint8: "Uint8",
|
||||
EncodingInt16: "Int16",
|
||||
EncodingUint16: "Uint16",
|
||||
EncodingInt32: "Int32",
|
||||
EncodingUint32: "Uint32",
|
||||
EncodingInt64: "Int64",
|
||||
EncodingUint64: "Uint64",
|
||||
EncodingFloat32: "Float32",
|
||||
EncodingFloat64: "Float64",
|
||||
EncodingBit64: "Bit64",
|
||||
EncodingHash128: "Hash128",
|
||||
EncodingYear: "Year",
|
||||
EncodingDate: "Date",
|
||||
EncodingTime: "Time",
|
||||
EncodingDatetime: "Datetime",
|
||||
EncodingEnum: "Enum",
|
||||
EncodingSet: "Set",
|
||||
EncodingBytesAddr: "BytesAddr",
|
||||
EncodingCommitAddr: "CommitAddr",
|
||||
EncodingString: "String",
|
||||
EncodingBytes: "Bytes",
|
||||
EncodingDecimal: "Decimal",
|
||||
EncodingJSON: "JSON",
|
||||
EncodingGeometry: "Geometry",
|
||||
}
|
||||
|
||||
var EnumValuesEncoding = map[string]Encoding{
|
||||
"Null": EncodingNull,
|
||||
"Int8": EncodingInt8,
|
||||
"Uint8": EncodingUint8,
|
||||
"Int16": EncodingInt16,
|
||||
"Uint16": EncodingUint16,
|
||||
"Int32": EncodingInt32,
|
||||
"Uint32": EncodingUint32,
|
||||
"Int64": EncodingInt64,
|
||||
"Uint64": EncodingUint64,
|
||||
"Float32": EncodingFloat32,
|
||||
"Float64": EncodingFloat64,
|
||||
"Bit64": EncodingBit64,
|
||||
"Hash128": EncodingHash128,
|
||||
"Year": EncodingYear,
|
||||
"Date": EncodingDate,
|
||||
"Time": EncodingTime,
|
||||
"Datetime": EncodingDatetime,
|
||||
"Enum": EncodingEnum,
|
||||
"Set": EncodingSet,
|
||||
"Address": EncodingAddress,
|
||||
"String": EncodingString,
|
||||
"Bytes": EncodingBytes,
|
||||
"Decimal": EncodingDecimal,
|
||||
"JSON": EncodingJSON,
|
||||
"Geometry": EncodingGeometry,
|
||||
"Null": EncodingNull,
|
||||
"Int8": EncodingInt8,
|
||||
"Uint8": EncodingUint8,
|
||||
"Int16": EncodingInt16,
|
||||
"Uint16": EncodingUint16,
|
||||
"Int32": EncodingInt32,
|
||||
"Uint32": EncodingUint32,
|
||||
"Int64": EncodingInt64,
|
||||
"Uint64": EncodingUint64,
|
||||
"Float32": EncodingFloat32,
|
||||
"Float64": EncodingFloat64,
|
||||
"Bit64": EncodingBit64,
|
||||
"Hash128": EncodingHash128,
|
||||
"Year": EncodingYear,
|
||||
"Date": EncodingDate,
|
||||
"Time": EncodingTime,
|
||||
"Datetime": EncodingDatetime,
|
||||
"Enum": EncodingEnum,
|
||||
"Set": EncodingSet,
|
||||
"BytesAddr": EncodingBytesAddr,
|
||||
"CommitAddr": EncodingCommitAddr,
|
||||
"String": EncodingString,
|
||||
"Bytes": EncodingBytes,
|
||||
"Decimal": EncodingDecimal,
|
||||
"JSON": EncodingJSON,
|
||||
"Geometry": EncodingGeometry,
|
||||
}
|
||||
|
||||
func (v Encoding) String() string {
|
||||
|
||||
@@ -68,7 +68,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dolthub/go-mysql-server v0.12.1-0.20220621185654-bbe8bee61d0a
|
||||
github.com/dolthub/go-mysql-server v0.12.1-0.20220622173612-932627449b01
|
||||
github.com/google/flatbuffers v2.0.6+incompatible
|
||||
github.com/gosuri/uilive v0.0.4
|
||||
github.com/kch42/buzhash v0.0.0-20160816060738-9bdec3dec7c6
|
||||
|
||||
@@ -178,8 +178,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U=
|
||||
github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0=
|
||||
github.com/dolthub/go-mysql-server v0.12.1-0.20220621185654-bbe8bee61d0a h1:+gT2+flsaQUJ/H/Ap8X4PWvdQU3zjfDG0Q6klxpKYuM=
|
||||
github.com/dolthub/go-mysql-server v0.12.1-0.20220621185654-bbe8bee61d0a/go.mod h1:gvDEMITJQDVYDLR4XtcqEZx6rawTvMh2veM1bPsJC3I=
|
||||
github.com/dolthub/go-mysql-server v0.12.1-0.20220622173612-932627449b01 h1:K7D2ge4FwpTqS3FnnTum0Zg3MqqnYGPELeoIbHIjJgg=
|
||||
github.com/dolthub/go-mysql-server v0.12.1-0.20220622173612-932627449b01/go.mod h1:gvDEMITJQDVYDLR4XtcqEZx6rawTvMh2veM1bPsJC3I=
|
||||
github.com/dolthub/ishell v0.0.0-20220112232610-14e753f0f371 h1:oyPHJlzumKta1vnOQqUnfdz+pk3EmnHS3Nd0cCT0I2g=
|
||||
github.com/dolthub/ishell v0.0.0-20220112232610-14e753f0f371/go.mod h1:dhGBqcCEfK5kuFmeO5+WOx3hqc1k3M29c1oS/R7N4ms=
|
||||
github.com/dolthub/jsonpath v0.0.0-20210609232853-d49537a30474 h1:xTrR+l5l+1Lfq0NvhiEsctylXinUMFhhsqaEcl414p8=
|
||||
|
||||
@@ -29,6 +29,8 @@ import (
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/dolt/go/store/prolly/shim"
|
||||
"github.com/dolthub/dolt/go/store/prolly/tree"
|
||||
"github.com/dolthub/dolt/go/store/types"
|
||||
)
|
||||
|
||||
@@ -106,6 +108,11 @@ func (t *Table) ValueReadWriter() types.ValueReadWriter {
|
||||
return durable.VrwFromTable(t.table)
|
||||
}
|
||||
|
||||
// NodeStore returns the NodeStore for this table.
|
||||
func (t *Table) NodeStore() tree.NodeStore {
|
||||
return tree.NewNodeStore(shim.ChunkStoreFromVRW(t.ValueReadWriter()))
|
||||
}
|
||||
|
||||
// SetConflicts sets the merge conflicts for this table.
|
||||
func (t *Table) SetConflicts(ctx context.Context, schemas conflict.ConflictSchema, conflictData durable.ConflictIndex) (*Table, error) {
|
||||
table, err := t.table.SetConflicts(ctx, schemas, conflictData)
|
||||
|
||||
@@ -24,13 +24,6 @@ import (
|
||||
|
||||
// EncodingFromSqlType returns a serial.Encoding for a query.Type.
|
||||
func EncodingFromSqlType(typ query.Type) serial.Encoding {
|
||||
// todo(andy): replace temp encodings
|
||||
switch typ {
|
||||
case query.Type_BLOB, query.Type_TEXT:
|
||||
// todo: temporary hack for enginetests
|
||||
return serial.EncodingString
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case query.Type_INT8:
|
||||
return serial.EncodingInt8
|
||||
@@ -86,6 +79,10 @@ func EncodingFromSqlType(typ query.Type) serial.Encoding {
|
||||
return serial.EncodingJSON
|
||||
case query.Type_GEOMETRY:
|
||||
return serial.EncodingGeometry
|
||||
case query.Type_BLOB:
|
||||
return serial.EncodingBytesAddr
|
||||
case query.Type_TEXT:
|
||||
return serial.EncodingString
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown encoding %v", typ))
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/vitess/go/sqltypes"
|
||||
@@ -73,7 +74,11 @@ func CreateBlobStringTypeFromParams(params map[string]string) (TypeInfo, error)
|
||||
// ConvertNomsValueToValue implements TypeInfo interface.
|
||||
func (ti *blobStringType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
|
||||
if val, ok := v.(types.Blob); ok {
|
||||
return fromBlob(val)
|
||||
b, err := fromBlob(val)
|
||||
if sql.IsBinaryType(ti.sqlStringType) {
|
||||
return b, err
|
||||
}
|
||||
return string(b), err
|
||||
}
|
||||
if _, ok := v.(types.Null); ok || v == nil {
|
||||
return nil, nil
|
||||
@@ -90,7 +95,11 @@ func (ti *blobStringType) ReadFrom(_ *types.NomsBinFormat, reader types.CodecRea
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fromBlob(val)
|
||||
b, err := fromBlob(val)
|
||||
if sql.IsBinaryType(ti.sqlStringType) {
|
||||
return b, err
|
||||
}
|
||||
return string(b), err
|
||||
case types.NullKind:
|
||||
_ = reader.ReadKind()
|
||||
return nil, nil
|
||||
@@ -134,7 +143,7 @@ func (ti *blobStringType) FormatValue(v types.Value) (*string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resStr, nil
|
||||
return (*string)(unsafe.Pointer(&resStr)), nil
|
||||
}
|
||||
if _, ok := v.(types.Null); ok || v == nil {
|
||||
return nil, nil
|
||||
@@ -204,7 +213,7 @@ func blobStringTypeConverter(ctx context.Context, src *blobStringType, destTi Ty
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newVal, err := strconv.ParseUint(val, 10, int(dest.sqlBitType.NumberOfBits()))
|
||||
newVal, err := strconv.ParseUint(string(val), 10, int(dest.sqlBitType.NumberOfBits()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -105,9 +105,9 @@ func (ti *inlineBlobType) ConvertValueToNomsValue(ctx context.Context, vrw types
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val, ok := strVal.(string)
|
||||
val, ok := strVal.([]byte)
|
||||
if ok {
|
||||
return *(*types.InlineBlob)(unsafe.Pointer(&val)), nil
|
||||
return types.InlineBlob(val), nil
|
||||
}
|
||||
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ func wrapConvertValueToNomsValue(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vInt = str
|
||||
vInt = string(str)
|
||||
case types.Bool:
|
||||
vInt = bool(val)
|
||||
case types.Decimal:
|
||||
|
||||
@@ -101,9 +101,9 @@ func (ti *varBinaryType) ConvertValueToNomsValue(ctx context.Context, vrw types.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val, ok := strVal.(string)
|
||||
val, ok := strVal.([]byte)
|
||||
if ok {
|
||||
return types.NewBlob(ctx, vrw, strings.NewReader(val))
|
||||
return types.NewBlob(ctx, vrw, strings.NewReader(string(val)))
|
||||
}
|
||||
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
|
||||
}
|
||||
@@ -126,7 +126,7 @@ func (ti *varBinaryType) FormatValue(v types.Value) (*string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resStr, nil
|
||||
return (*string)(unsafe.Pointer(&resStr)), nil
|
||||
}
|
||||
if _, ok := v.(types.Null); ok || v == nil {
|
||||
return nil, nil
|
||||
@@ -180,18 +180,18 @@ func (ti *varBinaryType) ToSqlType() sql.Type {
|
||||
}
|
||||
|
||||
// fromBlob returns a string from a types.Blob.
|
||||
func fromBlob(b types.Blob) (string, error) {
|
||||
func fromBlob(b types.Blob) ([]byte, error) {
|
||||
strLength := b.Len()
|
||||
if strLength == 0 {
|
||||
return "", nil
|
||||
return []byte{}, nil
|
||||
}
|
||||
str := make([]byte, strLength)
|
||||
n, err := b.ReadAt(context.Background(), str, 0)
|
||||
if err != nil && err != io.EOF {
|
||||
return "", err
|
||||
return []byte{}, err
|
||||
}
|
||||
if uint64(n) != strLength {
|
||||
return "", fmt.Errorf("wanted %d bytes from blob for data, got %d", strLength, n)
|
||||
return []byte{}, fmt.Errorf("wanted %d bytes from blob for data, got %d", strLength, n)
|
||||
}
|
||||
|
||||
// For very large byte slices, the standard method of converting a byte slice to a string using "string(str)" will
|
||||
@@ -200,7 +200,7 @@ func fromBlob(b types.Blob) (string, error) {
|
||||
// testing, performance improved by 40%.
|
||||
// This is inspired by Go's own source code in strings.Builder.String(): https://golang.org/src/strings/builder.go#L48
|
||||
// This is also marked as a valid strategy in unsafe.Pointer's own method documentation.
|
||||
return *(*string)(unsafe.Pointer(&str)), nil
|
||||
return str, nil
|
||||
}
|
||||
|
||||
// hasPrefix finds out if a Blob has a prefixed integer. Initially blobs for varBinary prepended an integer indicating
|
||||
@@ -242,7 +242,7 @@ func varBinaryTypeConverter(ctx context.Context, src *varBinaryType, destTi Type
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newVal, err := strconv.ParseUint(val, 10, int(dest.sqlBitType.NumberOfBits()))
|
||||
newVal, err := strconv.ParseUint(string(val), 10, int(dest.sqlBitType.NumberOfBits()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ func (itr *prollyConflictRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
r[0] = c.h.String()
|
||||
|
||||
for i := 0; i < itr.kd.Count(); i++ {
|
||||
f, err := index.GetField(itr.kd, i, c.k)
|
||||
f, err := index.GetField(ctx, itr.kd, i, c.k, itr.baseRows.NodeStore())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func (itr *prollyConflictRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
|
||||
if c.bV != nil {
|
||||
for i := 0; i < itr.baseVD.Count(); i++ {
|
||||
f, err := index.GetField(itr.baseVD, i, c.bV)
|
||||
f, err := index.GetField(ctx, itr.baseVD, i, c.bV, itr.baseRows.NodeStore())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -196,7 +196,7 @@ func (itr *prollyConflictRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
|
||||
if c.oV != nil {
|
||||
for i := 0; i < itr.oursVD.Count(); i++ {
|
||||
f, err := index.GetField(itr.oursVD, i, c.oV)
|
||||
f, err := index.GetField(ctx, itr.oursVD, i, c.oV, itr.baseRows.NodeStore())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -206,7 +206,7 @@ func (itr *prollyConflictRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
|
||||
if c.tV != nil {
|
||||
for i := 0; i < itr.theirsVD.Count(); i++ {
|
||||
f, err := index.GetField(itr.theirsVD, i, c.tV)
|
||||
f, err := index.GetField(ctx, itr.theirsVD, i, c.tV, itr.baseRows.NodeStore())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -353,7 +353,8 @@ func (cd *prollyConflictDeleter) Delete(ctx *sql.Context, r sql.Row) error {
|
||||
|
||||
// first part of the artifact key is the keys of the source table
|
||||
for i := 0; i < cd.kd.Count()-2; i++ {
|
||||
err := index.PutField(cd.kB, i, r[o+i])
|
||||
err := index.PutField(ctx, cd.ed.Mut.NodeStore(), cd.kB, i, r[o+i])
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -361,7 +362,7 @@ func (cd *prollyConflictDeleter) Delete(ctx *sql.Context, r sql.Row) error {
|
||||
|
||||
// then the hash follows. It is the first column of the row and the second to last in the key
|
||||
h := hash.Parse(r[0].(string))
|
||||
cd.kB.PutAddress(cd.kd.Count()-2, h)
|
||||
cd.kB.PutCommitAddr(cd.kd.Count()-2, h)
|
||||
|
||||
// Finally the artifact type which is always a conflict
|
||||
cd.kB.PutUint8(cd.kd.Count()-1, uint8(prolly.ArtifactTypeConflict))
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/dolthub/dolt/go/store/pool"
|
||||
"github.com/dolthub/dolt/go/store/prolly"
|
||||
"github.com/dolthub/dolt/go/store/prolly/shim"
|
||||
"github.com/dolthub/dolt/go/store/prolly/tree"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
@@ -112,7 +113,13 @@ func (cvt *prollyConstraintViolationsTable) PartitionRows(ctx *sql.Context, part
|
||||
return nil, err
|
||||
}
|
||||
kd, vd := shim.MapDescriptorsFromSchema(sch)
|
||||
return prollyCVIter{itr, sch, kd, vd}, nil
|
||||
return prollyCVIter{
|
||||
itr: itr,
|
||||
sch: sch,
|
||||
kd: kd,
|
||||
vd: vd,
|
||||
ns: cvt.artM.NodeStore(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cvt *prollyConstraintViolationsTable) Deleter(context *sql.Context) sql.RowDeleter {
|
||||
@@ -134,6 +141,7 @@ type prollyCVIter struct {
|
||||
itr prolly.ArtifactIter
|
||||
sch schema.Schema
|
||||
kd, vd val.TupleDesc
|
||||
ns tree.NodeStore
|
||||
}
|
||||
|
||||
func (itr prollyCVIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
@@ -154,7 +162,7 @@ func (itr prollyCVIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
|
||||
o := 2
|
||||
for i := 0; i < itr.kd.Count(); i++ {
|
||||
r[o+i], err = index.GetField(itr.kd, i, art.Key)
|
||||
r[o+i], err = index.GetField(ctx, itr.kd, i, art.Key, itr.ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -162,7 +170,7 @@ func (itr prollyCVIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
o += itr.kd.Count()
|
||||
|
||||
for i := 0; i < itr.vd.Count(); i++ {
|
||||
r[o+i], err = index.GetField(itr.vd, i, meta.Value)
|
||||
r[o+i], err = index.GetField(ctx, itr.vd, i, meta.Value, itr.ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -193,7 +201,7 @@ var _ sql.RowDeleter = (*prollyCVDeleter)(nil)
|
||||
func (d *prollyCVDeleter) Delete(ctx *sql.Context, r sql.Row) error {
|
||||
// first part of the artifact key is the keys of the source table
|
||||
for i := 0; i < d.kd.Count()-2; i++ {
|
||||
err := index.PutField(d.kb, i, r[i+2])
|
||||
err := index.PutField(ctx, d.cvt.artM.NodeStore(), d.kb, i, r[i+2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -201,7 +209,7 @@ func (d *prollyCVDeleter) Delete(ctx *sql.Context, r sql.Row) error {
|
||||
|
||||
// then the hash
|
||||
h := hash.Parse(r[0].(string))
|
||||
d.kb.PutAddress(d.kd.Count()-2, h)
|
||||
d.kb.PutCommitAddr(d.kd.Count()-2, h)
|
||||
|
||||
// Finally the artifact type
|
||||
artType := unmapCVType(merge.CvType(r[1].(uint64)))
|
||||
|
||||
@@ -256,12 +256,12 @@ func newProllyDiffIter(ctx *sql.Context, dp DiffPartition, ddb *doltdb.DoltDB, t
|
||||
}
|
||||
to := durable.ProllyMapFromIndex(t)
|
||||
|
||||
fromConverter, err := NewProllyRowConverter(fSch, targetFromSchema, ctx.Warn)
|
||||
fromConverter, err := NewProllyRowConverter(fSch, targetFromSchema, ctx.Warn, nil)
|
||||
if err != nil {
|
||||
return prollyDiffIter{}, err
|
||||
}
|
||||
|
||||
toConverter, err := NewProllyRowConverter(tSch, targetToSchema, ctx.Warn)
|
||||
toConverter, err := NewProllyRowConverter(tSch, targetToSchema, ctx.Warn, dp.to.NodeStore())
|
||||
if err != nil {
|
||||
return prollyDiffIter{}, err
|
||||
}
|
||||
@@ -312,7 +312,7 @@ func (itr prollyDiffIter) Close(ctx *sql.Context) error {
|
||||
|
||||
func (itr prollyDiffIter) queueRows(ctx context.Context) {
|
||||
err := prolly.DiffMaps(ctx, itr.from, itr.to, func(ctx context.Context, d tree.Diff) error {
|
||||
r, err := itr.makeDiffRow(d)
|
||||
r, err := itr.makeDiffRow(ctx, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -335,7 +335,7 @@ func (itr prollyDiffIter) queueRows(ctx context.Context) {
|
||||
}
|
||||
|
||||
// todo(andy): copy string fields
|
||||
func (itr prollyDiffIter) makeDiffRow(d tree.Diff) (r sql.Row, err error) {
|
||||
func (itr prollyDiffIter) makeDiffRow(ctx context.Context, d tree.Diff) (r sql.Row, err error) {
|
||||
|
||||
n := itr.targetToSch.GetAllCols().Size()
|
||||
m := itr.targetFromSch.GetAllCols().Size()
|
||||
@@ -345,7 +345,7 @@ func (itr prollyDiffIter) makeDiffRow(d tree.Diff) (r sql.Row, err error) {
|
||||
// todo (dhruv): implement warnings for row column value coercions.
|
||||
|
||||
if d.Type != tree.RemovedDiff {
|
||||
err = itr.toConverter.PutConverted(val.Tuple(d.Key), val.Tuple(d.To), r[0:n])
|
||||
err = itr.toConverter.PutConverted(ctx, val.Tuple(d.Key), val.Tuple(d.To), r[0:n])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -356,7 +356,7 @@ func (itr prollyDiffIter) makeDiffRow(d tree.Diff) (r sql.Row, err error) {
|
||||
r[o+1] = maybeTime(itr.toCm.ts)
|
||||
|
||||
if d.Type != tree.AddedDiff {
|
||||
err = itr.fromConverter.PutConverted(val.Tuple(d.Key), val.Tuple(d.From), r[n+2:n+2+m])
|
||||
err = itr.fromConverter.PutConverted(ctx, val.Tuple(d.Key), val.Tuple(d.From), r[n+2:n+2+m])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package dtables
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/diff"
|
||||
@@ -22,6 +24,7 @@ import (
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
|
||||
"github.com/dolthub/dolt/go/store/prolly/shim"
|
||||
"github.com/dolthub/dolt/go/store/prolly/tree"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
@@ -37,9 +40,10 @@ type ProllyRowConverter struct {
|
||||
pkTargetTypes []sql.Type
|
||||
nonPkTargetTypes []sql.Type
|
||||
warnFn rowconv.WarnFunction
|
||||
ns tree.NodeStore
|
||||
}
|
||||
|
||||
func NewProllyRowConverter(inSch, outSch schema.Schema, warnFn rowconv.WarnFunction) (ProllyRowConverter, error) {
|
||||
func NewProllyRowConverter(inSch, outSch schema.Schema, warnFn rowconv.WarnFunction, ns tree.NodeStore) (ProllyRowConverter, error) {
|
||||
keyProj, valProj, err := diff.MapSchemaBasedOnName(inSch, outSch)
|
||||
if err != nil {
|
||||
return ProllyRowConverter{}, err
|
||||
@@ -89,18 +93,19 @@ func NewProllyRowConverter(inSch, outSch schema.Schema, warnFn rowconv.WarnFunct
|
||||
pkTargetTypes: pkTargetTypes,
|
||||
nonPkTargetTypes: nonPkTargetTypes,
|
||||
warnFn: warnFn,
|
||||
ns: ns,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PutConverted converts the |key| and |value| val.Tuple from |inSchema| to |outSchema|
|
||||
// and places the converted row in |dstRow|.
|
||||
func (c ProllyRowConverter) PutConverted(key, value val.Tuple, dstRow []interface{}) error {
|
||||
err := c.putFields(key, c.keyProj, c.keyDesc, c.pkTargetTypes, dstRow)
|
||||
func (c ProllyRowConverter) PutConverted(ctx context.Context, key, value val.Tuple, dstRow []interface{}) error {
|
||||
err := c.putFields(ctx, key, c.keyProj, c.keyDesc, c.pkTargetTypes, dstRow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.putFields(value, c.valProj, c.valDesc, c.nonPkTargetTypes, dstRow)
|
||||
err = c.putFields(ctx, value, c.valProj, c.valDesc, c.nonPkTargetTypes, dstRow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -108,12 +113,12 @@ func (c ProllyRowConverter) PutConverted(key, value val.Tuple, dstRow []interfac
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c ProllyRowConverter) putFields(tup val.Tuple, proj val.OrdinalMapping, desc val.TupleDesc, targetTypes []sql.Type, dstRow []interface{}) error {
|
||||
func (c ProllyRowConverter) putFields(ctx context.Context, tup val.Tuple, proj val.OrdinalMapping, desc val.TupleDesc, targetTypes []sql.Type, dstRow []interface{}) error {
|
||||
for i, j := range proj {
|
||||
if j == -1 {
|
||||
continue
|
||||
}
|
||||
f, err := index.GetField(desc, i, tup)
|
||||
f, err := index.GetField(ctx, desc, i, tup, c.ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
29
go/libraries/doltcore/sqle/enginetest/blob_queries.go
Normal file
29
go/libraries/doltcore/sqle/enginetest/blob_queries.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 enginetest
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/enginetest/queries"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
)
|
||||
|
||||
var BigBlobQueries = []queries.WriteQueryTest{
|
||||
{
|
||||
WriteQuery: "INSERT INTO blobt VALUES(4, LOAD_FILE('testdata/test1.png'))",
|
||||
ExpectedWriteResult: []sql.Row{{sql.NewOkResult(1)}},
|
||||
SelectQuery: "select sha1(b) from blobt where i = 4",
|
||||
ExpectedSelect: []sql.Row{{"012bcb75a319f2913614a5170fc046fb6c49ee86"}},
|
||||
},
|
||||
}
|
||||
@@ -440,6 +440,21 @@ func TestCreateDatabase(t *testing.T) {
|
||||
enginetest.TestCreateDatabase(t, newDoltHarness(t))
|
||||
}
|
||||
|
||||
func TestBlobs(t *testing.T) {
|
||||
skipOldFormat(t)
|
||||
enginetest.TestBlobs(t, newDoltHarness(t))
|
||||
}
|
||||
|
||||
func TestBigBlobs(t *testing.T) {
|
||||
skipOldFormat(t)
|
||||
|
||||
h := newDoltHarness(t)
|
||||
h.Setup(setup.MydbData, setup.BlobData)
|
||||
for _, tt := range BigBlobQueries {
|
||||
enginetest.RunWriteQueryTest(t, h, tt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropDatabase(t *testing.T) {
|
||||
enginetest.TestScript(t, newDoltHarness(t), queries.ScriptTest{
|
||||
Name: "Drop database engine tests for Dolt only",
|
||||
@@ -1198,6 +1213,12 @@ var newFormatSkippedScripts = []string{
|
||||
"Multiple indexes on the same columns in a different order",
|
||||
}
|
||||
|
||||
func skipOldFormat(t *testing.T) {
|
||||
if !types.IsFormat_DOLT_1(types.Format_Default) {
|
||||
t.Skip()
|
||||
}
|
||||
}
|
||||
|
||||
func skipPreparedTests(t *testing.T) {
|
||||
if skipPrepared {
|
||||
t.Skip("skip prepared")
|
||||
|
||||
BIN
go/libraries/doltcore/sqle/enginetest/testdata/test1.png
vendored
Normal file
BIN
go/libraries/doltcore/sqle/enginetest/testdata/test1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 313 KiB |
@@ -27,6 +27,8 @@ import (
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/table/typed/noms"
|
||||
"github.com/dolthub/dolt/go/store/pool"
|
||||
"github.com/dolthub/dolt/go/store/prolly"
|
||||
"github.com/dolthub/dolt/go/store/prolly/shim"
|
||||
"github.com/dolthub/dolt/go/store/prolly/tree"
|
||||
"github.com/dolthub/dolt/go/store/types"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
@@ -379,7 +381,7 @@ func (di *doltIndex) NewLookup(ctx *sql.Context, ranges ...sql.Range) (sql.Index
|
||||
}
|
||||
|
||||
if types.IsFormat_DOLT_1(di.vrw.Format()) {
|
||||
return di.newProllyLookup(ctx, ranges...)
|
||||
return di.newProllyLookup(ctx, tree.NewNodeStore(shim.ChunkStoreFromVRW(di.vrw)), ranges...)
|
||||
}
|
||||
|
||||
return di.newNomsLookup(ctx, ranges...)
|
||||
@@ -436,7 +438,7 @@ func (di *doltIndex) getDurableState(ctx *sql.Context, ti DoltTableable) (*durab
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (di *doltIndex) newProllyLookup(ctx *sql.Context, ranges ...sql.Range) (sql.IndexLookup, error) {
|
||||
func (di *doltIndex) newProllyLookup(ctx *sql.Context, ns tree.NodeStore, ranges ...sql.Range) (sql.IndexLookup, error) {
|
||||
var err error
|
||||
sqlRanges, err := pruneEmptyRanges(ranges)
|
||||
if err != nil {
|
||||
@@ -445,7 +447,7 @@ func (di *doltIndex) newProllyLookup(ctx *sql.Context, ranges ...sql.Range) (sql
|
||||
|
||||
prs := make([]prolly.Range, len(sqlRanges))
|
||||
for i, sr := range sqlRanges {
|
||||
prs[i], err = prollyRangeFromSqlRange(sr, di.keyBld)
|
||||
prs[i], err = prollyRangeFromSqlRange(ctx, ns, sr, di.keyBld)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -732,7 +734,7 @@ func pruneEmptyRanges(sqlRanges []sql.Range) (pruned []sql.Range, err error) {
|
||||
return pruned, nil
|
||||
}
|
||||
|
||||
func prollyRangeFromSqlRange(rng sql.Range, tb *val.TupleBuilder) (prolly.Range, error) {
|
||||
func prollyRangeFromSqlRange(ctx context.Context, ns tree.NodeStore, rng sql.Range, tb *val.TupleBuilder) (prolly.Range, error) {
|
||||
prollyRange := prolly.Range{
|
||||
Start: make([]prolly.RangeCut, len(rng)),
|
||||
Stop: make([]prolly.RangeCut, len(rng)),
|
||||
@@ -748,7 +750,7 @@ func prollyRangeFromSqlRange(rng sql.Range, tb *val.TupleBuilder) (prolly.Range,
|
||||
if err != nil {
|
||||
return prolly.Range{}, err
|
||||
}
|
||||
if err = PutField(tb, i, v); err != nil {
|
||||
if err = PutField(ctx, ns, tb, i, v); err != nil {
|
||||
return prolly.Range{}, err
|
||||
}
|
||||
}
|
||||
@@ -780,7 +782,7 @@ func prollyRangeFromSqlRange(rng sql.Range, tb *val.TupleBuilder) (prolly.Range,
|
||||
if err != nil {
|
||||
return prolly.Range{}, err
|
||||
}
|
||||
if err = PutField(tb, i, v); err != nil {
|
||||
if err = PutField(ctx, ns, tb, i, v); err != nil {
|
||||
return prolly.Range{}, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,9 +69,8 @@ func RowIterForProllyRange(ctx *sql.Context, idx DoltIndex, r prolly.Range, pkSc
|
||||
covers := idx.coversColumns(durableState, columns)
|
||||
if covers {
|
||||
return newProllyCoveringIndexIter(ctx, idx, r, pkSch, durableState.Secondary)
|
||||
} else {
|
||||
return newProllyIndexIter(ctx, idx, r, pkSch, durableState.Primary, durableState.Secondary)
|
||||
}
|
||||
return newProllyIndexIter(ctx, idx, r, pkSch, durableState.Primary, durableState.Secondary)
|
||||
}
|
||||
|
||||
func RowIterForNomsRanges(ctx *sql.Context, idx DoltIndex, ranges []*noms.ReadRange, columns []string, durableState *durableIndexState) (sql.RowIter, error) {
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
@@ -26,13 +29,14 @@ import (
|
||||
|
||||
geo "github.com/dolthub/dolt/go/store/geometry"
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/dolt/go/store/prolly/tree"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
var ErrValueExceededMaxFieldSize = errors.New("value exceeded max field size of 65kb")
|
||||
|
||||
// GetField reads the value from the ith field of the Tuple as an interface{}.
|
||||
func GetField(td val.TupleDesc, i int, tup val.Tuple) (v interface{}, err error) {
|
||||
func GetField(ctx context.Context, td val.TupleDesc, i int, tup val.Tuple, ns tree.NodeStore) (v interface{}, err error) {
|
||||
var ok bool
|
||||
switch td.Types[i].Enc {
|
||||
case val.Int8Enc:
|
||||
@@ -95,8 +99,14 @@ func GetField(td val.TupleDesc, i int, tup val.Tuple) (v interface{}, err error)
|
||||
}
|
||||
case val.Hash128Enc:
|
||||
v, ok = td.GetHash128(i, tup)
|
||||
case val.AddressEnc:
|
||||
v, ok = td.GetAddress(i, tup)
|
||||
case val.BytesAddrEnc:
|
||||
var b val.BytesAddr
|
||||
b, ok = td.GetBlob(i, tup)
|
||||
if ok {
|
||||
v, err = tree.NewByteArray(b.Addr, ns).ToBytes(ctx)
|
||||
}
|
||||
case val.CommitAddrEnc:
|
||||
v, ok = td.GetCommitAddr(i, tup)
|
||||
default:
|
||||
panic("unknown val.encoding")
|
||||
}
|
||||
@@ -107,7 +117,7 @@ func GetField(td val.TupleDesc, i int, tup val.Tuple) (v interface{}, err error)
|
||||
}
|
||||
|
||||
// PutField writes an interface{} to the ith field of the Tuple being built.
|
||||
func PutField(tb *val.TupleBuilder, i int, v interface{}) error {
|
||||
func PutField(ctx context.Context, ns tree.NodeStore, tb *val.TupleBuilder, i int, v interface{}) error {
|
||||
if v == nil {
|
||||
return nil // NULL
|
||||
}
|
||||
@@ -177,8 +187,14 @@ func PutField(tb *val.TupleBuilder, i int, v interface{}) error {
|
||||
tb.PutJSON(i, buf)
|
||||
case val.Hash128Enc:
|
||||
tb.PutHash128(i, v.([]byte))
|
||||
case val.AddressEnc:
|
||||
tb.PutAddress(i, v.(hash.Hash))
|
||||
case val.BytesAddrEnc:
|
||||
b, err := serializeBytesToAddr(ctx, ns, bytes.NewReader(v.([]byte)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tb.PutBytesAddr(i, b.Addr)
|
||||
case val.CommitAddrEnc:
|
||||
tb.PutCommitAddr(i, v.(hash.Hash))
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown encoding %v %v", enc, v))
|
||||
}
|
||||
@@ -266,6 +282,14 @@ func serializeGeometry(v interface{}) []byte {
|
||||
}
|
||||
}
|
||||
|
||||
func serializeBytesToAddr(ctx context.Context, ns tree.NodeStore, r io.Reader) (val.BytesAddr, error) {
|
||||
tree, err := tree.NewImmutableTreeFromReader(ctx, r, ns, tree.DefaultFixedChunkLength)
|
||||
if err != nil {
|
||||
return val.BytesAddr{}, err
|
||||
}
|
||||
return val.NewBytesAddr(tree.Addr), nil
|
||||
}
|
||||
|
||||
func convJson(v interface{}) (buf []byte, err error) {
|
||||
v, err = sql.JSON.Convert(v)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"testing"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/pool"
|
||||
"github.com/dolthub/dolt/go/store/prolly/tree"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
@@ -161,6 +163,11 @@ func TestRoundTripProllyFields(t *testing.T) {
|
||||
typ: val.Type{Enc: val.GeometryEnc},
|
||||
value: mustParseGeometryType(t, "POLYGON((0 0,1 1,1 0,0 0))"),
|
||||
},
|
||||
{
|
||||
name: "binary",
|
||||
typ: val.Type{Enc: val.BytesAddrEnc},
|
||||
value: []byte("lorem ipsum"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -175,13 +182,14 @@ var testPool = pool.NewBuffPool()
|
||||
func testRoundTripProllyFields(t *testing.T, test prollyFieldTest) {
|
||||
desc := val.NewTupleDescriptor(test.typ)
|
||||
builder := val.NewTupleBuilder(desc)
|
||||
ns := tree.NewTestNodeStore()
|
||||
|
||||
err := PutField(builder, 0, test.value)
|
||||
err := PutField(context.Background(), ns, builder, 0, test.value)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tup := builder.Build(testPool)
|
||||
|
||||
v, err := GetField(desc, 0, tup)
|
||||
v, err := GetField(context.Background(), desc, 0, tup, ns)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.value, v)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
|
||||
"github.com/dolthub/dolt/go/store/prolly"
|
||||
"github.com/dolthub/dolt/go/store/prolly/tree"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
@@ -51,13 +52,7 @@ var _ sql.RowIter = prollyIndexIter{}
|
||||
var _ sql.RowIter2 = prollyIndexIter{}
|
||||
|
||||
// NewProllyIndexIter returns a new prollyIndexIter.
|
||||
func newProllyIndexIter(
|
||||
ctx *sql.Context,
|
||||
idx DoltIndex,
|
||||
rng prolly.Range,
|
||||
pkSch sql.PrimaryKeySchema,
|
||||
dprimary, dsecondary durable.Index,
|
||||
) (prollyIndexIter, error) {
|
||||
func newProllyIndexIter(ctx *sql.Context, idx DoltIndex, rng prolly.Range, pkSch sql.PrimaryKeySchema, dprimary, dsecondary durable.Index) (prollyIndexIter, error) {
|
||||
secondary := durable.ProllyMapFromIndex(dsecondary)
|
||||
indexIter, err := secondary.IterRange(ctx, rng)
|
||||
if err != nil {
|
||||
@@ -128,7 +123,7 @@ func (p prollyIndexIter) queueRows(ctx context.Context) error {
|
||||
|
||||
r := make(sql.Row, len(p.keyMap)+len(p.valMap))
|
||||
err = p.primary.Get(ctx, pk, func(key, value val.Tuple) error {
|
||||
return p.rowFromTuples(key, value, r)
|
||||
return p.rowFromTuples(ctx, key, value, r)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -142,14 +137,14 @@ func (p prollyIndexIter) queueRows(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (p prollyIndexIter) rowFromTuples(key, value val.Tuple, r sql.Row) (err error) {
|
||||
func (p prollyIndexIter) rowFromTuples(ctx context.Context, key, value val.Tuple, r sql.Row) (err error) {
|
||||
keyDesc, valDesc := p.primary.Descriptors()
|
||||
|
||||
for keyIdx, rowIdx := range p.keyMap {
|
||||
if rowIdx == -1 {
|
||||
continue
|
||||
}
|
||||
r[rowIdx], err = GetField(keyDesc, keyIdx, key)
|
||||
r[rowIdx], err = GetField(ctx, keyDesc, keyIdx, key, p.primary.NodeStore())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -158,7 +153,7 @@ func (p prollyIndexIter) rowFromTuples(key, value val.Tuple, r sql.Row) (err err
|
||||
if rowIdx == -1 {
|
||||
continue
|
||||
}
|
||||
r[rowIdx], err = GetField(valDesc, valIdx, value)
|
||||
r[rowIdx], err = GetField(ctx, valDesc, valIdx, value, p.primary.NodeStore())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -208,6 +203,8 @@ type prollyCoveringIndexIter struct {
|
||||
keyDesc val.TupleDesc
|
||||
valDesc val.TupleDesc
|
||||
|
||||
ns tree.NodeStore
|
||||
|
||||
// keyMap transforms secondary index key tuples into SQL tuples.
|
||||
// secondary index value tuples are assumed to be empty.
|
||||
|
||||
@@ -242,6 +239,7 @@ func newProllyCoveringIndexIter(ctx *sql.Context, idx DoltIndex, rng prolly.Rang
|
||||
keyMap: keyMap,
|
||||
valMap: valMap,
|
||||
sqlSch: pkSch.Schema,
|
||||
ns: secondary.NodeStore(),
|
||||
}
|
||||
|
||||
return iter, nil
|
||||
@@ -255,7 +253,7 @@ func (p prollyCoveringIndexIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
}
|
||||
|
||||
r := make(sql.Row, len(p.keyMap))
|
||||
if err := p.writeRowFromTuples(k, v, r); err != nil {
|
||||
if err := p.writeRowFromTuples(ctx, k, v, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -271,13 +269,13 @@ func (p prollyCoveringIndexIter) Next2(ctx *sql.Context, f *sql.RowFrame) error
|
||||
return p.writeRow2FromTuples(k, v, f)
|
||||
}
|
||||
|
||||
func (p prollyCoveringIndexIter) writeRowFromTuples(key, value val.Tuple, r sql.Row) (err error) {
|
||||
func (p prollyCoveringIndexIter) writeRowFromTuples(ctx context.Context, key, value val.Tuple, r sql.Row) (err error) {
|
||||
for to := range p.keyMap {
|
||||
from := p.keyMap.MapOrdinal(to)
|
||||
if from == -1 {
|
||||
continue
|
||||
}
|
||||
r[to], err = GetField(p.keyDesc, from, key)
|
||||
r[to], err = GetField(ctx, p.keyDesc, from, key, p.ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -288,7 +286,7 @@ func (p prollyCoveringIndexIter) writeRowFromTuples(key, value val.Tuple, r sql.
|
||||
if from == -1 {
|
||||
continue
|
||||
}
|
||||
r[to], err = GetField(p.valDesc, from, value)
|
||||
r[to], err = GetField(ctx, p.valDesc, from, value, p.ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -384,13 +382,7 @@ type prollyKeylessIndexIter struct {
|
||||
var _ sql.RowIter = prollyKeylessIndexIter{}
|
||||
var _ sql.RowIter2 = prollyKeylessIndexIter{}
|
||||
|
||||
func newProllyKeylessIndexIter(
|
||||
ctx *sql.Context,
|
||||
idx DoltIndex,
|
||||
rng prolly.Range,
|
||||
pkSch sql.PrimaryKeySchema,
|
||||
rows, dsecondary durable.Index,
|
||||
) (prollyKeylessIndexIter, error) {
|
||||
func newProllyKeylessIndexIter(ctx *sql.Context, idx DoltIndex, rng prolly.Range, pkSch sql.PrimaryKeySchema, rows, dsecondary durable.Index) (prollyKeylessIndexIter, error) {
|
||||
secondary := durable.ProllyMapFromIndex(dsecondary)
|
||||
indexIter, err := secondary.IterRange(ctx, rng)
|
||||
if err != nil {
|
||||
@@ -468,7 +460,7 @@ func (p prollyKeylessIndexIter) queueRows(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := p.keylessRowsFromValueTuple(value)
|
||||
rows, err := p.keylessRowsFromValueTuple(ctx, p.clustered.NodeStore(), value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -483,7 +475,7 @@ func (p prollyKeylessIndexIter) queueRows(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (p prollyKeylessIndexIter) keylessRowsFromValueTuple(value val.Tuple) (rows []sql.Row, err error) {
|
||||
func (p prollyKeylessIndexIter) keylessRowsFromValueTuple(ctx context.Context, ns tree.NodeStore, value val.Tuple) (rows []sql.Row, err error) {
|
||||
card := val.ReadKeylessCardinality(value)
|
||||
rows = make([]sql.Row, card)
|
||||
rows[0] = make(sql.Row, len(p.valueMap)-1) // omit cardinality field
|
||||
@@ -492,7 +484,7 @@ func (p prollyKeylessIndexIter) keylessRowsFromValueTuple(value val.Tuple) (rows
|
||||
if rowIdx == -1 {
|
||||
continue
|
||||
}
|
||||
rows[0][rowIdx], err = GetField(p.valueDesc, valIdx, value)
|
||||
rows[0][rowIdx], err = GetField(ctx, p.valueDesc, valIdx, value, ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
|
||||
"github.com/dolthub/dolt/go/store/prolly"
|
||||
"github.com/dolthub/dolt/go/store/prolly/tree"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
@@ -50,6 +51,7 @@ var encodingToType [256]query.Type
|
||||
|
||||
type prollyRowIter struct {
|
||||
iter prolly.MapIter
|
||||
ns tree.NodeStore
|
||||
|
||||
sqlSch sql.Schema
|
||||
keyDesc val.TupleDesc
|
||||
@@ -62,13 +64,7 @@ type prollyRowIter struct {
|
||||
var _ sql.RowIter = prollyRowIter{}
|
||||
var _ sql.RowIter2 = prollyRowIter{}
|
||||
|
||||
func NewProllyRowIter(
|
||||
sch schema.Schema,
|
||||
schSch sql.Schema,
|
||||
rows prolly.Map,
|
||||
iter prolly.MapIter,
|
||||
projections []string,
|
||||
) (sql.RowIter, error) {
|
||||
func NewProllyRowIter(sch schema.Schema, schSch sql.Schema, rows prolly.Map, iter prolly.MapIter, projections []string) (sql.RowIter, error) {
|
||||
|
||||
// todo(andy): NomsRangeReader seemingly ignores projections
|
||||
//if projections == nil {
|
||||
@@ -86,6 +82,7 @@ func NewProllyRowIter(
|
||||
valDesc: vd,
|
||||
valProj: valProj,
|
||||
rowLen: len(projections),
|
||||
ns: rows.NodeStore(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -97,6 +94,7 @@ func NewProllyRowIter(
|
||||
keyProj: keyProj,
|
||||
valProj: valProj,
|
||||
rowLen: len(projections),
|
||||
ns: rows.NodeStore(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -146,7 +144,7 @@ func (it prollyRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
if rowIdx == -1 {
|
||||
continue
|
||||
}
|
||||
row[rowIdx], err = GetField(it.keyDesc, keyIdx, key)
|
||||
row[rowIdx], err = GetField(ctx, it.keyDesc, keyIdx, key, it.ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -155,7 +153,7 @@ func (it prollyRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
if rowIdx == -1 {
|
||||
continue
|
||||
}
|
||||
row[rowIdx], err = GetField(it.valDesc, valIdx, value)
|
||||
row[rowIdx], err = GetField(ctx, it.valDesc, valIdx, value, it.ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -205,6 +203,7 @@ func (it prollyRowIter) Close(ctx *sql.Context) error {
|
||||
|
||||
type prollyKeylessIter struct {
|
||||
iter prolly.MapIter
|
||||
ns tree.NodeStore
|
||||
|
||||
valDesc val.TupleDesc
|
||||
valProj []int
|
||||
@@ -243,7 +242,7 @@ func (it *prollyKeylessIter) nextTuple(ctx *sql.Context) error {
|
||||
if rowIdx == -1 {
|
||||
continue
|
||||
}
|
||||
it.curr[rowIdx], err = GetField(it.valDesc, valIdx, value)
|
||||
it.curr[rowIdx], err = GetField(ctx, it.valDesc, valIdx, value, it.ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -319,6 +319,7 @@ func SqlColToStr(ctx context.Context, sqlType sql.Type, col interface{}) (string
|
||||
return "", err
|
||||
}
|
||||
return res.ToString(), nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,13 +128,13 @@ func (iter prollyFkPkRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
err = iter.primary.mut.Get(ctx, pkTup, func(tblKey, tblVal val.Tuple) error {
|
||||
for from := range iter.primary.keyMap {
|
||||
to := iter.primary.keyMap.MapOrdinal(from)
|
||||
if nextRow[to], err = index.GetField(iter.primary.keyBld.Desc, from, tblKey); err != nil {
|
||||
if nextRow[to], err = index.GetField(ctx, iter.primary.keyBld.Desc, from, tblKey, iter.primary.mut.NodeStore()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for from := range iter.primary.valMap {
|
||||
to := iter.primary.valMap.MapOrdinal(from)
|
||||
if nextRow[to], err = index.GetField(iter.primary.valBld.Desc, from, tblVal); err != nil {
|
||||
if nextRow[to], err = index.GetField(ctx, iter.primary.valBld.Desc, from, tblVal, iter.primary.mut.NodeStore()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,7 @@ func (iter prollyFkKeylessRowIter) Next(ctx *sql.Context) (sql.Row, error) {
|
||||
err = iter.primary.mut.Get(ctx, primaryKey, func(tblKey, tblVal val.Tuple) error {
|
||||
for from := range iter.primary.valMap {
|
||||
to := iter.primary.valMap.MapOrdinal(from)
|
||||
if nextRow[to], err = index.GetField(iter.primary.valBld.Desc, from+1, tblVal); err != nil {
|
||||
if nextRow[to], err = index.GetField(ctx, iter.primary.valBld.Desc, from+1, tblVal, iter.primary.mut.NodeStore()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func (m prollyIndexWriter) Map(ctx context.Context) (prolly.Map, error) {
|
||||
func (m prollyIndexWriter) Insert(ctx context.Context, sqlRow sql.Row) error {
|
||||
for to := range m.keyMap {
|
||||
from := m.keyMap.MapOrdinal(to)
|
||||
if err := index.PutField(m.keyBld, to, sqlRow[from]); err != nil {
|
||||
if err := index.PutField(ctx, m.mut.NodeStore(), m.keyBld, to, sqlRow[from]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,7 @@ func (m prollyIndexWriter) Insert(ctx context.Context, sqlRow sql.Row) error {
|
||||
|
||||
for to := range m.valMap {
|
||||
from := m.valMap.MapOrdinal(to)
|
||||
if err = index.PutField(m.valBld, to, sqlRow[from]); err != nil {
|
||||
if err = index.PutField(ctx, m.mut.NodeStore(), m.valBld, to, sqlRow[from]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func (m prollyIndexWriter) Insert(ctx context.Context, sqlRow sql.Row) error {
|
||||
func (m prollyIndexWriter) Delete(ctx context.Context, sqlRow sql.Row) error {
|
||||
for to := range m.keyMap {
|
||||
from := m.keyMap.MapOrdinal(to)
|
||||
if err := index.PutField(m.keyBld, to, sqlRow[from]); err != nil {
|
||||
if err := index.PutField(ctx, m.mut.NodeStore(), m.keyBld, to, sqlRow[from]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func (m prollyIndexWriter) Delete(ctx context.Context, sqlRow sql.Row) error {
|
||||
func (m prollyIndexWriter) Update(ctx context.Context, oldRow sql.Row, newRow sql.Row) error {
|
||||
for to := range m.keyMap {
|
||||
from := m.keyMap.MapOrdinal(to)
|
||||
if err := index.PutField(m.keyBld, to, oldRow[from]); err != nil {
|
||||
if err := index.PutField(ctx, m.mut.NodeStore(), m.keyBld, to, oldRow[from]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,7 @@ func (m prollyIndexWriter) Update(ctx context.Context, oldRow sql.Row, newRow sq
|
||||
|
||||
for to := range m.keyMap {
|
||||
from := m.keyMap.MapOrdinal(to)
|
||||
if err := index.PutField(m.keyBld, to, newRow[from]); err != nil {
|
||||
if err := index.PutField(ctx, m.mut.NodeStore(), m.keyBld, to, newRow[from]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func (m prollyIndexWriter) Update(ctx context.Context, oldRow sql.Row, newRow sq
|
||||
|
||||
for to := range m.valMap {
|
||||
from := m.valMap.MapOrdinal(to)
|
||||
if err = index.PutField(m.valBld, to, newRow[from]); err != nil {
|
||||
if err = index.PutField(ctx, m.mut.NodeStore(), m.valBld, to, newRow[from]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ func (m prollyIndexWriter) HasEdits(ctx context.Context) bool {
|
||||
func (m prollyIndexWriter) UniqueKeyError(ctx context.Context, sqlRow sql.Row) error {
|
||||
for to := range m.keyMap {
|
||||
from := m.keyMap.MapOrdinal(to)
|
||||
if err := index.PutField(m.keyBld, to, sqlRow[from]); err != nil {
|
||||
if err := index.PutField(ctx, m.mut.NodeStore(), m.keyBld, to, sqlRow[from]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -226,7 +226,7 @@ func (m prollyIndexWriter) keyError(ctx context.Context, key val.Tuple, isPk boo
|
||||
kd := m.keyBld.Desc
|
||||
for from := range m.keyMap {
|
||||
to := m.keyMap.MapOrdinal(from)
|
||||
if dupe[to], err = index.GetField(kd, from, key); err != nil {
|
||||
if dupe[to], err = index.GetField(ctx, kd, from, key, m.mut.NodeStore()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func (m prollyIndexWriter) keyError(ctx context.Context, key val.Tuple, isPk boo
|
||||
vd := m.valBld.Desc
|
||||
for from := range m.valMap {
|
||||
to := m.valMap.MapOrdinal(from)
|
||||
if dupe[to], err = index.GetField(vd, from, value); err != nil {
|
||||
if dupe[to], err = index.GetField(ctx, vd, from, value, m.mut.NodeStore()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -276,7 +276,7 @@ func (k prollyKeylessWriter) Map(ctx context.Context) (prolly.Map, error) {
|
||||
}
|
||||
|
||||
func (k prollyKeylessWriter) Insert(ctx context.Context, sqlRow sql.Row) error {
|
||||
hashId, value, err := k.tuplesFromRow(sqlRow)
|
||||
hashId, value, err := k.tuplesFromRow(ctx, sqlRow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func (k prollyKeylessWriter) Insert(ctx context.Context, sqlRow sql.Row) error {
|
||||
}
|
||||
|
||||
func (k prollyKeylessWriter) Delete(ctx context.Context, sqlRow sql.Row) error {
|
||||
hashId, _, err := k.tuplesFromRow(sqlRow)
|
||||
hashId, _, err := k.tuplesFromRow(ctx, sqlRow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -355,15 +355,15 @@ func (k prollyKeylessWriter) UniqueKeyError(ctx context.Context, sqlRow sql.Row)
|
||||
return fmt.Errorf("keyless does not yet know how to handle unique key errors")
|
||||
}
|
||||
|
||||
func (k prollyKeylessWriter) tuplesFromRow(sqlRow sql.Row) (hashId, value val.Tuple, err error) {
|
||||
func (k prollyKeylessWriter) tuplesFromRow(ctx context.Context, sqlRow sql.Row) (hashId, value val.Tuple, err error) {
|
||||
// initialize cardinality to 0
|
||||
if err = index.PutField(k.valBld, 0, uint64(0)); err != nil {
|
||||
if err = index.PutField(ctx, k.mut.NodeStore(), k.valBld, 0, uint64(0)); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for to := range k.valMap {
|
||||
from := k.valMap.MapOrdinal(to)
|
||||
if err = index.PutField(k.valBld, to+1, sqlRow[from]); err != nil {
|
||||
if err = index.PutField(ctx, k.mut.NodeStore(), k.valBld, to+1, sqlRow[from]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
@@ -402,11 +402,11 @@ func (writer prollyKeylessSecondaryWriter) Map(ctx context.Context) (prolly.Map,
|
||||
func (writer prollyKeylessSecondaryWriter) Insert(ctx context.Context, sqlRow sql.Row) error {
|
||||
for to := range writer.keyMap {
|
||||
from := writer.keyMap.MapOrdinal(to)
|
||||
if err := index.PutField(writer.keyBld, to, sqlRow[from]); err != nil {
|
||||
if err := index.PutField(ctx, writer.mut.NodeStore(), writer.keyBld, to, sqlRow[from]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
hashId, _, err := writer.primary.tuplesFromRow(sqlRow)
|
||||
hashId, _, err := writer.primary.tuplesFromRow(ctx, sqlRow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -428,7 +428,7 @@ func (writer prollyKeylessSecondaryWriter) Insert(ctx context.Context, sqlRow sq
|
||||
|
||||
// Delete implements the interface indexWriter.
|
||||
func (writer prollyKeylessSecondaryWriter) Delete(ctx context.Context, sqlRow sql.Row) error {
|
||||
hashId, cardRow, err := writer.primary.tuplesFromRow(sqlRow)
|
||||
hashId, cardRow, err := writer.primary.tuplesFromRow(ctx, sqlRow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -444,7 +444,7 @@ func (writer prollyKeylessSecondaryWriter) Delete(ctx context.Context, sqlRow sq
|
||||
|
||||
for to := range writer.keyMap {
|
||||
from := writer.keyMap.MapOrdinal(to)
|
||||
if err := index.PutField(writer.keyBld, to, sqlRow[from]); err != nil {
|
||||
if err := index.PutField(ctx, writer.mut.NodeStore(), writer.keyBld, to, sqlRow[from]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,26 +16,27 @@ namespace serial;
|
||||
|
||||
enum Encoding : uint8 {
|
||||
// fixed width
|
||||
Null = 0,
|
||||
Int8 = 1,
|
||||
Uint8 = 2,
|
||||
Int16 = 3,
|
||||
Uint16 = 4,
|
||||
Int32 = 7,
|
||||
Uint32 = 8,
|
||||
Int64 = 9,
|
||||
Uint64 = 10,
|
||||
Float32 = 11,
|
||||
Float64 = 12,
|
||||
Bit64 = 13,
|
||||
Hash128 = 14,
|
||||
Year = 15,
|
||||
Date = 16,
|
||||
Time = 17,
|
||||
Datetime = 18,
|
||||
Enum = 19,
|
||||
Set = 20,
|
||||
Address = 21,
|
||||
Null = 0,
|
||||
Int8 = 1,
|
||||
Uint8 = 2,
|
||||
Int16 = 3,
|
||||
Uint16 = 4,
|
||||
Int32 = 7,
|
||||
Uint32 = 8,
|
||||
Int64 = 9,
|
||||
Uint64 = 10,
|
||||
Float32 = 11,
|
||||
Float64 = 12,
|
||||
Bit64 = 13,
|
||||
Hash128 = 14,
|
||||
Year = 15,
|
||||
Date = 16,
|
||||
Time = 17,
|
||||
Datetime = 18,
|
||||
Enum = 19,
|
||||
Set = 20,
|
||||
BytesAddr = 21,
|
||||
CommitAddr = 22,
|
||||
|
||||
// variable width
|
||||
String = 128,
|
||||
|
||||
@@ -179,7 +179,7 @@ func commit_flatbuffer(vaddr hash.Hash, opts CommitOptions, heights []uint64, pa
|
||||
|
||||
var commitKeyTupleDesc = val.NewTupleDescriptor(
|
||||
val.Type{Enc: val.Uint64Enc, Nullable: false},
|
||||
val.Type{Enc: val.AddressEnc, Nullable: false},
|
||||
val.Type{Enc: val.CommitAddrEnc, Nullable: false},
|
||||
)
|
||||
var commitValueTupleDesc = val.NewTupleDescriptor()
|
||||
|
||||
|
||||
@@ -110,6 +110,10 @@ func (m ArtifactMap) Node() tree.Node {
|
||||
return m.tuples.root
|
||||
}
|
||||
|
||||
func (m ArtifactMap) NodeStore() tree.NodeStore {
|
||||
return m.tuples.ns
|
||||
}
|
||||
|
||||
func (m ArtifactMap) Format() *types.NomsBinFormat {
|
||||
return m.tuples.ns.Format()
|
||||
}
|
||||
@@ -142,7 +146,7 @@ func (m ArtifactMap) Editor() ArtifactsEditor {
|
||||
artKD, artVD := m.Descriptors()
|
||||
return ArtifactsEditor{
|
||||
srcKeyDesc: m.srcKeyDesc,
|
||||
mut: MutableMap{
|
||||
Mut: MutableMap{
|
||||
tuples: m.tuples.mutate(),
|
||||
keyDesc: m.keyDesc,
|
||||
valDesc: m.valDesc,
|
||||
@@ -280,7 +284,7 @@ func (m ArtifactMap) iterAllOfTypes(ctx context.Context, artTypes ...ArtifactTyp
|
||||
|
||||
func MergeArtifactMaps(ctx context.Context, left, right, base ArtifactMap, cb tree.CollisionFn) (ArtifactMap, error) {
|
||||
serializer := message.ProllyMapSerializer{Pool: left.tuples.ns.Pool()}
|
||||
tuples, err := mergeOrderedTrees(ctx, left.tuples, right.tuples, base.tuples, cb, serializer)
|
||||
tuples, err := mergeOrderedTrees(ctx, left.tuples, right.tuples, base.tuples, cb, serializer, base.valDesc)
|
||||
if err != nil {
|
||||
return ArtifactMap{}, err
|
||||
}
|
||||
@@ -293,7 +297,7 @@ func MergeArtifactMaps(ctx context.Context, left, right, base ArtifactMap, cb tr
|
||||
}
|
||||
|
||||
type ArtifactsEditor struct {
|
||||
mut MutableMap
|
||||
Mut MutableMap
|
||||
srcKeyDesc val.TupleDesc
|
||||
artKB, artVB *val.TupleBuilder
|
||||
pool pool.BuffPool
|
||||
@@ -303,14 +307,14 @@ func (wr ArtifactsEditor) Add(ctx context.Context, srcKey val.Tuple, theirRootIs
|
||||
for i := 0; i < srcKey.Count(); i++ {
|
||||
wr.artKB.PutRaw(i, srcKey.GetField(i))
|
||||
}
|
||||
wr.artKB.PutAddress(srcKey.Count(), theirRootIsh)
|
||||
wr.artKB.PutCommitAddr(srcKey.Count(), theirRootIsh)
|
||||
wr.artKB.PutUint8(srcKey.Count()+1, uint8(artType))
|
||||
key := wr.artKB.Build(wr.pool)
|
||||
|
||||
wr.artVB.PutJSON(0, meta)
|
||||
value := wr.artVB.Build(wr.pool)
|
||||
|
||||
return wr.mut.Put(ctx, key, value)
|
||||
return wr.Mut.Put(ctx, key, value)
|
||||
}
|
||||
|
||||
// ReplaceFKConstraintViolation replaces foreign key constraint violations that
|
||||
@@ -318,14 +322,14 @@ func (wr ArtifactsEditor) Add(ctx context.Context, srcKey val.Tuple, theirRootIs
|
||||
// exists, the given will be inserted.
|
||||
func (wr ArtifactsEditor) ReplaceFKConstraintViolation(ctx context.Context, srcKey val.Tuple, theirRootIsh hash.Hash, meta ConstraintViolationMeta) error {
|
||||
rng := ClosedRange(srcKey, srcKey, wr.srcKeyDesc)
|
||||
itr, err := wr.mut.IterRange(ctx, rng)
|
||||
itr, err := wr.Mut.IterRange(ctx, rng)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aItr := artifactIterImpl{
|
||||
itr: itr,
|
||||
artKD: wr.mut.keyDesc,
|
||||
artVD: wr.mut.valDesc,
|
||||
artKD: wr.Mut.keyDesc,
|
||||
artVD: wr.Mut.valDesc,
|
||||
pool: wr.pool,
|
||||
tb: val.NewTupleBuilder(wr.srcKeyDesc),
|
||||
numPks: wr.srcKeyDesc.Count(),
|
||||
@@ -368,11 +372,11 @@ func (wr ArtifactsEditor) ReplaceFKConstraintViolation(ctx context.Context, srcK
|
||||
}
|
||||
|
||||
func (wr ArtifactsEditor) Delete(ctx context.Context, key val.Tuple) error {
|
||||
return wr.mut.Delete(ctx, key)
|
||||
return wr.Mut.Delete(ctx, key)
|
||||
}
|
||||
|
||||
func (wr ArtifactsEditor) Flush(ctx context.Context) (ArtifactMap, error) {
|
||||
m, err := wr.mut.Map(ctx)
|
||||
m, err := wr.Mut.Map(ctx)
|
||||
if err != nil {
|
||||
return ArtifactMap{}, err
|
||||
}
|
||||
@@ -380,8 +384,8 @@ func (wr ArtifactsEditor) Flush(ctx context.Context) (ArtifactMap, error) {
|
||||
return ArtifactMap{
|
||||
tuples: m.tuples,
|
||||
srcKeyDesc: wr.srcKeyDesc,
|
||||
keyDesc: wr.mut.keyDesc,
|
||||
valDesc: wr.mut.valDesc,
|
||||
keyDesc: wr.Mut.keyDesc,
|
||||
valDesc: wr.Mut.valDesc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -498,7 +502,7 @@ func (itr artifactIterImpl) Next(ctx context.Context) (Artifact, error) {
|
||||
}
|
||||
|
||||
srcKey := itr.getSrcKeyFromArtKey(artKey)
|
||||
cmHash, _ := itr.artKD.GetAddress(itr.numPks, artKey)
|
||||
cmHash, _ := itr.artKD.GetCommitAddr(itr.numPks, artKey)
|
||||
artType, _ := itr.artKD.GetUint8(itr.numPks+1, artKey)
|
||||
metadata, _ := itr.artVD.GetJSON(0, v)
|
||||
|
||||
@@ -539,7 +543,7 @@ func calcArtifactsDescriptors(srcKd val.TupleDesc) (kd, vd val.TupleDesc) {
|
||||
keyTypes := srcKd.Types
|
||||
|
||||
// target branch commit hash
|
||||
keyTypes = append(keyTypes, val.Type{Enc: val.AddressEnc, Nullable: false})
|
||||
keyTypes = append(keyTypes, val.Type{Enc: val.CommitAddrEnc, Nullable: false})
|
||||
|
||||
// artifact type
|
||||
keyTypes = append(keyTypes, val.Type{Enc: val.Uint8Enc, Nullable: false})
|
||||
|
||||
@@ -83,7 +83,7 @@ func DiffMaps(ctx context.Context, from, to Map, cb DiffFn) error {
|
||||
|
||||
func MergeMaps(ctx context.Context, left, right, base Map, cb tree.CollisionFn) (Map, error) {
|
||||
serializer := message.ProllyMapSerializer{Pool: left.tuples.ns.Pool()}
|
||||
tuples, err := mergeOrderedTrees(ctx, left.tuples, right.tuples, base.tuples, cb, serializer)
|
||||
tuples, err := mergeOrderedTrees(ctx, left.tuples, right.tuples, base.tuples, cb, serializer, base.valDesc)
|
||||
if err != nil {
|
||||
return Map{}, err
|
||||
}
|
||||
@@ -95,6 +95,11 @@ func MergeMaps(ctx context.Context, left, right, base Map, cb tree.CollisionFn)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NodeStore returns the map's NodeStore
|
||||
func (m Map) NodeStore() tree.NodeStore {
|
||||
return m.tuples.ns
|
||||
}
|
||||
|
||||
// Mutate makes a MutableMap from a Map.
|
||||
func (m Map) Mutate() MutableMap {
|
||||
return newMutableMap(m)
|
||||
|
||||
@@ -247,7 +247,8 @@ func makeMapWithInserts(t *testing.T, m Map, numInserts int) (Map, [][2]val.Tupl
|
||||
// generates tuple pairs not currently in |m|
|
||||
func generateInserts(t *testing.T, m Map, kd, vd val.TupleDesc, numInserts int) [][2]val.Tuple {
|
||||
ctx := context.Background()
|
||||
tups := tree.RandomTuplePairs(numInserts*2, kd, vd)
|
||||
ns := tree.NewTestNodeStore()
|
||||
tups := tree.RandomTuplePairs(numInserts*2, kd, vd, ns)
|
||||
inserts, extra := tups[:numInserts], tups[numInserts:]
|
||||
|
||||
j := 0
|
||||
@@ -293,10 +294,12 @@ func makeUpdatesToTuples(kd, vd val.TupleDesc, tuples ...[2]val.Tuple) (updates
|
||||
updates = make([][3]val.Tuple, len(tuples))
|
||||
|
||||
valBuilder := val.NewTupleBuilder(vd)
|
||||
ns := tree.NewTestNodeStore()
|
||||
|
||||
for i := range updates {
|
||||
updates[i][0] = tuples[i][0]
|
||||
updates[i][1] = tuples[i][1]
|
||||
updates[i][2] = tree.RandomTuple(valBuilder)
|
||||
updates[i][2] = tree.RandomTuple(valBuilder, ns)
|
||||
}
|
||||
|
||||
sort.Slice(updates, func(i, j int) bool {
|
||||
|
||||
@@ -42,6 +42,7 @@ func Test3WayMapMerge(t *testing.T) {
|
||||
val.Type{Enc: val.Uint32Enc, Nullable: true},
|
||||
val.Type{Enc: val.Uint32Enc, Nullable: true},
|
||||
)
|
||||
ns := tree.NewTestNodeStore()
|
||||
|
||||
for _, s := range scales {
|
||||
name := fmt.Sprintf("test proCur map at scale %d", s)
|
||||
@@ -51,12 +52,12 @@ func Test3WayMapMerge(t *testing.T) {
|
||||
})
|
||||
t.Run("3way merge inserts", func(t *testing.T) {
|
||||
for k := 0; k < 10; k++ {
|
||||
testThreeWayMapMerge(t, kd, vd, s)
|
||||
testThreeWayMapMerge(t, kd, vd, s, ns)
|
||||
}
|
||||
})
|
||||
t.Run("tuple merge fn", func(t *testing.T) {
|
||||
for k := 0; k < 10; k++ {
|
||||
testTupleMergeFn(t, kd, vd, s)
|
||||
testTupleMergeFn(t, kd, vd, s, ns)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -73,8 +74,8 @@ func testEqualMapMerge(t *testing.T, sz int) {
|
||||
assert.Equal(t, m.HashOf(), mm.HashOf())
|
||||
}
|
||||
|
||||
func testThreeWayMapMerge(t *testing.T, kd, vd val.TupleDesc, sz int) {
|
||||
baseTuples, leftEdits, rightEdits := makeTuplesAndMutations(kd, vd, sz)
|
||||
func testThreeWayMapMerge(t *testing.T, kd, vd val.TupleDesc, sz int, ns tree.NodeStore) {
|
||||
baseTuples, leftEdits, rightEdits := makeTuplesAndMutations(kd, vd, sz, ns)
|
||||
om := prollyMapFromTuples(t, kd, vd, baseTuples)
|
||||
|
||||
base := om.(Map)
|
||||
@@ -139,9 +140,9 @@ func testThreeWayMapMerge(t *testing.T, kd, vd val.TupleDesc, sz int) {
|
||||
}
|
||||
}
|
||||
|
||||
func testTupleMergeFn(t *testing.T, kd, vd val.TupleDesc, sz int) {
|
||||
func testTupleMergeFn(t *testing.T, kd, vd val.TupleDesc, sz int, ns tree.NodeStore) {
|
||||
ctx := context.Background()
|
||||
tuples := tree.RandomTuplePairs(sz, kd, vd)
|
||||
tuples := tree.RandomTuplePairs(sz, kd, vd, ns)
|
||||
om := prollyMapFromTuples(t, kd, vd, tuples)
|
||||
base := om.(Map)
|
||||
|
||||
@@ -210,10 +211,10 @@ type mutationSet struct {
|
||||
updates [][3]val.Tuple
|
||||
}
|
||||
|
||||
func makeTuplesAndMutations(kd, vd val.TupleDesc, sz int) (base [][2]val.Tuple, left, right mutationSet) {
|
||||
func makeTuplesAndMutations(kd, vd val.TupleDesc, sz int, ns tree.NodeStore) (base [][2]val.Tuple, left, right mutationSet) {
|
||||
mutSz := sz / 10
|
||||
totalSz := sz + (mutSz * 2)
|
||||
tuples := tree.RandomTuplePairs(totalSz, kd, vd)
|
||||
tuples := tree.RandomTuplePairs(totalSz, kd, vd, ns)
|
||||
|
||||
base = tuples[:sz]
|
||||
|
||||
|
||||
@@ -123,8 +123,9 @@ func makeProllyMap(t *testing.T, count int) (testMap, [][2]val.Tuple) {
|
||||
val.Type{Enc: val.Uint32Enc, Nullable: true},
|
||||
val.Type{Enc: val.Uint32Enc, Nullable: true},
|
||||
)
|
||||
ns := tree.NewTestNodeStore()
|
||||
|
||||
tuples := tree.RandomTuplePairs(count, kd, vd)
|
||||
tuples := tree.RandomTuplePairs(count, kd, vd, ns)
|
||||
om := prollyMapFromTuples(t, kd, vd, tuples)
|
||||
|
||||
return om, tuples
|
||||
@@ -136,8 +137,8 @@ func makeProllySecondaryIndex(t *testing.T, count int) (testMap, [][2]val.Tuple)
|
||||
val.Type{Enc: val.Uint32Enc, Nullable: false},
|
||||
)
|
||||
vd := val.NewTupleDescriptor()
|
||||
|
||||
tuples := tree.RandomCompositeTuplePairs(count, kd, vd)
|
||||
ns := tree.NewTestNodeStore()
|
||||
tuples := tree.RandomCompositeTuplePairs(count, kd, vd, ns)
|
||||
om := prollyMapFromTuples(t, kd, vd, tuples)
|
||||
|
||||
return om, tuples
|
||||
|
||||
@@ -18,9 +18,10 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
|
||||
"github.com/dolthub/dolt/go/gen/fb/serial"
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
const MessageTypesKind int = 28
|
||||
|
||||
@@ -38,7 +38,8 @@ const (
|
||||
var prollyMapFileID = []byte(serial.ProllyTreeNodeFileID)
|
||||
|
||||
type ProllyMapSerializer struct {
|
||||
Pool pool.BuffPool
|
||||
Pool pool.BuffPool
|
||||
ValDesc val.TupleDesc
|
||||
}
|
||||
|
||||
var _ Serializer = ProllyMapSerializer{}
|
||||
@@ -47,10 +48,11 @@ func (s ProllyMapSerializer) Serialize(keys, values [][]byte, subtrees []uint64,
|
||||
var (
|
||||
keyTups, keyOffs fb.UOffsetT
|
||||
valTups, valOffs fb.UOffsetT
|
||||
valAddrOffs fb.UOffsetT
|
||||
refArr, cardArr fb.UOffsetT
|
||||
)
|
||||
|
||||
keySz, valSz, bufSz := estimateProllyMapSize(keys, values, subtrees)
|
||||
keySz, valSz, bufSz := estimateProllyMapSize(keys, values, subtrees, len(s.ValDesc.Addrs))
|
||||
b := getFlatbufferBuilder(s.Pool, bufSz)
|
||||
|
||||
// serialize keys and offsets
|
||||
@@ -63,6 +65,10 @@ func (s ProllyMapSerializer) Serialize(keys, values [][]byte, subtrees []uint64,
|
||||
valTups = writeItemBytes(b, values, valSz)
|
||||
serial.ProllyTreeNodeStartValueOffsetsVector(b, len(values)-1)
|
||||
valOffs = writeItemOffsets(b, values, valSz)
|
||||
if len(s.ValDesc.Addrs) > 0 {
|
||||
serial.ProllyTreeNodeStartValueAddressOffsetsVector(b, len(values)*len(s.ValDesc.Addrs))
|
||||
valAddrOffs = writeValAddrOffsets(b, values, valSz, s.ValDesc)
|
||||
}
|
||||
} else {
|
||||
// serialize child refs and subtree counts for internal nodes
|
||||
refArr = writeItemBytes(b, values, valSz)
|
||||
@@ -77,6 +83,7 @@ func (s ProllyMapSerializer) Serialize(keys, values [][]byte, subtrees []uint64,
|
||||
serial.ProllyTreeNodeAddValueItems(b, valTups)
|
||||
serial.ProllyTreeNodeAddValueOffsets(b, valOffs)
|
||||
serial.ProllyTreeNodeAddTreeCount(b, uint64(len(keys)))
|
||||
serial.ProllyTreeNodeAddValueAddressOffsets(b, valAddrOffs)
|
||||
} else {
|
||||
serial.ProllyTreeNodeAddAddressArray(b, refArr)
|
||||
serial.ProllyTreeNodeAddSubtreeCounts(b, cardArr)
|
||||
@@ -126,7 +133,7 @@ func walkProllyMapAddresses(ctx context.Context, msg Message, cb func(ctx contex
|
||||
arr2 := pm.ValueItemsBytes()
|
||||
for i := 0; i < cnt; i++ {
|
||||
o := pm.ValueAddressOffsets(i)
|
||||
addr := hash.New(arr[o : o+addrSize])
|
||||
addr := hash.New(arr2[o : o+addrSize])
|
||||
if err := cb(ctx, addr); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -182,7 +189,8 @@ func getProllyMapValueOffsets(pm *serial.ProllyTreeNode) []byte {
|
||||
|
||||
// estimateProllyMapSize returns the exact Size of the tuple vectors for keys and values,
|
||||
// and an estimate of the overall Size of the final flatbuffer.
|
||||
func estimateProllyMapSize(keys, values [][]byte, subtrees []uint64) (keySz, valSz, bufSz int) {
|
||||
func estimateProllyMapSize(keys, values [][]byte, subtrees []uint64, valAddrsCnt int) (int, int, int) {
|
||||
var keySz, valSz, bufSz int
|
||||
for i := range keys {
|
||||
keySz += len(keys[i])
|
||||
valSz += len(values[i])
|
||||
@@ -204,7 +212,8 @@ func estimateProllyMapSize(keys, values [][]byte, subtrees []uint64) (keySz, val
|
||||
bufSz += 8 + 1 + 1 + 1 // metadata
|
||||
bufSz += 72 // vtable (approx)
|
||||
bufSz += 100 // padding?
|
||||
bufSz += valAddrsCnt * len(values)
|
||||
bufSz += messagePrefixSz
|
||||
|
||||
return
|
||||
return keySz, valSz, bufSz
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ import (
|
||||
|
||||
fb "github.com/google/flatbuffers/go"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/dolt/go/store/pool"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -58,6 +60,29 @@ func writeItemOffsets(b *fb.Builder, items [][]byte, sumSz int) fb.UOffsetT {
|
||||
return b.EndVector(cnt)
|
||||
}
|
||||
|
||||
// writeAddrOffests returns offsets into the values array that correspond to addr root
|
||||
// hashes that themselves have subtrees
|
||||
func writeValAddrOffsets(b *fb.Builder, items [][]byte, sumSz int, valDesc val.TupleDesc) fb.UOffsetT {
|
||||
var cnt int
|
||||
var off = sumSz
|
||||
for i := len(items) - 1; i >= 0; i-- {
|
||||
tup := val.Tuple(items[i])
|
||||
off -= len(tup) // start of tuple
|
||||
for _, j := range valDesc.Addrs {
|
||||
// get index into value tuple pointing at address
|
||||
o, _ := tup.GetOffset(j)
|
||||
a := tup.GetField(j)
|
||||
if len(a) == 0 || hash.New(a).IsEmpty() {
|
||||
continue
|
||||
}
|
||||
o += off // offset is tuple start plus field start
|
||||
b.PrependUint16(uint16(o))
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
return b.EndVector(cnt)
|
||||
}
|
||||
|
||||
func writeCountArray(b *fb.Builder, counts []uint64) fb.UOffsetT {
|
||||
// todo(andy): encode without alloc
|
||||
buf := make([]byte, maxEncodedSize(len(counts)))
|
||||
|
||||
@@ -51,7 +51,7 @@ func (mut MutableMap) Map(ctx context.Context) (Map, error) {
|
||||
return Map{}, err
|
||||
}
|
||||
tr := mut.tuples.tree
|
||||
serializer := message.ProllyMapSerializer{Pool: tr.ns.Pool()}
|
||||
serializer := message.ProllyMapSerializer{Pool: tr.ns.Pool(), ValDesc: mut.valDesc}
|
||||
|
||||
root, err := tree.ApplyMutations(ctx, tr.ns, tr.root, serializer, mut.tuples.mutations(), tr.compareItems)
|
||||
if err != nil {
|
||||
@@ -69,6 +69,11 @@ func (mut MutableMap) Map(ctx context.Context) (Map, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NodeStore returns the map's NodeStore
|
||||
func (mut MutableMap) NodeStore() tree.NodeStore {
|
||||
return mut.tuples.tree.ns
|
||||
}
|
||||
|
||||
// Put adds the Tuple pair |key|, |value| to the MutableMap.
|
||||
func (mut MutableMap) Put(ctx context.Context, key, value val.Tuple) error {
|
||||
return mut.tuples.put(ctx, key, value)
|
||||
|
||||
@@ -117,7 +117,7 @@ func makeMutableMap(t *testing.T, count int) (testMap, [][2]val.Tuple) {
|
||||
val.Type{Enc: val.Uint32Enc, Nullable: true},
|
||||
)
|
||||
|
||||
tuples := tree.RandomTuplePairs(count, kd, vd)
|
||||
tuples := tree.RandomTuplePairs(count, kd, vd, ns)
|
||||
// 2/3 of tuples in Map
|
||||
// 1/3 of tuples in memoryMap
|
||||
clone := tree.CloneRandomTuples(tuples)
|
||||
|
||||
@@ -19,11 +19,11 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/prolly/message"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/dolt/go/store/prolly/message"
|
||||
"github.com/dolthub/dolt/go/store/prolly/tree"
|
||||
"github.com/dolthub/dolt/go/store/skip"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
type KeyValueFn[K, V ~[]byte] func(key K, value V) error
|
||||
@@ -71,9 +71,10 @@ func mergeOrderedTrees[K, V ~[]byte, O ordering[K], S message.Serializer](
|
||||
l, r, base orderedTree[K, V, O],
|
||||
cb tree.CollisionFn,
|
||||
serializer S,
|
||||
valDesc val.TupleDesc,
|
||||
) (orderedTree[K, V, O], error) {
|
||||
cfn := base.compareItems
|
||||
root, err := tree.ThreeWayMerge(ctx, base.ns, l.root, r.root, base.root, cfn, cb, serializer)
|
||||
root, err := tree.ThreeWayMerge(ctx, base.ns, l.root, r.root, base.root, cfn, cb, serializer, valDesc)
|
||||
if err != nil {
|
||||
return orderedTree[K, V, O]{}, err
|
||||
}
|
||||
|
||||
@@ -15,14 +15,17 @@
|
||||
package tree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/dolt/go/store/prolly/message"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
const defaultFixedChunkLength = 4000
|
||||
const DefaultFixedChunkLength = 4000
|
||||
|
||||
var ErrInvalidChunkSize = errors.New("invalid chunkSize; value must be > 1")
|
||||
|
||||
@@ -148,3 +151,83 @@ func _newLeaf(ctx context.Context, ns NodeStore, s message.Serializer, buf []byt
|
||||
treeCount: 1,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type ByteArray struct {
|
||||
ImmutableTree
|
||||
}
|
||||
|
||||
func NewByteArray(addr hash.Hash, ns NodeStore) *ByteArray {
|
||||
return &ByteArray{ImmutableTree{Addr: addr, ns: ns}}
|
||||
}
|
||||
|
||||
func (b *ByteArray) ToBytes(ctx context.Context) ([]byte, error) {
|
||||
if b.buf == nil {
|
||||
err := b.load(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return b.buf[:], nil
|
||||
}
|
||||
|
||||
func (b *ByteArray) ToString(ctx context.Context) (string, error) {
|
||||
if b.buf == nil {
|
||||
err := b.load(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
toShow := 128
|
||||
if len(b.buf) < toShow {
|
||||
toShow = len(b.buf)
|
||||
}
|
||||
return string(b.buf[:toShow]), nil
|
||||
}
|
||||
|
||||
type ImmutableTree struct {
|
||||
Addr hash.Hash
|
||||
buf []byte
|
||||
ns NodeStore
|
||||
}
|
||||
|
||||
func NewImmutableTreeFromReader(ctx context.Context, r io.Reader, ns NodeStore, chunkSize int) (*ImmutableTree, error) {
|
||||
s := message.ProllyMapSerializer{Pool: ns.Pool(), ValDesc: val.TupleDesc{}}
|
||||
root, err := buildImmutableTree(ctx, r, ns, s, chunkSize)
|
||||
if errors.Is(err, io.EOF) {
|
||||
return &ImmutableTree{Addr: hash.Hash{}}, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ImmutableTree{Addr: root.HashOf()}, nil
|
||||
}
|
||||
|
||||
func (t *ImmutableTree) load(ctx context.Context) error {
|
||||
if t.Addr.IsEmpty() {
|
||||
t.buf = []byte{}
|
||||
return nil
|
||||
}
|
||||
n, err := t.ns.Read(ctx, t.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
WalkNodes(ctx, n, t.ns, func(ctx context.Context, n Node) error {
|
||||
if n.IsLeaf() {
|
||||
t.buf = append(t.buf, n.getValue(0)...)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ImmutableTree) next() (Node, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (t *ImmutableTree) close() error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (t *ImmutableTree) Read(buf bytes.Buffer) (int, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/prolly/message"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
func TestWriteImmutableTree(t *testing.T) {
|
||||
@@ -196,3 +197,110 @@ func expectedUnfilled(size, chunk int) int {
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
func TestImmutableTreeWalk(t *testing.T) {
|
||||
tests := []struct {
|
||||
blobLen int
|
||||
chunkSize int
|
||||
keyCnt int
|
||||
}{
|
||||
{
|
||||
blobLen: 25,
|
||||
chunkSize: 6,
|
||||
keyCnt: 4,
|
||||
},
|
||||
{
|
||||
blobLen: 25,
|
||||
chunkSize: 5,
|
||||
keyCnt: 4,
|
||||
},
|
||||
{
|
||||
blobLen: 378,
|
||||
chunkSize: 5,
|
||||
keyCnt: 12,
|
||||
},
|
||||
{
|
||||
blobLen: 5000,
|
||||
chunkSize: 12,
|
||||
keyCnt: 6,
|
||||
},
|
||||
{
|
||||
blobLen: 1,
|
||||
chunkSize: 12,
|
||||
keyCnt: 6,
|
||||
},
|
||||
{
|
||||
blobLen: 0,
|
||||
chunkSize: 12,
|
||||
keyCnt: 6,
|
||||
},
|
||||
}
|
||||
|
||||
ns := NewTestNodeStore()
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("inputSize=%d; chunkSize=%d; keyCnt=%d", tt.blobLen, tt.chunkSize, tt.keyCnt), func(t *testing.T) {
|
||||
r := newTree(t, ns, tt.keyCnt, tt.blobLen, tt.chunkSize)
|
||||
var cnt int
|
||||
walkOpaqueNodes(context.Background(), r, ns, func(ctx context.Context, n Node) error {
|
||||
cnt++
|
||||
return nil
|
||||
})
|
||||
require.Equal(t, leafAddrCnt(tt.blobLen, tt.chunkSize)*tt.keyCnt+1, cnt)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func leafAddrCnt(size, chunk int) int {
|
||||
if size == 0 {
|
||||
return 0
|
||||
}
|
||||
l := 1
|
||||
for size > chunk {
|
||||
size = int(math.Ceil(float64(size) / float64(chunk)))
|
||||
l += size
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func newTree(t *testing.T, ns NodeStore, keyCnt, blobLen, chunkSize int) Node {
|
||||
ctx := context.Background()
|
||||
|
||||
keyDesc := val.NewTupleDescriptor(val.Type{Enc: val.Uint32Enc})
|
||||
valDesc := val.NewTupleDescriptor(val.Type{Enc: val.BytesAddrEnc})
|
||||
|
||||
tuples := make([][2]val.Tuple, keyCnt)
|
||||
keyBld := val.NewTupleBuilder(keyDesc)
|
||||
valBld := val.NewTupleBuilder(valDesc)
|
||||
for i := range tuples {
|
||||
keyBld.PutUint32(0, uint32(i))
|
||||
tuples[i][0] = keyBld.Build(sharedPool)
|
||||
|
||||
b := mustNewBlob(ctx, ns, blobLen, chunkSize)
|
||||
valBld.PutBytesAddr(0, b.Addr)
|
||||
tuples[i][1] = valBld.Build(sharedPool)
|
||||
}
|
||||
|
||||
serializer := message.ProllyMapSerializer{Pool: ns.Pool(), ValDesc: valDesc}
|
||||
chunker, err := newEmptyChunker(ctx, ns, serializer)
|
||||
require.NoError(t, err)
|
||||
for _, pair := range tuples {
|
||||
err := chunker.AddPair(ctx, Item(pair[0]), Item(pair[1]))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
root, err := chunker.Done(ctx)
|
||||
require.NoError(t, err)
|
||||
return root
|
||||
}
|
||||
|
||||
func mustNewBlob(ctx context.Context, ns NodeStore, len, chunkSize int) *ImmutableTree {
|
||||
buf := make([]byte, len)
|
||||
for i := range buf {
|
||||
buf[i] = byte(i)
|
||||
}
|
||||
r := bytes.NewReader(buf)
|
||||
root, err := NewImmutableTreeFromReader(ctx, r, ns, chunkSize)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/prolly/message"
|
||||
"github.com/dolthub/dolt/go/store/val"
|
||||
)
|
||||
|
||||
const patchBufferSize = 1024
|
||||
@@ -44,6 +45,7 @@ func ThreeWayMerge[S message.Serializer](
|
||||
compare CompareFn,
|
||||
collide CollisionFn,
|
||||
serializer S,
|
||||
valDesc val.TupleDesc,
|
||||
) (final Node, err error) {
|
||||
|
||||
ld, err := DifferFromRoots(ctx, ns, base, left, compare)
|
||||
|
||||
@@ -58,6 +58,8 @@ func WalkAddresses(ctx context.Context, nd Node, ns NodeStore, cb AddressCb) err
|
||||
|
||||
type NodeCb func(ctx context.Context, nd Node) error
|
||||
|
||||
// WalkNodes runs a callback function on every node found in the DFS of |nd|
|
||||
// that is of the same message type as |nd|.
|
||||
func WalkNodes(ctx context.Context, nd Node, ns NodeStore, cb NodeCb) error {
|
||||
if err := cb(ctx, nd); err != nil {
|
||||
return err
|
||||
@@ -76,6 +78,22 @@ func WalkNodes(ctx context.Context, nd Node, ns NodeStore, cb NodeCb) error {
|
||||
})
|
||||
}
|
||||
|
||||
// walkOpaqueNodes runs a callback function on every node found in the DFS of |nd|
|
||||
// including nested trees.
|
||||
func walkOpaqueNodes(ctx context.Context, nd Node, ns NodeStore, cb NodeCb) error {
|
||||
if err := cb(ctx, nd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return walkAddresses(ctx, nd, func(ctx context.Context, addr hash.Hash) error {
|
||||
child, err := ns.Read(ctx, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return WalkNodes(ctx, child, ns, cb)
|
||||
})
|
||||
}
|
||||
|
||||
func NodeFromBytes(msg []byte) Node {
|
||||
keys, values, count := message.GetKeysAndValues(msg)
|
||||
return Node{
|
||||
|
||||
@@ -85,7 +85,7 @@ func randomTree(t *testing.T, count int) (Node, [][2]Item, NodeStore) {
|
||||
chkr, err := newEmptyChunker(ctx, ns, serializer)
|
||||
require.NoError(t, err)
|
||||
|
||||
items := randomTupleItemPairs(count / 2)
|
||||
items := randomTupleItemPairs(count/2, ns)
|
||||
for _, item := range items {
|
||||
err = chkr.AddPair(ctx, Item(item[0]), Item(item[1]))
|
||||
assert.NoError(t, err)
|
||||
@@ -112,8 +112,8 @@ func searchTestTree(item Item, nd Node) int {
|
||||
})
|
||||
}
|
||||
|
||||
func randomTupleItemPairs(count int) (items [][2]Item) {
|
||||
tups := RandomTuplePairs(count, keyDesc, valDesc)
|
||||
func randomTupleItemPairs(count int, ns NodeStore) (items [][2]Item) {
|
||||
tups := RandomTuplePairs(count, keyDesc, valDesc, ns)
|
||||
items = make([][2]Item, count)
|
||||
if len(tups) != len(items) {
|
||||
panic("mismatch")
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package tree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
@@ -43,14 +44,14 @@ func NewTupleLeafNode(keys, values []val.Tuple) Node {
|
||||
return newLeafNode(ks, vs)
|
||||
}
|
||||
|
||||
func RandomTuplePairs(count int, keyDesc, valDesc val.TupleDesc) (items [][2]val.Tuple) {
|
||||
func RandomTuplePairs(count int, keyDesc, valDesc val.TupleDesc, ns NodeStore) (items [][2]val.Tuple) {
|
||||
keyBuilder := val.NewTupleBuilder(keyDesc)
|
||||
valBuilder := val.NewTupleBuilder(valDesc)
|
||||
|
||||
items = make([][2]val.Tuple, count)
|
||||
for i := range items {
|
||||
items[i][0] = RandomTuple(keyBuilder)
|
||||
items[i][1] = RandomTuple(valBuilder)
|
||||
items[i][0] = RandomTuple(keyBuilder, ns)
|
||||
items[i][1] = RandomTuple(valBuilder, ns)
|
||||
}
|
||||
|
||||
dupes := make([]int, 0, count)
|
||||
@@ -70,14 +71,14 @@ func RandomTuplePairs(count int, keyDesc, valDesc val.TupleDesc) (items [][2]val
|
||||
|
||||
// replace duplicates and validate again
|
||||
for _, d := range dupes {
|
||||
items[d][0] = RandomTuple(keyBuilder)
|
||||
items[d][0] = RandomTuple(keyBuilder, ns)
|
||||
}
|
||||
dupes = dupes[:0]
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func RandomCompositeTuplePairs(count int, keyDesc, valDesc val.TupleDesc) (items [][2]val.Tuple) {
|
||||
func RandomCompositeTuplePairs(count int, keyDesc, valDesc val.TupleDesc, ns NodeStore) (items [][2]val.Tuple) {
|
||||
// preconditions
|
||||
if count%5 != 0 {
|
||||
panic("expected empty divisible by 5")
|
||||
@@ -86,7 +87,7 @@ func RandomCompositeTuplePairs(count int, keyDesc, valDesc val.TupleDesc) (items
|
||||
panic("expected composite key")
|
||||
}
|
||||
|
||||
tt := RandomTuplePairs(count, keyDesc, valDesc)
|
||||
tt := RandomTuplePairs(count, keyDesc, valDesc, ns)
|
||||
|
||||
tuples := make([][2]val.Tuple, len(tt)*3)
|
||||
for i := range tuples {
|
||||
@@ -125,9 +126,9 @@ func AscendingUintTuples(count int) (tuples [][2]val.Tuple, desc val.TupleDesc)
|
||||
return
|
||||
}
|
||||
|
||||
func RandomTuple(tb *val.TupleBuilder) (tup val.Tuple) {
|
||||
func RandomTuple(tb *val.TupleBuilder, ns NodeStore) (tup val.Tuple) {
|
||||
for i, typ := range tb.Desc.Types {
|
||||
randomField(tb, i, typ)
|
||||
randomField(tb, i, typ, ns)
|
||||
}
|
||||
return tb.Build(sharedPool)
|
||||
}
|
||||
@@ -181,7 +182,7 @@ func deduplicateTuples(desc val.TupleDesc, tups [][2]val.Tuple) (uniq [][2]val.T
|
||||
return
|
||||
}
|
||||
|
||||
func randomField(tb *val.TupleBuilder, idx int, typ val.Type) {
|
||||
func randomField(tb *val.TupleBuilder, idx int, typ val.Type, ns NodeStore) {
|
||||
// todo(andy): add NULLs
|
||||
|
||||
neg := -1
|
||||
@@ -230,10 +231,19 @@ func randomField(tb *val.TupleBuilder, idx int, typ val.Type) {
|
||||
buf := make([]byte, 16)
|
||||
testRand.Read(buf)
|
||||
tb.PutHash128(idx, buf)
|
||||
case val.AddressEnc:
|
||||
case val.CommitAddrEnc:
|
||||
buf := make([]byte, 20)
|
||||
testRand.Read(buf)
|
||||
tb.PutAddress(idx, hash.New(buf))
|
||||
tb.PutCommitAddr(idx, hash.New(buf))
|
||||
case val.BytesAddrEnc:
|
||||
buf := make([]byte, (testRand.Int63()%40)+10)
|
||||
testRand.Read(buf)
|
||||
tree, err := NewImmutableTreeFromReader(context.Background(), bytes.NewReader(buf), ns, DefaultFixedChunkLength)
|
||||
if err != nil {
|
||||
panic("failed to write blob tree")
|
||||
}
|
||||
tb.PutBytesAddr(idx, tree.Addr)
|
||||
|
||||
default:
|
||||
panic("unknown encoding")
|
||||
}
|
||||
|
||||
25
go/store/val/byte_array.go
Normal file
25
go/store/val/byte_array.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2022 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 "github.com/dolthub/dolt/go/store/hash"
|
||||
|
||||
type BytesAddr struct {
|
||||
Addr hash.Hash
|
||||
}
|
||||
|
||||
func NewBytesAddr(addr hash.Hash) BytesAddr {
|
||||
return BytesAddr{Addr: addr}
|
||||
}
|
||||
@@ -53,39 +53,40 @@ const (
|
||||
float64Size ByteSize = 8
|
||||
bit64Size ByteSize = 8
|
||||
hash128Size ByteSize = 16
|
||||
addressSize ByteSize = 20
|
||||
yearSize ByteSize = 1
|
||||
dateSize ByteSize = 4
|
||||
timeSize ByteSize = 8
|
||||
datetimeSize ByteSize = 8
|
||||
enumSize ByteSize = 2
|
||||
setSize ByteSize = 8
|
||||
addrSize ByteSize = hash.ByteLen
|
||||
)
|
||||
|
||||
type Encoding byte
|
||||
|
||||
// Fixed Width Encodings
|
||||
const (
|
||||
NullEnc = Encoding(serial.EncodingNull)
|
||||
Int8Enc = Encoding(serial.EncodingInt8)
|
||||
Uint8Enc = Encoding(serial.EncodingUint8)
|
||||
Int16Enc = Encoding(serial.EncodingInt16)
|
||||
Uint16Enc = Encoding(serial.EncodingUint16)
|
||||
Int32Enc = Encoding(serial.EncodingInt32)
|
||||
Uint32Enc = Encoding(serial.EncodingUint32)
|
||||
Int64Enc = Encoding(serial.EncodingInt64)
|
||||
Uint64Enc = Encoding(serial.EncodingUint64)
|
||||
Float32Enc = Encoding(serial.EncodingFloat32)
|
||||
Float64Enc = Encoding(serial.EncodingFloat64)
|
||||
Bit64Enc = Encoding(serial.EncodingBit64)
|
||||
Hash128Enc = Encoding(serial.EncodingHash128)
|
||||
AddressEnc = Encoding(serial.EncodingAddress)
|
||||
YearEnc = Encoding(serial.EncodingYear)
|
||||
DateEnc = Encoding(serial.EncodingDate)
|
||||
TimeEnc = Encoding(serial.EncodingTime)
|
||||
DatetimeEnc = Encoding(serial.EncodingDatetime)
|
||||
EnumEnc = Encoding(serial.EncodingEnum)
|
||||
SetEnc = Encoding(serial.EncodingSet)
|
||||
NullEnc = Encoding(serial.EncodingNull)
|
||||
Int8Enc = Encoding(serial.EncodingInt8)
|
||||
Uint8Enc = Encoding(serial.EncodingUint8)
|
||||
Int16Enc = Encoding(serial.EncodingInt16)
|
||||
Uint16Enc = Encoding(serial.EncodingUint16)
|
||||
Int32Enc = Encoding(serial.EncodingInt32)
|
||||
Uint32Enc = Encoding(serial.EncodingUint32)
|
||||
Int64Enc = Encoding(serial.EncodingInt64)
|
||||
Uint64Enc = Encoding(serial.EncodingUint64)
|
||||
Float32Enc = Encoding(serial.EncodingFloat32)
|
||||
Float64Enc = Encoding(serial.EncodingFloat64)
|
||||
Bit64Enc = Encoding(serial.EncodingBit64)
|
||||
Hash128Enc = Encoding(serial.EncodingHash128)
|
||||
YearEnc = Encoding(serial.EncodingYear)
|
||||
DateEnc = Encoding(serial.EncodingDate)
|
||||
TimeEnc = Encoding(serial.EncodingTime)
|
||||
DatetimeEnc = Encoding(serial.EncodingDatetime)
|
||||
EnumEnc = Encoding(serial.EncodingEnum)
|
||||
SetEnc = Encoding(serial.EncodingSet)
|
||||
BytesAddrEnc = Encoding(serial.EncodingBytesAddr)
|
||||
CommitAddrEnc = Encoding(serial.EncodingCommitAddr)
|
||||
|
||||
sentinel Encoding = 127
|
||||
)
|
||||
@@ -132,8 +133,8 @@ func sizeFromType(t Type) (ByteSize, bool) {
|
||||
return float64Size, true
|
||||
case Hash128Enc:
|
||||
return hash128Size, true
|
||||
case AddressEnc:
|
||||
return addressSize, true
|
||||
case BytesAddrEnc:
|
||||
return addrSize, true
|
||||
case YearEnc:
|
||||
return yearSize, true
|
||||
case DateEnc:
|
||||
@@ -559,22 +560,17 @@ func writeHash128(buf, val []byte) {
|
||||
copy(buf, val)
|
||||
}
|
||||
|
||||
func readAddress(val []byte) []byte {
|
||||
expectSize(val, addressSize)
|
||||
return val
|
||||
}
|
||||
|
||||
func writeAddress(buf []byte, val hash.Hash) {
|
||||
expectSize(buf, addressSize)
|
||||
copy(buf, val[:])
|
||||
}
|
||||
|
||||
func compareHash128(l, r []byte) int {
|
||||
return bytes.Compare(l, r)
|
||||
}
|
||||
|
||||
func compareAddress(l, r []byte) int {
|
||||
return bytes.Compare(l, r)
|
||||
func compareBytesAddr(l, r hash.Hash) int {
|
||||
// TODO sort
|
||||
return l.Compare(r)
|
||||
}
|
||||
|
||||
func compareCommitAddr(l, r hash.Hash) int {
|
||||
return l.Compare(r)
|
||||
}
|
||||
|
||||
func writeRaw(buf, val []byte) {
|
||||
@@ -582,6 +578,16 @@ func writeRaw(buf, val []byte) {
|
||||
copy(buf, val)
|
||||
}
|
||||
|
||||
func writeAddr(buf []byte, v []byte) {
|
||||
expectSize(buf, addrSize)
|
||||
copy(buf, v)
|
||||
}
|
||||
|
||||
func readAddr(val []byte) hash.Hash {
|
||||
expectSize(val, addrSize)
|
||||
return hash.New(val)
|
||||
}
|
||||
|
||||
func expectSize(buf []byte, sz ByteSize) {
|
||||
if ByteSize(len(buf)) != sz {
|
||||
panic("byte slice is not of expected size")
|
||||
|
||||
@@ -127,6 +127,29 @@ func allocateTuple(pool pool.BuffPool, bufSz ByteSize, fields int) (tup Tuple, o
|
||||
return
|
||||
}
|
||||
|
||||
func (tup Tuple) GetOffset(i int) (int, bool) {
|
||||
cnt := tup.Count()
|
||||
if i >= cnt {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
sz := ByteSize(len(tup))
|
||||
split := sz - uint16Size*ByteSize(cnt)
|
||||
offs := tup[split : sz-countSize]
|
||||
|
||||
start, stop := uint16(0), uint16(split)
|
||||
if i*2 < len(offs) {
|
||||
pos := i * 2
|
||||
stop = readUint16(offs[pos : pos+2])
|
||||
}
|
||||
if i > 0 {
|
||||
pos := (i - 1) * 2
|
||||
start = readUint16(offs[pos : pos+2])
|
||||
}
|
||||
|
||||
return int(start), start == stop
|
||||
}
|
||||
|
||||
// GetField returns the value for field |i|.
|
||||
func (tup Tuple) GetField(i int) []byte {
|
||||
cnt := tup.Count()
|
||||
|
||||
@@ -263,11 +263,11 @@ func (tb *TupleBuilder) PutHash128(i int, v []byte) {
|
||||
tb.pos += hash128Size
|
||||
}
|
||||
|
||||
func (tb *TupleBuilder) PutAddress(i int, v hash.Hash) {
|
||||
tb.Desc.expectEncoding(i, AddressEnc)
|
||||
tb.fields[i] = tb.buf[tb.pos : tb.pos+addressSize]
|
||||
writeAddress(tb.fields[i], v)
|
||||
tb.pos += addressSize
|
||||
func (tb *TupleBuilder) PutCommitAddr(i int, v hash.Hash) {
|
||||
tb.Desc.expectEncoding(i, CommitAddrEnc)
|
||||
tb.fields[i] = tb.buf[tb.pos : tb.pos+addrSize]
|
||||
writeAddr(tb.fields[i], v[:])
|
||||
tb.pos += addrSize
|
||||
}
|
||||
|
||||
// PutRaw writes a []byte to the ith field of the Tuple being built.
|
||||
@@ -282,3 +282,11 @@ func (tb *TupleBuilder) PutRaw(i int, buf []byte) {
|
||||
writeRaw(tb.fields[i], buf)
|
||||
tb.pos += sz
|
||||
}
|
||||
|
||||
// PutBytesAddr writes an out of band []byte to the ith field of the Tuple being built.
|
||||
func (tb *TupleBuilder) PutBytesAddr(i int, v hash.Hash) {
|
||||
tb.Desc.expectEncoding(i, BytesAddrEnc)
|
||||
tb.fields[i] = tb.buf[tb.pos : tb.pos+addrSize]
|
||||
writeAddr(tb.fields[i], v[:])
|
||||
tb.pos += addrSize
|
||||
}
|
||||
|
||||
@@ -112,8 +112,10 @@ func compare(typ Type, left, right []byte) int {
|
||||
return compareByteString(readByteString(left), readByteString(right))
|
||||
case Hash128Enc:
|
||||
return compareHash128(readHash128(left), readHash128(right))
|
||||
case AddressEnc:
|
||||
return compareAddress(readAddress(left), readAddress(right))
|
||||
case BytesAddrEnc:
|
||||
return compareBytesAddr(readAddr(left), readAddr(right))
|
||||
case CommitAddrEnc:
|
||||
return compareCommitAddr(readAddr(left), readAddr(right))
|
||||
default:
|
||||
panic("unknown encoding")
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// TupleDesc describes a Tuple set.
|
||||
@@ -32,6 +32,7 @@ type TupleDesc struct {
|
||||
Types []Type
|
||||
cmp TupleComparator
|
||||
fast fixedAccess
|
||||
Addrs []int
|
||||
}
|
||||
|
||||
// NewTupleDescriptor makes a TupleDescriptor from |types|.
|
||||
@@ -51,10 +52,18 @@ func NewTupleDescriptorWithComparator(cmp TupleComparator, types ...Type) (td Tu
|
||||
}
|
||||
}
|
||||
|
||||
var addrIdxs []int
|
||||
for i, t := range types {
|
||||
if t.Enc == BytesAddrEnc {
|
||||
addrIdxs = append(addrIdxs, i)
|
||||
}
|
||||
}
|
||||
|
||||
td = TupleDesc{
|
||||
Types: types,
|
||||
cmp: cmp,
|
||||
fast: makeFixedAccess(types),
|
||||
Addrs: addrIdxs,
|
||||
}
|
||||
|
||||
return
|
||||
@@ -399,8 +408,17 @@ func (td TupleDesc) GetHash128(i int, tup Tuple) (v []byte, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (td TupleDesc) GetAddress(i int, tup Tuple) (v hash.Hash, ok bool) {
|
||||
td.expectEncoding(i, AddressEnc)
|
||||
func (td TupleDesc) GetBlob(i int, tup Tuple) (BytesAddr, bool) {
|
||||
td.expectEncoding(i, BytesAddrEnc)
|
||||
b := td.GetField(i, tup)
|
||||
if len(b) == 0 {
|
||||
return BytesAddr{}, false
|
||||
}
|
||||
return NewBytesAddr(hash.New(b)), true
|
||||
}
|
||||
|
||||
func (td TupleDesc) GetCommitAddr(i int, tup Tuple) (v hash.Hash, ok bool) {
|
||||
td.expectEncoding(i, CommitAddrEnc)
|
||||
b := td.GetField(i, tup)
|
||||
if b != nil {
|
||||
v = hash.New(b)
|
||||
@@ -508,7 +526,9 @@ func formatValue(enc Encoding, value []byte) string {
|
||||
return string(value)
|
||||
case Hash128Enc:
|
||||
return string(value)
|
||||
case AddressEnc:
|
||||
case BytesAddrEnc:
|
||||
return string(value)
|
||||
case CommitAddrEnc:
|
||||
return string(value)
|
||||
default:
|
||||
return string(value)
|
||||
|
||||
@@ -5,7 +5,7 @@ setup() {
|
||||
setup_common
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
pk varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
c1 LONGTEXT COMMENT 'tag:1',
|
||||
c2 LONGTEXT COMMENT 'tag:2',
|
||||
c3 LONGTEXT COMMENT 'tag:3',
|
||||
|
||||
@@ -19,7 +19,7 @@ setup_repository() {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE blame_test (
|
||||
pk1 BIGINT NOT NULL COMMENT 'tag:0',
|
||||
pk2 TEXT,
|
||||
pk2 varchar(40),
|
||||
name LONGTEXT COMMENT 'tag:1',
|
||||
PRIMARY KEY (pk1, pk2)
|
||||
);
|
||||
@@ -206,4 +206,4 @@ SQL
|
||||
[ "$status" -eq 0 ]
|
||||
echo -e "OUTPUT:\n $output"
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ DELIM
|
||||
dolt sql -q "insert into test values (1, 1, 1, 1, 1, 1)"
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`id\` LONGTEXT NOT NULL,
|
||||
\`id\` varchar(20) NOT NULL,
|
||||
\`first name\` LONGTEXT,
|
||||
\`last name\` LONGTEXT,
|
||||
\`title\` LONGTEXT,
|
||||
@@ -673,4 +673,4 @@ SQL
|
||||
|
||||
run dolt diff HEAD~1
|
||||
[ "${#lines[@]}" -eq 2007 ] # 2000 diffs + 6 for top rows before data + 1 for bottom row of table
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,7 +713,7 @@ SQL
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ! "$output" =~ "dolt_docs" ]] || false
|
||||
|
||||
run dolt sql -q "CREATE TABLE dolt_docs (doc_name TEXT, doc_text LONGTEXT, PRIMARY KEY(doc_name))"
|
||||
run dolt sql -q "CREATE TABLE dolt_docs (doc_name varchar(20), doc_text varchar(20), PRIMARY KEY(doc_name))"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" =~ "reserved" ]] || false
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ CREATE TABLE test_int (
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
CREATE TABLE test_string (
|
||||
pk LONGTEXT NOT NULL,
|
||||
pk varchar(20) NOT NULL,
|
||||
c1 LONGTEXT,
|
||||
c2 LONGTEXT,
|
||||
c3 LONGTEXT,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
CREATE TABLE employees (
|
||||
`id` LONGTEXT NOT NULL,
|
||||
`id` varchar(20) NOT NULL,
|
||||
`first name` LONGTEXT,
|
||||
`last name` LONGTEXT,
|
||||
`title` LONGTEXT,
|
||||
`start date` LONGTEXT,
|
||||
`end date` LONGTEXT,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
);
|
||||
|
||||
@@ -485,7 +485,7 @@ SQL
|
||||
@test "import-create-tables: create a table with null values from json import with json file" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk LONGTEXT NOT NULL,
|
||||
pk varchar(20) NOT NULL,
|
||||
headerOne LONGTEXT,
|
||||
headerTwo BIGINT,
|
||||
PRIMARY KEY (pk)
|
||||
@@ -512,7 +512,7 @@ SQL
|
||||
@test "import-create-tables: fail to create a table with null values from json import with json file" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk LONGTEXT NOT NULL,
|
||||
pk varchar(20) NOT NULL,
|
||||
headerOne LONGTEXT NOT NULL,
|
||||
headerTwo BIGINT NOT NULL,
|
||||
PRIMARY KEY (pk)
|
||||
|
||||
@@ -103,7 +103,7 @@ SQL
|
||||
@test "import-replace-tables: replace table using json" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`id\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`id\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first name\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last name\` LONGTEXT COMMENT 'tag:2',
|
||||
\`title\` LONGTEXT COMMENT 'tag:3',
|
||||
@@ -121,7 +121,7 @@ SQL
|
||||
@test "import-replace-tables: replace table using json with wrong schema" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`idz\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`idz\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first namez\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last namez\` LONGTEXT COMMENT 'tag:2',
|
||||
\`titlez\` LONGTEXT COMMENT 'tag:3',
|
||||
@@ -140,7 +140,7 @@ SQL
|
||||
@test "import-replace-tables: replace table using schema with json" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`idz\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`idz\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first namez\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last namez\` LONGTEXT COMMENT 'tag:2',
|
||||
\`titlez\` LONGTEXT COMMENT 'tag:3',
|
||||
@@ -182,7 +182,7 @@ SQL
|
||||
@test "import-replace-tables: replace table with bad json" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`id\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`id\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first name\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last name\` LONGTEXT COMMENT 'tag:2',
|
||||
\`title\` LONGTEXT COMMENT 'tag:3',
|
||||
@@ -199,7 +199,7 @@ SQL
|
||||
@test "import-replace-tables: replace table using xlsx file" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`id\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`id\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last\` LONGTEXT COMMENT 'tag:2',
|
||||
\`title\` LONGTEXT COMMENT 'tag:3',
|
||||
@@ -217,7 +217,7 @@ SQL
|
||||
@test "import-replace-tables: replace table using xlsx file with wrong schema" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`id\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`id\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first name\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last name\` LONGTEXT COMMENT 'tag:2',
|
||||
\`title\` LONGTEXT COMMENT 'tag:3',
|
||||
@@ -253,7 +253,7 @@ SQL
|
||||
@test "import-replace-tables: replace table with a json with columns in different order" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`id\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`id\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first name\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last name\` LONGTEXT COMMENT 'tag:2',
|
||||
\`title\` LONGTEXT COMMENT 'tag:3',
|
||||
@@ -271,7 +271,7 @@ SQL
|
||||
@test "import-replace-tables: replace table with a csv with columns in different order" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`id\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`id\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first name\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last name\` LONGTEXT COMMENT 'tag:2',
|
||||
\`title\` LONGTEXT COMMENT 'tag:3',
|
||||
|
||||
@@ -37,7 +37,7 @@ DELIM
|
||||
|
||||
cat <<SQL > employees-sch.sql
|
||||
CREATE TABLE employees (
|
||||
\`id\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`id\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first name\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last name\` LONGTEXT COMMENT 'tag:2',
|
||||
\`title\` LONGTEXT COMMENT 'tag:3',
|
||||
@@ -158,7 +158,7 @@ teardown() {
|
||||
@test "import-update-tables: update table using csv with newlines" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE test (
|
||||
pk LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
pk varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
c1 LONGTEXT COMMENT 'tag:1',
|
||||
c2 LONGTEXT COMMENT 'tag:2',
|
||||
c3 LONGTEXT COMMENT 'tag:3',
|
||||
@@ -182,7 +182,7 @@ SQL
|
||||
@test "import-update-tables: update table using wrong json" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`idz\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`idz\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first namez\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last namez\` LONGTEXT COMMENT 'tag:2',
|
||||
\`titlez\` LONGTEXT COMMENT 'tag:3',
|
||||
@@ -221,7 +221,7 @@ SQL
|
||||
@test "import-update-tables: update table with a json with columns in different order" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`id\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`id\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first name\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last name\` LONGTEXT COMMENT 'tag:2',
|
||||
\`title\` LONGTEXT COMMENT 'tag:3',
|
||||
@@ -247,7 +247,7 @@ SQL
|
||||
@test "import-update-tables: update table with a csv with columns in different order" {
|
||||
dolt sql <<SQL
|
||||
CREATE TABLE employees (
|
||||
\`id\` LONGTEXT NOT NULL COMMENT 'tag:0',
|
||||
\`id\` varchar(20) NOT NULL COMMENT 'tag:0',
|
||||
\`first name\` LONGTEXT COMMENT 'tag:1',
|
||||
\`last name\` LONGTEXT COMMENT 'tag:2',
|
||||
\`title\` LONGTEXT COMMENT 'tag:3',
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -704,8 +704,7 @@ SQL
|
||||
CREATE TABLE test (
|
||||
pk BIGINT NOT NULL,
|
||||
v LONGTEXT,
|
||||
PRIMARY KEY (pk),
|
||||
INDEX (v)
|
||||
PRIMARY KEY (pk)
|
||||
);
|
||||
SQL
|
||||
run dolt schema show
|
||||
|
||||
Reference in New Issue
Block a user