mirror of
https://github.com/dolthub/dolt.git
synced 2026-04-21 02:57:46 -05:00
273 lines
8.1 KiB
Go
Executable File
273 lines
8.1 KiB
Go
Executable File
// 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 datas
|
|
|
|
import (
|
|
"context"
|
|
|
|
flatbuffers "github.com/google/flatbuffers/go"
|
|
|
|
"github.com/dolthub/dolt/go/gen/fb/serial"
|
|
"github.com/dolthub/dolt/go/store/hash"
|
|
"github.com/dolthub/dolt/go/store/types"
|
|
)
|
|
|
|
const (
|
|
workingSetName = "WorkingSet"
|
|
workingSetMetaField = "meta"
|
|
workingRootRefField = "workingRootRef"
|
|
stagedRootRefField = "stagedRootRef"
|
|
mergeStateField = "mergeState"
|
|
)
|
|
|
|
const (
|
|
mergeStateName = "MergeState"
|
|
mergeStateCommitField = "commit"
|
|
mergeStateWorkingPreMergeField = "workingPreMerge"
|
|
)
|
|
|
|
const (
|
|
workingSetMetaName = "WorkingSetMeta"
|
|
workingSetMetaNameField = "name"
|
|
workingSetMetaEmailField = "email"
|
|
workingSetMetaTimestampField = "timestamp"
|
|
workingSetMetaDescriptionField = "description"
|
|
workingSetMetaVersionField = "version"
|
|
)
|
|
|
|
const workingSetMetaVersion = "1.0"
|
|
|
|
type WorkingSetMeta struct {
|
|
Name string
|
|
Email string
|
|
Description string
|
|
Timestamp uint64
|
|
}
|
|
|
|
func (m *WorkingSetMeta) toNomsStruct(format *types.NomsBinFormat) (types.Struct, error) {
|
|
fields := make(types.StructData)
|
|
fields[workingSetMetaNameField] = types.String(m.Name)
|
|
fields[workingSetMetaEmailField] = types.String(m.Email)
|
|
fields[workingSetMetaTimestampField] = types.Uint(m.Timestamp)
|
|
fields[workingSetMetaDescriptionField] = types.String(m.Description)
|
|
fields[workingSetMetaVersionField] = types.String(workingSetMetaVersion)
|
|
return types.NewStruct(format, workingSetMetaName, fields)
|
|
}
|
|
|
|
func workingSetMetaFromWorkingSetSt(workingSetSt types.Struct) (*WorkingSetMeta, error) {
|
|
metaV, ok, err := workingSetSt.MaybeGet(workingSetMetaNameField)
|
|
if err != nil || !ok {
|
|
return nil, err
|
|
}
|
|
return workingSetMetaFromNomsSt(metaV.(types.Struct))
|
|
}
|
|
|
|
var mergeStateTemplate = types.MakeStructTemplate(mergeStateName, []string{mergeStateCommitField, mergeStateWorkingPreMergeField})
|
|
|
|
type WorkingSetSpec struct {
|
|
Meta *WorkingSetMeta
|
|
WorkingRoot types.Ref
|
|
StagedRoot types.Ref
|
|
MergeState *MergeState
|
|
}
|
|
|
|
// NewWorkingSet creates a new working set object.
|
|
// A working set is a value that has been persisted but is not necessarily referenced by a Commit. As the name implies,
|
|
// it's storage for data changes that have not yet been incorporated into the commit graph but need durable storage.
|
|
//
|
|
// A working set struct has the following type:
|
|
//
|
|
// ```
|
|
//
|
|
// struct WorkingSet {
|
|
// meta: M,
|
|
// workingRootRef: R,
|
|
// stagedRootRef: R,
|
|
// mergeState: R,
|
|
// }
|
|
//
|
|
// ```
|
|
// where M is a struct type and R is a ref type.
|
|
func newWorkingSet(ctx context.Context, db *database, meta *WorkingSetMeta, workingRef, stagedRef types.Ref, mergeState *MergeState) (hash.Hash, types.Ref, error) {
|
|
if db.Format().UsesFlatbuffers() {
|
|
stagedAddr := stagedRef.TargetHash()
|
|
data := workingset_flatbuffer(workingRef.TargetHash(), &stagedAddr, mergeState, meta)
|
|
|
|
r, err := db.WriteValue(ctx, types.SerialMessage(data))
|
|
if err != nil {
|
|
return hash.Hash{}, types.Ref{}, err
|
|
}
|
|
|
|
ref, err := types.ToRefOfValue(r, db.Format())
|
|
if err != nil {
|
|
return hash.Hash{}, types.Ref{}, err
|
|
}
|
|
|
|
return ref.TargetHash(), ref, nil
|
|
}
|
|
|
|
metaSt, err := meta.toNomsStruct(workingRef.Format())
|
|
if err != nil {
|
|
return hash.Hash{}, types.Ref{}, err
|
|
}
|
|
|
|
fields := make(types.StructData)
|
|
fields[workingSetMetaField] = metaSt
|
|
fields[workingRootRefField] = workingRef
|
|
fields[stagedRootRefField] = stagedRef
|
|
|
|
if mergeState != nil {
|
|
fields[mergeStateField] = *mergeState.nomsMergeStateRef
|
|
}
|
|
|
|
st, err := types.NewStruct(workingRef.Format(), workingSetName, fields)
|
|
if err != nil {
|
|
return hash.Hash{}, types.Ref{}, err
|
|
}
|
|
|
|
wsRef, err := db.WriteValue(ctx, st)
|
|
if err != nil {
|
|
return hash.Hash{}, types.Ref{}, err
|
|
}
|
|
|
|
ref, err := types.ToRefOfValue(wsRef, db.Format())
|
|
if err != nil {
|
|
return hash.Hash{}, types.Ref{}, err
|
|
}
|
|
|
|
return ref.TargetHash(), ref, nil
|
|
}
|
|
|
|
func workingset_flatbuffer(working hash.Hash, staged *hash.Hash, mergeState *MergeState, meta *WorkingSetMeta) serial.Message {
|
|
builder := flatbuffers.NewBuilder(1024)
|
|
workingoff := builder.CreateByteVector(working[:])
|
|
var stagedOff, mergeStateOff flatbuffers.UOffsetT
|
|
if staged != nil {
|
|
stagedOff = builder.CreateByteVector((*staged)[:])
|
|
}
|
|
if mergeState != nil {
|
|
prerootaddroff := builder.CreateByteVector((*mergeState.preMergeWorkingAddr)[:])
|
|
fromaddroff := builder.CreateByteVector((*mergeState.fromCommitAddr)[:])
|
|
serial.MergeStateStart(builder)
|
|
serial.MergeStateAddPreWorkingRootAddr(builder, prerootaddroff)
|
|
serial.MergeStateAddFromCommitAddr(builder, fromaddroff)
|
|
mergeStateOff = serial.MergeStateEnd(builder)
|
|
}
|
|
|
|
var nameOff, emailOff, descOff flatbuffers.UOffsetT
|
|
if meta != nil {
|
|
nameOff = builder.CreateString(meta.Name)
|
|
emailOff = builder.CreateString(meta.Email)
|
|
descOff = builder.CreateString(meta.Description)
|
|
}
|
|
|
|
serial.WorkingSetStart(builder)
|
|
serial.WorkingSetAddWorkingRootAddr(builder, workingoff)
|
|
if stagedOff != 0 {
|
|
serial.WorkingSetAddStagedRootAddr(builder, stagedOff)
|
|
}
|
|
if mergeStateOff != 0 {
|
|
serial.WorkingSetAddMergeState(builder, mergeStateOff)
|
|
}
|
|
if meta != nil {
|
|
serial.WorkingSetAddName(builder, nameOff)
|
|
serial.WorkingSetAddEmail(builder, emailOff)
|
|
serial.WorkingSetAddDesc(builder, descOff)
|
|
serial.WorkingSetAddTimestampMillis(builder, meta.Timestamp)
|
|
}
|
|
return serial.FinishMessage(builder, serial.WorkingSetEnd(builder), []byte(serial.WorkingSetFileID))
|
|
}
|
|
|
|
func NewMergeState(ctx context.Context, vrw types.ValueReadWriter, preMergeWorking types.Ref, commit *Commit) (*MergeState, error) {
|
|
if vrw.Format().UsesFlatbuffers() {
|
|
ms := &MergeState{
|
|
preMergeWorkingAddr: new(hash.Hash),
|
|
fromCommitAddr: new(hash.Hash),
|
|
}
|
|
*ms.preMergeWorkingAddr = preMergeWorking.TargetHash()
|
|
*ms.fromCommitAddr = commit.Addr()
|
|
return ms, nil
|
|
} else {
|
|
v, err := mergeStateTemplate.NewStruct(preMergeWorking.Format(), []types.Value{commit.NomsValue(), preMergeWorking})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ref, err := vrw.WriteValue(ctx, v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &MergeState{
|
|
nomsMergeStateRef: &ref,
|
|
nomsMergeState: &v,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
func IsWorkingSet(v types.Value) (bool, error) {
|
|
if s, ok := v.(types.Struct); ok {
|
|
// We're being more lenient here than in other checks, to make it more likely we can release changes to the
|
|
// working set data description in a backwards compatible way.
|
|
// types.IsValueSubtypeOf is very strict about the type description.
|
|
return s.Name() == workingSetName, nil
|
|
} else if sm, ok := v.(types.SerialMessage); ok {
|
|
return serial.GetFileID(sm) == serial.WorkingSetFileID, nil
|
|
} else {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
func workingSetMetaFromNomsSt(st types.Struct) (*WorkingSetMeta, error) {
|
|
// Like other places that deal with working set meta, we err on the side of leniency w.r.t. this data structure's
|
|
// contents
|
|
name, ok, err := st.MaybeGet(workingSetMetaNameField)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !ok {
|
|
name = types.String("not present")
|
|
}
|
|
|
|
email, ok, err := st.MaybeGet(workingSetMetaEmailField)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !ok {
|
|
email = types.String("not present")
|
|
}
|
|
|
|
timestamp, ok, err := st.MaybeGet(workingSetMetaTimestampField)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !ok {
|
|
timestamp = types.Uint(0)
|
|
}
|
|
|
|
description, ok, err := st.MaybeGet(workingSetMetaDescriptionField)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !ok {
|
|
description = types.String("not present")
|
|
}
|
|
|
|
return &WorkingSetMeta{
|
|
Name: string(name.(types.String)),
|
|
Email: string(email.(types.String)),
|
|
Timestamp: uint64(timestamp.(types.Uint)),
|
|
Description: string(description.(types.String)),
|
|
}, nil
|
|
}
|