Files
2026-07-13 12:32:25 +08:00

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...)
}