github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/datas/stash.go (about)

     1  // Copyright 2023 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package datas
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"strings"
    21  
    22  	flatbuffers "github.com/dolthub/flatbuffers/v23/go"
    23  
    24  	"github.com/dolthub/dolt/go/gen/fb/serial"
    25  	"github.com/dolthub/dolt/go/store/hash"
    26  	"github.com/dolthub/dolt/go/store/types"
    27  )
    28  
    29  const (
    30  	stashListName = "StashList"
    31  )
    32  
    33  // NewStash creates a new stash object.
    34  func NewStash(ctx context.Context, nbf *types.NomsBinFormat, vrw types.ValueReadWriter, stashRef types.Ref, headAddr hash.Hash, meta *StashMeta) (hash.Hash, types.Ref, error) {
    35  	if nbf.UsesFlatbuffers() {
    36  		headCommit, err := vrw.ReadValue(ctx, headAddr)
    37  		if err != nil {
    38  			return hash.Hash{}, types.Ref{}, err
    39  		}
    40  
    41  		isCommit, err := IsCommit(headCommit)
    42  		if err != nil {
    43  			return hash.Hash{}, types.Ref{}, err
    44  		}
    45  		if !isCommit {
    46  			return hash.Hash{}, types.Ref{}, errors.New("newStash: headAddr does not point to a commit")
    47  		}
    48  
    49  		headRef, err := types.NewRef(headCommit, nbf)
    50  		if err != nil {
    51  			return hash.Hash{}, types.Ref{}, err
    52  		}
    53  
    54  		data := stash_flatbuffer(stashRef.TargetHash(), headRef.TargetHash(), meta)
    55  		r, err := vrw.WriteValue(ctx, types.SerialMessage(data))
    56  		if err != nil {
    57  			return hash.Hash{}, types.Ref{}, err
    58  		}
    59  
    60  		ref, err := types.ToRefOfValue(r, nbf)
    61  		if err != nil {
    62  			return hash.Hash{}, types.Ref{}, err
    63  		}
    64  
    65  		return ref.TargetHash(), ref, nil
    66  	} else {
    67  		return hash.Hash{}, types.Ref{}, errors.New("newStash: stash is not supported for old storage format")
    68  	}
    69  }
    70  
    71  // GetStashData takes types.Value, which should be of type types.SerialMessage as stash is supported only for new format.
    72  // This function returns stashRoot address hash, head commit address hash and stash meta, which contains branch name
    73  // that stash was made on and head commit meta description.
    74  func GetStashData(val types.Value) (hash.Hash, hash.Hash, *StashMeta, error) {
    75  	bs := []byte(val.(types.SerialMessage))
    76  	var msg serial.Stash
    77  	err := serial.InitStashRoot(&msg, bs, serial.MessagePrefixSz)
    78  	if err != nil {
    79  		return hash.Hash{}, hash.Hash{}, nil, err
    80  	}
    81  
    82  	var tblsToStage []string
    83  	at := msg.TablesToStageLength()
    84  	if at > 0 {
    85  		tblsToStage = make([]string, at)
    86  		for i := range tblsToStage {
    87  			tblsToStage[i] = string(msg.TablesToStage(i))
    88  		}
    89  	}
    90  
    91  	meta := NewStashMeta(string(msg.BranchName()), string(msg.Desc()), tblsToStage)
    92  	stashRootAddr := hash.New(msg.StashRootAddrBytes())
    93  	headCommitAddr := hash.New(msg.HeadCommitAddrBytes())
    94  
    95  	return stashRootAddr, headCommitAddr, meta, err
    96  }
    97  
    98  func stash_flatbuffer(stash, head hash.Hash, meta *StashMeta) serial.Message {
    99  	builder := flatbuffers.NewBuilder(1024)
   100  	stashOff := builder.CreateByteVector(stash[:])
   101  	headOff := builder.CreateByteVector(head[:])
   102  	branchNameOff := builder.CreateString(meta.BranchName)
   103  	descOff := builder.CreateString(meta.Description)
   104  	var (
   105  		addedTblsOff flatbuffers.UOffsetT
   106  	)
   107  	if meta.TablesToStage != nil {
   108  		addedTblsOff = SerializeStringVector(builder, meta.TablesToStage)
   109  	}
   110  
   111  	serial.StashStart(builder)
   112  	serial.StashAddStashRootAddr(builder, stashOff)
   113  	serial.StashAddHeadCommitAddr(builder, headOff)
   114  	serial.StashAddBranchName(builder, branchNameOff)
   115  	serial.StashAddDesc(builder, descOff)
   116  	serial.StashAddTablesToStage(builder, addedTblsOff)
   117  
   118  	return serial.FinishMessage(builder, serial.StashEnd(builder), []byte(serial.StashFileID))
   119  }
   120  
   121  func SerializeStringVector(b *flatbuffers.Builder, s []string) flatbuffers.UOffsetT {
   122  	offs := make([]flatbuffers.UOffsetT, len(s))
   123  	for j := len(s) - 1; j >= 0; j-- {
   124  		offs[j] = b.CreateString(s[j])
   125  	}
   126  	b.StartVector(4, len(s), 4)
   127  	for j := len(s) - 1; j >= 0; j-- {
   128  		b.PrependUOffsetT(offs[j])
   129  	}
   130  	return b.EndVector(len(s))
   131  }
   132  
   133  // StashMeta contains all the metadata that is associated with a stash within a data repo.
   134  // The BranchName is the name of the branch that the stash was made on.
   135  // The Description is the head commit description of the branch that the stash was made on.
   136  // The TablesToStage is array of table names that needs to be staged when popping the stash.
   137  // These tables were added tables that were staged when stashing.
   138  type StashMeta struct {
   139  	BranchName    string
   140  	Description   string
   141  	TablesToStage []string
   142  }
   143  
   144  // NewStashMeta returns StashMeta that can be used to create a stash.
   145  func NewStashMeta(name, desc string, tblsToStage []string) *StashMeta {
   146  	bn := strings.TrimSpace(name)
   147  	d := strings.TrimSpace(desc)
   148  
   149  	return &StashMeta{bn, d, tblsToStage}
   150  }