135 lines
4.8 KiB
Go
135 lines
4.8 KiB
Go
// Copyright 2024 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 core
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
doltserial "github.com/dolthub/dolt/go/gen/fb/serial"
|
|
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
|
"github.com/dolthub/dolt/go/store/hash"
|
|
"github.com/dolthub/dolt/go/store/prolly"
|
|
"github.com/dolthub/dolt/go/store/prolly/tree"
|
|
"github.com/dolthub/dolt/go/store/types"
|
|
flatbuffers "github.com/dolthub/flatbuffers/v23/go"
|
|
|
|
"github.com/dolthub/doltgresql/core/storage"
|
|
"github.com/dolthub/doltgresql/flatbuffers/gen/serial"
|
|
)
|
|
|
|
// emptyRootValue is Doltgres' implementation of doltdb.EmptyRootValue.
|
|
func emptyRootValue(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore) (doltdb.RootValue, error) {
|
|
builder := flatbuffers.NewBuilder(80)
|
|
|
|
emptyam, err := prolly.NewEmptyAddressMap(ns)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ambytes := []byte(tree.ValueFromNode(emptyam.Node()).(types.SerialMessage))
|
|
tablesoff := builder.CreateByteVector(ambytes)
|
|
|
|
var empty hash.Hash
|
|
fkoff := builder.CreateByteVector(empty[:])
|
|
serial.RootValueStart(builder)
|
|
serial.RootValueAddFeatureVersion(builder, int64(DoltgresFeatureVersion))
|
|
serial.RootValueAddCollation(builder, serial.Collationutf8mb4_0900_bin)
|
|
serial.RootValueAddTables(builder, tablesoff)
|
|
serial.RootValueAddForeignKeyAddr(builder, fkoff)
|
|
bs := doltserial.FinishMessage(builder, serial.RootValueEnd(builder), []byte(doltserial.DoltgresRootValueFileID))
|
|
return newRootValue(ctx, vrw, ns, types.SerialMessage(bs))
|
|
}
|
|
|
|
// newRootValue is Doltgres' implementation of doltdb.NewRootValue.
|
|
func newRootValue(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, v types.Value) (doltdb.RootValue, error) {
|
|
var st storage.RootStorage
|
|
|
|
srv, err := serial.TryGetRootAsRootValue([]byte(v.(types.SerialMessage)), doltserial.MessagePrefixSz)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
st = storage.RootStorage{SRV: srv}
|
|
ver := st.GetFeatureVersion()
|
|
if DoltgresFeatureVersion < ver {
|
|
return nil, doltdb.ErrClientOutOfDate{
|
|
ClientVer: DoltgresFeatureVersion,
|
|
RepoVer: ver,
|
|
}
|
|
}
|
|
|
|
return &RootValue{
|
|
vrw: vrw,
|
|
ns: ns,
|
|
st: st,
|
|
}, nil
|
|
}
|
|
|
|
// rootValueHumanReadableStringAtIndentationLevel is Doltgres' implementation of
|
|
// types.DoltgresRootValueHumanReadableStringAtIndentationLevel.
|
|
func rootValueHumanReadableStringAtIndentationLevel(sm types.SerialMessage, level int) string {
|
|
msg, _ := serial.TryGetRootAsRootValue(sm, doltserial.MessagePrefixSz)
|
|
ret := &strings.Builder{}
|
|
printWithIndendationLevel(level, ret, "{\n")
|
|
printWithIndendationLevel(level, ret, "\tFeatureVersion: %d\n", msg.FeatureVersion())
|
|
printWithIndendationLevel(level, ret, "\tForeignKeys: #%s\n", hash.New(msg.ForeignKeyAddrBytes()).String())
|
|
printWithIndendationLevel(level, ret, "\tTables: %s\n",
|
|
types.SerialMessage(msg.TablesBytes()).HumanReadableStringAtIndentationLevel(level+1))
|
|
printWithIndendationLevel(level, ret, "}")
|
|
return ret.String()
|
|
}
|
|
|
|
// rootValueWalkAddrs is Doltgres' implementation of types.DoltgresRootValueWalkAddrs.
|
|
func rootValueWalkAddrs(sm types.SerialMessage, cb func(addr hash.Hash) error) error {
|
|
var msg serial.RootValue
|
|
err := serial.InitRootValueRoot(&msg, []byte(sm), doltserial.MessagePrefixSz)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = types.SerialMessage(msg.TablesBytes()).WalkAddrs(types.Format_DOLT, cb)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
addr := hash.New(msg.ForeignKeyAddrBytes())
|
|
if !addr.IsEmpty() {
|
|
if err = cb(addr); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// Walk all Doltgres root object fields (sequences, types, functions, procedures, triggers, extensions, casts).
|
|
// Each field stores a 20-byte hash pointing to a prolly address map in the chunk store. Without walking these,
|
|
// backup/restore (SyncRoots/PullChunks) will not copy the root object chunks and they will be absent after restore.
|
|
for _, ros := range storage.RootObjectSerializations {
|
|
addrBytes := ros.Bytes(&msg)
|
|
if len(addrBytes) == 0 {
|
|
continue
|
|
}
|
|
objAddr := hash.New(addrBytes)
|
|
if !objAddr.IsEmpty() {
|
|
if err = cb(objAddr); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// printWithIndendationLevel is a helper for rootValueHumanReadableStringAtIndentationLevel to print at the given
|
|
// indentation level.
|
|
func printWithIndendationLevel(level int, builder *strings.Builder, format string, a ...any) {
|
|
fmt.Fprint(builder, strings.Repeat("\t", level))
|
|
fmt.Fprintf(builder, format, a...)
|
|
}
|