214 lines
6.1 KiB
Go
214 lines
6.1 KiB
Go
// 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 prolly
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/dolthub/dolt/go/serial"
|
|
|
|
"github.com/dolthub/dolt/go/store/hash"
|
|
"github.com/dolthub/dolt/go/store/pool"
|
|
"github.com/dolthub/dolt/go/store/prolly/message"
|
|
"github.com/dolthub/dolt/go/store/prolly/tree"
|
|
"github.com/dolthub/dolt/go/store/types"
|
|
)
|
|
|
|
// Closure values are a long (8 bytes) followed by a hash (20 bytes, hash.ByteLen).
|
|
const prefixWidth = 8
|
|
|
|
type CommitClosureValue []byte
|
|
|
|
type CommitClosure struct {
|
|
closure tree.StaticMap[CommitClosureKey, CommitClosureValue, commitClosureKeyOrdering]
|
|
}
|
|
|
|
type commitClosureKeyOrdering struct{}
|
|
|
|
var _ tree.Ordering[CommitClosureKey] = commitClosureKeyOrdering{}
|
|
|
|
func (o commitClosureKeyOrdering) Compare(ctx context.Context, left, right CommitClosureKey) (int, error) {
|
|
lh, rh := left.Height(), right.Height()
|
|
if lh == rh {
|
|
return bytes.Compare(left[prefixWidth:], right[prefixWidth:]), nil
|
|
} else if lh < rh {
|
|
return -1, nil
|
|
}
|
|
return 1, nil
|
|
}
|
|
|
|
func NewEmptyCommitClosure(ns tree.NodeStore) (CommitClosure, error) {
|
|
// TODO: since this is constant, we should make a constant for it
|
|
serializer := message.NewCommitClosureSerializer(ns.Pool())
|
|
msg := serializer.Serialize(nil, nil, nil, 0)
|
|
node, fileId, err := tree.NodeFromBytes(msg)
|
|
if fileId != serial.CommitClosureFileID {
|
|
return CommitClosure{}, fmt.Errorf("unexpected file ID for commit closure, expected %s, found %s", serial.CommitClosureFileID, fileId)
|
|
}
|
|
if err != nil {
|
|
return CommitClosure{}, err
|
|
}
|
|
return NewCommitClosure(node, ns)
|
|
}
|
|
|
|
func NewCommitClosure(node *tree.Node, ns tree.NodeStore) (CommitClosure, error) {
|
|
return CommitClosure{
|
|
closure: tree.StaticMap[CommitClosureKey, CommitClosureValue, commitClosureKeyOrdering]{
|
|
Root: node,
|
|
NodeStore: ns,
|
|
Order: commitClosureKeyOrdering{},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (c CommitClosure) Count() (int, error) {
|
|
return c.closure.Count()
|
|
}
|
|
|
|
func (c CommitClosure) Height() int {
|
|
return c.closure.Height()
|
|
}
|
|
|
|
func (c CommitClosure) Node() *tree.Node {
|
|
return c.closure.Root
|
|
}
|
|
|
|
func (c CommitClosure) HashOf() hash.Hash {
|
|
return c.closure.HashOf()
|
|
}
|
|
|
|
func (c CommitClosure) Format() *types.NomsBinFormat {
|
|
return c.closure.NodeStore.Format()
|
|
}
|
|
|
|
func (c CommitClosure) Editor() CommitClosureEditor {
|
|
return CommitClosureEditor{
|
|
closure: c.closure.Mutate(),
|
|
}
|
|
}
|
|
|
|
func (c CommitClosure) IterAllReverse(ctx context.Context) (CommitClosureIter, error) {
|
|
return c.closure.IterAllReverse(ctx)
|
|
}
|
|
|
|
func (c CommitClosure) IterHeight(ctx context.Context, height uint64) (CommitClosureIter, error) {
|
|
start := NewCommitClosureKey(c.closure.NodeStore.Pool(), height, hash.Hash{})
|
|
stop := NewCommitClosureKey(c.closure.NodeStore.Pool(), height+1, hash.Hash{})
|
|
return c.closure.IterKeyRange(ctx, start, stop)
|
|
}
|
|
|
|
func (c CommitClosure) IsEmpty() bool {
|
|
return c.Node().Size() == 0
|
|
}
|
|
|
|
func (c CommitClosure) ContainsKey(ctx context.Context, h hash.Hash, height uint64) (bool, error) {
|
|
k := NewCommitClosureKey(c.closure.NodeStore.Pool(), height, h)
|
|
return c.closure.Has(ctx, k)
|
|
}
|
|
|
|
func (c CommitClosure) AsHashSet(ctx context.Context) (hash.HashSet, error) {
|
|
closureIter, err := c.IterAllReverse(ctx)
|
|
if err != nil {
|
|
return hash.HashSet{}, err
|
|
}
|
|
|
|
skipCmts := hash.NewHashSet()
|
|
for {
|
|
key, _, err := closureIter.Next(ctx)
|
|
if err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
break
|
|
}
|
|
return hash.HashSet{}, err
|
|
|
|
}
|
|
|
|
clsrHash := hash.New(key[prefixWidth:])
|
|
skipCmts.Insert(clsrHash)
|
|
}
|
|
|
|
return skipCmts, nil
|
|
}
|
|
|
|
type CommitClosureEditor struct {
|
|
closure tree.MutableMap[CommitClosureKey, CommitClosureValue, commitClosureKeyOrdering, tree.StaticMap[CommitClosureKey, CommitClosureValue, commitClosureKeyOrdering]]
|
|
}
|
|
|
|
type CommitClosureKey []byte
|
|
|
|
type CommitClosureIter tree.KvIter[CommitClosureKey, CommitClosureValue]
|
|
|
|
func NewCommitClosureKey(p pool.BuffPool, height uint64, addr hash.Hash) CommitClosureKey {
|
|
r := p.Get(prefixWidth + hash.ByteLen)
|
|
binary.LittleEndian.PutUint64(r, height)
|
|
copy(r[prefixWidth:], addr[:])
|
|
return CommitClosureKey(r)
|
|
}
|
|
|
|
func (k CommitClosureKey) Height() uint64 {
|
|
return binary.LittleEndian.Uint64(k)
|
|
}
|
|
|
|
func (k CommitClosureKey) Addr() hash.Hash {
|
|
return hash.New(k[prefixWidth:])
|
|
}
|
|
|
|
func (k CommitClosureKey) Less(ctx context.Context, other CommitClosureKey) bool {
|
|
cmp, err := commitClosureKeyOrdering{}.Compare(ctx, k, other)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return cmp < 0
|
|
}
|
|
|
|
var emptyCommitClosureValue CommitClosureValue = CommitClosureValue(make([]byte, 1))
|
|
|
|
func (wr CommitClosureEditor) Add(ctx context.Context, key CommitClosureKey) error {
|
|
return wr.closure.Put(ctx, key, emptyCommitClosureValue)
|
|
}
|
|
|
|
func (wr CommitClosureEditor) Delete(ctx context.Context, key CommitClosureKey) error {
|
|
return wr.closure.Delete(ctx, key)
|
|
}
|
|
|
|
func (wr CommitClosureEditor) Flush(ctx context.Context) (CommitClosure, error) {
|
|
sm := wr.closure.Static
|
|
serializer := message.NewCommitClosureSerializer(sm.NodeStore.Pool())
|
|
fn := tree.ApplyMutations[CommitClosureKey, commitClosureKeyOrdering, message.CommitClosureSerializer]
|
|
|
|
root, err := fn(ctx, sm.NodeStore, sm.Root, commitClosureKeyOrdering{}, serializer, wr.closure.Mutations())
|
|
if err != nil {
|
|
return CommitClosure{}, err
|
|
}
|
|
|
|
return CommitClosure{
|
|
closure: tree.StaticMap[CommitClosureKey, CommitClosureValue, commitClosureKeyOrdering]{
|
|
Root: root,
|
|
NodeStore: sm.NodeStore,
|
|
Order: sm.Order,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func DiffCommitClosures(ctx context.Context, from, to CommitClosure, cb tree.DiffFn) error {
|
|
considerAllRowsModified := false
|
|
return tree.DiffOrderedTrees(ctx, from.closure, to.closure, considerAllRowsModified, cb)
|
|
}
|