mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-23 00:57:29 -06:00
Merge pull request #4571 from dolthub/daylon/bc-tests
Branch Control Pt. 8
This commit is contained in:
@@ -259,7 +259,7 @@ func (cmd SqlCmd) Exec(ctx context.Context, commandStr string, args []string, dE
|
||||
}
|
||||
}
|
||||
|
||||
// If no privilege filepath specified, default to doltcfg directory
|
||||
// If no branch control file path is specified, default to doltcfg directory
|
||||
branchControlFilePath, hasBCFilePath := apr.GetValue(BranchCtrlPathFlag)
|
||||
if !hasBCFilePath {
|
||||
branchControlFilePath, err = dEnv.FS.Abs(filepath.Join(cfgDirPath, DefaultBranchCtrlName))
|
||||
|
||||
1133
go/gen/fb/serial/branchcontrol.go
Normal file
1133
go/gen/fb/serial/branchcontrol.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@ const TableSchemaFileID = "DSCH"
|
||||
const ForeignKeyCollectionFileID = "DFKC"
|
||||
const MergeArtifactsFileID = "ARTM"
|
||||
const BlobFileID = "BLOB"
|
||||
const BranchControlFileID = "BRCL"
|
||||
|
||||
const MessageTypesKind int = 27
|
||||
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
package branch_control
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
|
||||
"github.com/dolthub/dolt/go/gen/fb/serial"
|
||||
)
|
||||
|
||||
// Permissions are a set of flags that denote a user's allowed functionality on a branch.
|
||||
@@ -31,10 +32,6 @@ const (
|
||||
Permissions_Write // Permissions_Write allows for all modifying operations on a branch, but does not allow modification of table entries
|
||||
)
|
||||
|
||||
const (
|
||||
currentAccessVersion = uint16(1)
|
||||
)
|
||||
|
||||
// Access contains all of the expressions that comprise the "dolt_branch_control" table, which handles write Access to
|
||||
// branches, along with write access to the branch control system tables.
|
||||
type Access struct {
|
||||
@@ -107,62 +104,118 @@ func (tbl *Access) GetIndex(branchExpr string, userExpr string, hostExpr string)
|
||||
return -1
|
||||
}
|
||||
|
||||
// Serialize writes the table to the given buffer. All encoded integers are big-endian.
|
||||
func (tbl *Access) Serialize(buffer *bytes.Buffer) {
|
||||
// Serialize returns the offset for the Access table written to the given builder.
|
||||
func (tbl *Access) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
tbl.RWMutex.RLock()
|
||||
defer tbl.RWMutex.RUnlock()
|
||||
|
||||
// Write the version bytes
|
||||
writeUint16(buffer, currentAccessVersion)
|
||||
// Write the number of entries
|
||||
numOfEntries := uint32(len(tbl.Values))
|
||||
writeUint32(buffer, numOfEntries)
|
||||
|
||||
// Write the rows
|
||||
for _, matchExpr := range tbl.Branches {
|
||||
matchExpr.Serialize(buffer)
|
||||
// Serialize the binlog
|
||||
binlog := tbl.binlog.Serialize(b)
|
||||
// Initialize field offset slices
|
||||
branchOffsets := make([]flatbuffers.UOffsetT, len(tbl.Branches))
|
||||
userOffsets := make([]flatbuffers.UOffsetT, len(tbl.Users))
|
||||
hostOffsets := make([]flatbuffers.UOffsetT, len(tbl.Hosts))
|
||||
valueOffsets := make([]flatbuffers.UOffsetT, len(tbl.Values))
|
||||
// Get field offsets
|
||||
for i, matchExpr := range tbl.Branches {
|
||||
branchOffsets[i] = matchExpr.Serialize(b)
|
||||
}
|
||||
for _, matchExpr := range tbl.Users {
|
||||
matchExpr.Serialize(buffer)
|
||||
for i, matchExpr := range tbl.Users {
|
||||
userOffsets[i] = matchExpr.Serialize(b)
|
||||
}
|
||||
for _, matchExpr := range tbl.Hosts {
|
||||
matchExpr.Serialize(buffer)
|
||||
for i, matchExpr := range tbl.Hosts {
|
||||
hostOffsets[i] = matchExpr.Serialize(b)
|
||||
}
|
||||
for _, val := range tbl.Values {
|
||||
val.Serialize(buffer)
|
||||
for i, val := range tbl.Values {
|
||||
valueOffsets[i] = val.Serialize(b)
|
||||
}
|
||||
// Write the binlog
|
||||
_ = tbl.binlog.Serialize(buffer)
|
||||
// Get the field vectors
|
||||
serial.BranchControlAccessStartBranchesVector(b, len(branchOffsets))
|
||||
for i := len(branchOffsets) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(branchOffsets[i])
|
||||
}
|
||||
branches := b.EndVector(len(branchOffsets))
|
||||
serial.BranchControlAccessStartUsersVector(b, len(userOffsets))
|
||||
for i := len(userOffsets) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(userOffsets[i])
|
||||
}
|
||||
users := b.EndVector(len(userOffsets))
|
||||
serial.BranchControlAccessStartHostsVector(b, len(hostOffsets))
|
||||
for i := len(hostOffsets) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(hostOffsets[i])
|
||||
}
|
||||
hosts := b.EndVector(len(hostOffsets))
|
||||
serial.BranchControlAccessStartValuesVector(b, len(valueOffsets))
|
||||
for i := len(valueOffsets) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(valueOffsets[i])
|
||||
}
|
||||
values := b.EndVector(len(valueOffsets))
|
||||
// Write the table
|
||||
serial.BranchControlAccessStart(b)
|
||||
serial.BranchControlAccessAddBinlog(b, binlog)
|
||||
serial.BranchControlAccessAddBranches(b, branches)
|
||||
serial.BranchControlAccessAddUsers(b, users)
|
||||
serial.BranchControlAccessAddHosts(b, hosts)
|
||||
serial.BranchControlAccessAddValues(b, values)
|
||||
return serial.BranchControlAccessEnd(b)
|
||||
}
|
||||
|
||||
// Deserialize populates the table with the given data. Returns an error if the data cannot be deserialized, or if the
|
||||
// table has already been written to. Deserialize must be called on an empty table.
|
||||
func (tbl *Access) Deserialize(data []byte, position *uint64) error {
|
||||
// Deserialize populates the table with the data from the flatbuffers representation.
|
||||
func (tbl *Access) Deserialize(fb *serial.BranchControlAccess) error {
|
||||
tbl.RWMutex.Lock()
|
||||
defer tbl.RWMutex.Unlock()
|
||||
|
||||
// Verify that the table is empty
|
||||
if len(tbl.Values) != 0 {
|
||||
return fmt.Errorf("cannot deserialize to a non-empty access table")
|
||||
}
|
||||
// Read the version
|
||||
version := binary.BigEndian.Uint16(data[*position:])
|
||||
*position += 2
|
||||
if version != currentAccessVersion {
|
||||
// If we ever increment the access version, this will instead handle the conversion from previous versions
|
||||
return fmt.Errorf(`cannot deserialize an access table with version "%d"`, version)
|
||||
// Verify that all fields have the same length
|
||||
if fb.BranchesLength() != fb.UsersLength() || fb.UsersLength() != fb.HostsLength() || fb.HostsLength() != fb.ValuesLength() {
|
||||
return fmt.Errorf("cannot deserialize an access table with differing field lengths")
|
||||
}
|
||||
// Read the number of entries
|
||||
numOfEntries := binary.BigEndian.Uint32(data[*position:])
|
||||
*position += 4
|
||||
// Read the rows
|
||||
tbl.Branches = deserializeMatchExpressions(numOfEntries, data, position)
|
||||
tbl.Users = deserializeMatchExpressions(numOfEntries, data, position)
|
||||
tbl.Hosts = deserializeMatchExpressions(numOfEntries, data, position)
|
||||
tbl.Values = make([]AccessValue, numOfEntries)
|
||||
for i := uint32(0); i < numOfEntries; i++ {
|
||||
tbl.Values[i] = deserializeAccessValue(data, position)
|
||||
// Read the binlog
|
||||
binlog, err := fb.TryBinlog(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tbl.binlog.Deserialize(data, position)
|
||||
if err = tbl.binlog.Deserialize(binlog); err != nil {
|
||||
return err
|
||||
}
|
||||
// Initialize every slice
|
||||
tbl.Branches = make([]MatchExpression, fb.BranchesLength())
|
||||
tbl.Users = make([]MatchExpression, fb.UsersLength())
|
||||
tbl.Hosts = make([]MatchExpression, fb.HostsLength())
|
||||
tbl.Values = make([]AccessValue, fb.ValuesLength())
|
||||
// Read the branches
|
||||
for i := 0; i < fb.BranchesLength(); i++ {
|
||||
serialMatchExpr := &serial.BranchControlMatchExpression{}
|
||||
fb.Branches(serialMatchExpr, i)
|
||||
tbl.Branches[i] = deserializeMatchExpression(serialMatchExpr)
|
||||
}
|
||||
// Read the users
|
||||
for i := 0; i < fb.UsersLength(); i++ {
|
||||
serialMatchExpr := &serial.BranchControlMatchExpression{}
|
||||
fb.Users(serialMatchExpr, i)
|
||||
tbl.Users[i] = deserializeMatchExpression(serialMatchExpr)
|
||||
}
|
||||
// Read the hosts
|
||||
for i := 0; i < fb.HostsLength(); i++ {
|
||||
serialMatchExpr := &serial.BranchControlMatchExpression{}
|
||||
fb.Hosts(serialMatchExpr, i)
|
||||
tbl.Hosts[i] = deserializeMatchExpression(serialMatchExpr)
|
||||
}
|
||||
// Read the values
|
||||
for i := 0; i < fb.ValuesLength(); i++ {
|
||||
serialAccessValue := &serial.BranchControlAccessValue{}
|
||||
fb.Values(serialAccessValue, i)
|
||||
tbl.Values[i] = AccessValue{
|
||||
Branch: string(serialAccessValue.Branch()),
|
||||
User: string(serialAccessValue.User()),
|
||||
Host: string(serialAccessValue.Host()),
|
||||
Permissions: Permissions(serialAccessValue.Permissions()),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// filterBranches returns all branches that match the given collection indexes.
|
||||
@@ -210,45 +263,16 @@ func (tbl *Access) gatherPermissions(collectionIndexes []uint32) Permissions {
|
||||
return perms
|
||||
}
|
||||
|
||||
// Serialize writes the value to the given buffer. All encoded integers are big-endian.
|
||||
func (val *AccessValue) Serialize(buffer *bytes.Buffer) {
|
||||
// Write the branch
|
||||
branchLen := uint16(len(val.Branch))
|
||||
writeUint16(buffer, branchLen)
|
||||
buffer.WriteString(val.Branch)
|
||||
// Write the user
|
||||
userLen := uint16(len(val.User))
|
||||
writeUint16(buffer, userLen)
|
||||
buffer.WriteString(val.User)
|
||||
// Write the host
|
||||
hostLen := uint16(len(val.Host))
|
||||
writeUint16(buffer, hostLen)
|
||||
buffer.WriteString(val.Host)
|
||||
// Write the permissions
|
||||
writeUint64(buffer, uint64(val.Permissions))
|
||||
}
|
||||
// Serialize returns the offset for the AccessValue written to the given builder.
|
||||
func (val *AccessValue) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
branch := b.CreateString(val.Branch)
|
||||
user := b.CreateString(val.User)
|
||||
host := b.CreateString(val.Host)
|
||||
|
||||
// deserializeAccessValue returns a AccessValue from the data at the given position. Assumes that the given data's
|
||||
// encoded integers are big-endian.
|
||||
func deserializeAccessValue(data []byte, position *uint64) AccessValue {
|
||||
val := AccessValue{}
|
||||
// Read the branch
|
||||
branchLen := uint64(binary.BigEndian.Uint16(data[*position:]))
|
||||
*position += 2
|
||||
val.Branch = string(data[*position : *position+branchLen])
|
||||
*position += branchLen
|
||||
// Read the user
|
||||
userLen := uint64(binary.BigEndian.Uint16(data[*position:]))
|
||||
*position += 2
|
||||
val.User = string(data[*position : *position+userLen])
|
||||
*position += userLen
|
||||
// Read the host
|
||||
hostLen := uint64(binary.BigEndian.Uint16(data[*position:]))
|
||||
*position += 2
|
||||
val.Host = string(data[*position : *position+hostLen])
|
||||
*position += hostLen
|
||||
// Read the permissions
|
||||
val.Permissions = Permissions(binary.BigEndian.Uint64(data[*position:]))
|
||||
*position += 8
|
||||
return val
|
||||
serial.BranchControlAccessValueStart(b)
|
||||
serial.BranchControlAccessValueAddBranch(b, branch)
|
||||
serial.BranchControlAccessValueAddUser(b, user)
|
||||
serial.BranchControlAccessValueAddHost(b, host)
|
||||
serial.BranchControlAccessValueAddPermissions(b, uint64(val.Permissions))
|
||||
return serial.BranchControlAccessValueEnd(b)
|
||||
}
|
||||
|
||||
@@ -15,14 +15,12 @@
|
||||
package branch_control
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
currentBinlogVersion = uint16(1)
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
|
||||
"github.com/dolthub/dolt/go/gen/fb/serial"
|
||||
)
|
||||
|
||||
//TODO: add stored procedure functions for modifying the binlog
|
||||
@@ -88,51 +86,51 @@ func NewNamespaceBinlog(vals []NamespaceValue) *Binlog {
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize returns the Binlog as a byte slice. Writes to the given buffer if one is provided, else allocates a
|
||||
// temporary buffer. All encoded integers are big-endian.
|
||||
func (binlog *Binlog) Serialize(buffer *bytes.Buffer) []byte {
|
||||
// Serialize returns the offset for the Binlog written to the given builder.
|
||||
func (binlog *Binlog) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
binlog.RWMutex.RLock()
|
||||
defer binlog.RWMutex.RUnlock()
|
||||
|
||||
if buffer == nil {
|
||||
buffer = &bytes.Buffer{}
|
||||
// Initialize row offset slice
|
||||
rowOffsets := make([]flatbuffers.UOffsetT, len(binlog.rows))
|
||||
// Get each row's offset
|
||||
for i, row := range binlog.rows {
|
||||
rowOffsets[i] = row.Serialize(b)
|
||||
}
|
||||
// Write the version bytes
|
||||
writeUint16(buffer, currentBinlogVersion)
|
||||
// Write the number of entries
|
||||
binlogSize := uint64(len(binlog.rows))
|
||||
writeUint64(buffer, binlogSize)
|
||||
|
||||
// Write the rows
|
||||
for _, binlogRow := range binlog.rows {
|
||||
binlogRow.Serialize(buffer)
|
||||
// Get the row vector
|
||||
serial.BranchControlBinlogStartRowsVector(b, len(binlog.rows))
|
||||
for i := len(rowOffsets) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(rowOffsets[i])
|
||||
}
|
||||
return buffer.Bytes()
|
||||
rows := b.EndVector(len(binlog.rows))
|
||||
// Write the binlog
|
||||
serial.BranchControlBinlogStart(b)
|
||||
serial.BranchControlBinlogAddRows(b, rows)
|
||||
return serial.BranchControlBinlogEnd(b)
|
||||
}
|
||||
|
||||
// Deserialize populates the binlog with the given data. Returns an error if the data cannot be deserialized, or if the
|
||||
// Binlog has already been written to. Deserialize must be called on an empty Binlog.
|
||||
func (binlog *Binlog) Deserialize(data []byte, position *uint64) error {
|
||||
// Deserialize populates the binlog with the data from the flatbuffers representation.
|
||||
func (binlog *Binlog) Deserialize(fb *serial.BranchControlBinlog) error {
|
||||
binlog.RWMutex.Lock()
|
||||
defer binlog.RWMutex.Unlock()
|
||||
|
||||
// Verify that the binlog is empty
|
||||
if len(binlog.rows) != 0 {
|
||||
return fmt.Errorf("cannot deserialize to a non-empty binlog")
|
||||
}
|
||||
// Read the version
|
||||
version := binary.BigEndian.Uint16(data[*position:])
|
||||
*position += 2
|
||||
if version != currentBinlogVersion {
|
||||
// If we ever increment the binlog version, this will instead handle the conversion from previous versions
|
||||
return fmt.Errorf(`cannot deserialize a binlog with version "%d"`, version)
|
||||
}
|
||||
// Read the number of entries
|
||||
binlogSize := binary.BigEndian.Uint64(data[*position:])
|
||||
*position += 8
|
||||
// Initialize the rows
|
||||
binlog.rows = make([]BinlogRow, fb.RowsLength())
|
||||
// Read the rows
|
||||
binlog.rows = make([]BinlogRow, binlogSize)
|
||||
for i := uint64(0); i < binlogSize; i++ {
|
||||
binlog.rows[i] = deserializeBinlogRow(data, position)
|
||||
for i := 0; i < fb.RowsLength(); i++ {
|
||||
serialBinlogRow := &serial.BranchControlBinlogRow{}
|
||||
fb.Rows(serialBinlogRow, i)
|
||||
binlog.rows[i] = BinlogRow{
|
||||
IsInsert: serialBinlogRow.IsInsert(),
|
||||
Branch: string(serialBinlogRow.Branch()),
|
||||
User: string(serialBinlogRow.User()),
|
||||
Host: string(serialBinlogRow.Host()),
|
||||
Permissions: serialBinlogRow.Permissions(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -197,60 +195,19 @@ func (binlog *Binlog) Rows() []BinlogRow {
|
||||
return binlog.rows
|
||||
}
|
||||
|
||||
// Serialize writes the row to the given buffer. All encoded integers are big-endian.
|
||||
func (row *BinlogRow) Serialize(buffer *bytes.Buffer) {
|
||||
// Write whether this was an insertion or deletion
|
||||
if row.IsInsert {
|
||||
buffer.WriteByte(1)
|
||||
} else {
|
||||
buffer.WriteByte(0)
|
||||
}
|
||||
// Write the branch
|
||||
branchLen := uint16(len(row.Branch))
|
||||
writeUint16(buffer, branchLen)
|
||||
buffer.WriteString(row.Branch)
|
||||
// Write the user
|
||||
userLen := uint16(len(row.User))
|
||||
writeUint16(buffer, userLen)
|
||||
buffer.WriteString(row.User)
|
||||
// Write the host
|
||||
hostLen := uint16(len(row.Host))
|
||||
writeUint16(buffer, hostLen)
|
||||
buffer.WriteString(row.Host)
|
||||
// Write the permissions
|
||||
writeUint64(buffer, row.Permissions)
|
||||
}
|
||||
// Serialize returns the offset for the BinlogRow written to the given builder.
|
||||
func (row *BinlogRow) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
branch := b.CreateString(row.Branch)
|
||||
user := b.CreateString(row.User)
|
||||
host := b.CreateString(row.Host)
|
||||
|
||||
// deserializeBinlogRow returns a BinlogRow from the data at the given position. Assumes that the given data's encoded
|
||||
// integers are big-endian.
|
||||
func deserializeBinlogRow(data []byte, position *uint64) BinlogRow {
|
||||
binlogRow := BinlogRow{}
|
||||
// Read whether this was an insert or write
|
||||
if data[*position] == 1 {
|
||||
binlogRow.IsInsert = true
|
||||
} else {
|
||||
binlogRow.IsInsert = false
|
||||
}
|
||||
*position += 1
|
||||
// Read the branch
|
||||
branchLen := uint64(binary.BigEndian.Uint16(data[*position:]))
|
||||
*position += 2
|
||||
binlogRow.Branch = string(data[*position : *position+branchLen])
|
||||
*position += branchLen
|
||||
// Read the user
|
||||
userLen := uint64(binary.BigEndian.Uint16(data[*position:]))
|
||||
*position += 2
|
||||
binlogRow.User = string(data[*position : *position+userLen])
|
||||
*position += userLen
|
||||
// Read the host
|
||||
hostLen := uint64(binary.BigEndian.Uint16(data[*position:]))
|
||||
*position += 2
|
||||
binlogRow.Host = string(data[*position : *position+hostLen])
|
||||
*position += hostLen
|
||||
// Read the permissions
|
||||
binlogRow.Permissions = binary.BigEndian.Uint64(data[*position:])
|
||||
*position += 8
|
||||
return binlogRow
|
||||
serial.BranchControlBinlogRowStart(b)
|
||||
serial.BranchControlBinlogRowAddIsInsert(b, row.IsInsert)
|
||||
serial.BranchControlBinlogRowAddBranch(b, branch)
|
||||
serial.BranchControlBinlogRowAddUser(b, user)
|
||||
serial.BranchControlBinlogRowAddHost(b, host)
|
||||
serial.BranchControlBinlogRowAddPermissions(b, row.Permissions)
|
||||
return serial.BranchControlBinlogRowEnd(b)
|
||||
}
|
||||
|
||||
// Insert adds an insert entry to the BinlogOverlay.
|
||||
@@ -274,29 +231,3 @@ func (overlay *BinlogOverlay) Delete(branch string, user string, host string, pe
|
||||
Permissions: permissions,
|
||||
})
|
||||
}
|
||||
|
||||
// writeUint64 writes an uint64 into the buffer.
|
||||
func writeUint64(buffer *bytes.Buffer, val uint64) {
|
||||
buffer.WriteByte(byte(val >> 56))
|
||||
buffer.WriteByte(byte(val >> 48))
|
||||
buffer.WriteByte(byte(val >> 40))
|
||||
buffer.WriteByte(byte(val >> 32))
|
||||
buffer.WriteByte(byte(val >> 24))
|
||||
buffer.WriteByte(byte(val >> 16))
|
||||
buffer.WriteByte(byte(val >> 8))
|
||||
buffer.WriteByte(byte(val))
|
||||
}
|
||||
|
||||
// writeUint32 writes an uint32 into the buffer.
|
||||
func writeUint32(buffer *bytes.Buffer, val uint32) {
|
||||
buffer.WriteByte(byte(val >> 24))
|
||||
buffer.WriteByte(byte(val >> 16))
|
||||
buffer.WriteByte(byte(val >> 8))
|
||||
buffer.WriteByte(byte(val))
|
||||
}
|
||||
|
||||
// writeUint16 writes an uint16 into the buffer.
|
||||
func writeUint16(buffer *bytes.Buffer, val uint16) {
|
||||
buffer.WriteByte(byte(val >> 8))
|
||||
buffer.WriteByte(byte(val))
|
||||
}
|
||||
|
||||
@@ -15,14 +15,16 @@
|
||||
package branch_control
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
goerrors "errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
"gopkg.in/src-d/go-errors.v1"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/dolt/go/gen/fb/serial"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -101,12 +103,27 @@ func LoadData(ctx context.Context, branchControlFilePath string, doltConfigDirPa
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
position := uint64(0)
|
||||
// The Deserialize functions acquire write locks, so we don't acquire them here
|
||||
if err = StaticController.Access.Deserialize(data, &position); err != nil {
|
||||
// Load the tables
|
||||
if serial.GetFileID(data) != serial.BranchControlFileID {
|
||||
return fmt.Errorf("unable to deserialize branch controller, unknown file ID `%s`", serial.GetFileID(data))
|
||||
}
|
||||
bc, err := serial.TryGetRootAsBranchControl(data, serial.MessagePrefixSz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = StaticController.Namespace.Deserialize(data, &position); err != nil {
|
||||
access, err := bc.TryAccessTbl(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
namespace, err := bc.TryNamespaceTbl(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The Deserialize functions acquire write locks, so we don't acquire them here
|
||||
if err = StaticController.Access.Deserialize(access); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = StaticController.Namespace.Deserialize(namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -133,11 +150,16 @@ func SaveData(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
buffer := bytes.Buffer{}
|
||||
b := flatbuffers.NewBuilder(1024)
|
||||
// The Serialize functions acquire read locks, so we don't acquire them here
|
||||
StaticController.Access.Serialize(&buffer)
|
||||
StaticController.Namespace.Serialize(&buffer)
|
||||
return os.WriteFile(StaticController.branchControlFilePath, buffer.Bytes(), 0777)
|
||||
accessOffset := StaticController.Access.Serialize(b)
|
||||
namespaceOffset := StaticController.Namespace.Serialize(b)
|
||||
serial.BranchControlStart(b)
|
||||
serial.BranchControlAddAccessTbl(b, accessOffset)
|
||||
serial.BranchControlAddNamespaceTbl(b, namespaceOffset)
|
||||
root := serial.BranchControlEnd(b)
|
||||
data := serial.FinishMessage(b, root, []byte(serial.BranchControlFileID))
|
||||
return os.WriteFile(StaticController.branchControlFilePath, data, 0777)
|
||||
}
|
||||
|
||||
// Reset is a temporary function just for testing. Once the controller is in the context, this will be unnecessary.
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
package branch_control
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
|
||||
"github.com/dolthub/dolt/go/gen/fb/serial"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -254,43 +255,28 @@ func (matchExpr MatchExpression) IsAtEnd() bool {
|
||||
return len(matchExpr.SortOrders) == 0 || (len(matchExpr.SortOrders) == 1 && matchExpr.SortOrders[0] == anyMatch)
|
||||
}
|
||||
|
||||
// Serialize writes the MatchExpression to the given buffer. All encoded integers are big-endian.
|
||||
func (matchExpr MatchExpression) Serialize(buffer *bytes.Buffer) {
|
||||
// Write the length
|
||||
sortOrderLen := uint16(len(matchExpr.SortOrders))
|
||||
writeUint16(buffer, sortOrderLen)
|
||||
// Write the sort orders
|
||||
for _, sortOrder := range matchExpr.SortOrders {
|
||||
// We can convert to an uint32 on write and convert back to an int32 on read
|
||||
writeUint32(buffer, uint32(sortOrder))
|
||||
// Serialize returns the offset for the MatchExpression written to the given builder.
|
||||
func (matchExpr MatchExpression) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
_ = serial.BranchControlMatchExpressionStartSortOrdersVector(b, len(matchExpr.SortOrders))
|
||||
for i := len(matchExpr.SortOrders) - 1; i >= 0; i-- {
|
||||
b.PrependInt32(matchExpr.SortOrders[i])
|
||||
}
|
||||
sortOrdersOffset := b.EndVector(len(matchExpr.SortOrders))
|
||||
|
||||
serial.BranchControlMatchExpressionStart(b)
|
||||
serial.BranchControlMatchExpressionAddIndex(b, matchExpr.CollectionIndex)
|
||||
serial.BranchControlMatchExpressionAddSortOrders(b, sortOrdersOffset)
|
||||
return serial.BranchControlMatchExpressionEnd(b)
|
||||
}
|
||||
|
||||
// deserializeMatchExpression returns a MatchExpression from the data at the given position. Assumes that the given
|
||||
// data's encoded integers are big-endian.
|
||||
func deserializeMatchExpression(collectionIndex uint32, data []byte, position *uint64) MatchExpression {
|
||||
matchExpr := MatchExpression{CollectionIndex: collectionIndex}
|
||||
// Read the length
|
||||
sortOrderLen := binary.BigEndian.Uint16(data[*position:])
|
||||
*position += 2
|
||||
// Create the sort order slice
|
||||
matchExpr.SortOrders = make([]int32, sortOrderLen)
|
||||
// Read the sort orders
|
||||
for i := uint16(0); i < sortOrderLen; i++ {
|
||||
matchExpr.SortOrders[i] = int32(binary.BigEndian.Uint32(data[*position:]))
|
||||
*position += 4
|
||||
// deserializeMatchExpression populates the MatchExpression with the data from the flatbuffers representation.
|
||||
func deserializeMatchExpression(fb *serial.BranchControlMatchExpression) MatchExpression {
|
||||
matchExpr := MatchExpression{
|
||||
CollectionIndex: fb.Index(),
|
||||
SortOrders: make([]int32, fb.SortOrdersLength()),
|
||||
}
|
||||
for i := 0; i < fb.SortOrdersLength(); i++ {
|
||||
matchExpr.SortOrders[i] = fb.SortOrders(i)
|
||||
}
|
||||
return matchExpr
|
||||
}
|
||||
|
||||
// deserializeMatchExpressions returns a []MatchExpression from the data at the given position. Assumes that the given
|
||||
// data's encoded integers are big-endian.
|
||||
func deserializeMatchExpressions(matchExprsLen uint32, data []byte, position *uint64) []MatchExpression {
|
||||
// Create the slice
|
||||
matchExprs := make([]MatchExpression, matchExprsLen)
|
||||
// Read each match expression
|
||||
for i := uint32(0); i < matchExprsLen; i++ {
|
||||
matchExprs[i] = deserializeMatchExpression(i, data, position)
|
||||
}
|
||||
return matchExprs
|
||||
}
|
||||
|
||||
@@ -15,16 +15,13 @@
|
||||
package branch_control
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
)
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
|
||||
const (
|
||||
currentNamespaceVersion = uint16(1)
|
||||
"github.com/dolthub/dolt/go/gen/fb/serial"
|
||||
)
|
||||
|
||||
// Namespace contains all of the expressions that comprise the "dolt_branch_namespace_control" table, which controls
|
||||
@@ -125,62 +122,117 @@ func (tbl *Namespace) Access() *Access {
|
||||
return tbl.access
|
||||
}
|
||||
|
||||
// Serialize writes the table to the given buffer. All encoded integers are big-endian.
|
||||
func (tbl *Namespace) Serialize(buffer *bytes.Buffer) {
|
||||
// Serialize returns the offset for the Namespace table written to the given builder.
|
||||
func (tbl *Namespace) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
tbl.RWMutex.RLock()
|
||||
defer tbl.RWMutex.RUnlock()
|
||||
|
||||
// Write the version bytes
|
||||
writeUint16(buffer, currentNamespaceVersion)
|
||||
// Write the number of entries
|
||||
numOfEntries := uint32(len(tbl.Values))
|
||||
writeUint32(buffer, numOfEntries)
|
||||
|
||||
// Write the rows
|
||||
for _, matchExpr := range tbl.Branches {
|
||||
matchExpr.Serialize(buffer)
|
||||
// Serialize the binlog
|
||||
binlog := tbl.binlog.Serialize(b)
|
||||
// Initialize field offset slices
|
||||
branchOffsets := make([]flatbuffers.UOffsetT, len(tbl.Branches))
|
||||
userOffsets := make([]flatbuffers.UOffsetT, len(tbl.Users))
|
||||
hostOffsets := make([]flatbuffers.UOffsetT, len(tbl.Hosts))
|
||||
valueOffsets := make([]flatbuffers.UOffsetT, len(tbl.Values))
|
||||
// Get field offsets
|
||||
for i, matchExpr := range tbl.Branches {
|
||||
branchOffsets[i] = matchExpr.Serialize(b)
|
||||
}
|
||||
for _, matchExpr := range tbl.Users {
|
||||
matchExpr.Serialize(buffer)
|
||||
for i, matchExpr := range tbl.Users {
|
||||
userOffsets[i] = matchExpr.Serialize(b)
|
||||
}
|
||||
for _, matchExpr := range tbl.Hosts {
|
||||
matchExpr.Serialize(buffer)
|
||||
for i, matchExpr := range tbl.Hosts {
|
||||
hostOffsets[i] = matchExpr.Serialize(b)
|
||||
}
|
||||
for _, val := range tbl.Values {
|
||||
val.Serialize(buffer)
|
||||
for i, val := range tbl.Values {
|
||||
valueOffsets[i] = val.Serialize(b)
|
||||
}
|
||||
// Write the binlog
|
||||
_ = tbl.binlog.Serialize(buffer)
|
||||
// Get the field vectors
|
||||
serial.BranchControlNamespaceStartBranchesVector(b, len(branchOffsets))
|
||||
for i := len(branchOffsets) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(branchOffsets[i])
|
||||
}
|
||||
branches := b.EndVector(len(branchOffsets))
|
||||
serial.BranchControlNamespaceStartUsersVector(b, len(userOffsets))
|
||||
for i := len(userOffsets) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(userOffsets[i])
|
||||
}
|
||||
users := b.EndVector(len(userOffsets))
|
||||
serial.BranchControlNamespaceStartHostsVector(b, len(hostOffsets))
|
||||
for i := len(hostOffsets) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(hostOffsets[i])
|
||||
}
|
||||
hosts := b.EndVector(len(hostOffsets))
|
||||
serial.BranchControlNamespaceStartValuesVector(b, len(valueOffsets))
|
||||
for i := len(valueOffsets) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(valueOffsets[i])
|
||||
}
|
||||
values := b.EndVector(len(valueOffsets))
|
||||
// Write the table
|
||||
serial.BranchControlNamespaceStart(b)
|
||||
serial.BranchControlNamespaceAddBinlog(b, binlog)
|
||||
serial.BranchControlNamespaceAddBranches(b, branches)
|
||||
serial.BranchControlNamespaceAddUsers(b, users)
|
||||
serial.BranchControlNamespaceAddHosts(b, hosts)
|
||||
serial.BranchControlNamespaceAddValues(b, values)
|
||||
return serial.BranchControlNamespaceEnd(b)
|
||||
}
|
||||
|
||||
// Deserialize populates the table with the given data. Returns an error if the data cannot be deserialized, or if the
|
||||
// table has already been written to. Deserialize must be called on an empty table.
|
||||
func (tbl *Namespace) Deserialize(data []byte, position *uint64) error {
|
||||
// Deserialize populates the table with the data from the flatbuffers representation.
|
||||
func (tbl *Namespace) Deserialize(fb *serial.BranchControlNamespace) error {
|
||||
tbl.RWMutex.Lock()
|
||||
defer tbl.RWMutex.Unlock()
|
||||
|
||||
// Verify that the table is empty
|
||||
if len(tbl.Values) != 0 {
|
||||
return fmt.Errorf("cannot deserialize to a non-empty namespace table")
|
||||
}
|
||||
// Read the version
|
||||
version := binary.BigEndian.Uint16(data[*position:])
|
||||
*position += 2
|
||||
if version != currentNamespaceVersion {
|
||||
// If we ever increment the namespace version, this will instead handle the conversion from previous versions
|
||||
return fmt.Errorf(`cannot deserialize an namespace table with version "%d"`, version)
|
||||
// Verify that all fields have the same length
|
||||
if fb.BranchesLength() != fb.UsersLength() || fb.UsersLength() != fb.HostsLength() || fb.HostsLength() != fb.ValuesLength() {
|
||||
return fmt.Errorf("cannot deserialize a namespace table with differing field lengths")
|
||||
}
|
||||
// Read the number of entries
|
||||
numOfEntries := binary.BigEndian.Uint32(data[*position:])
|
||||
*position += 4
|
||||
// Read the rows
|
||||
tbl.Branches = deserializeMatchExpressions(numOfEntries, data, position)
|
||||
tbl.Users = deserializeMatchExpressions(numOfEntries, data, position)
|
||||
tbl.Hosts = deserializeMatchExpressions(numOfEntries, data, position)
|
||||
tbl.Values = make([]NamespaceValue, numOfEntries)
|
||||
for i := uint32(0); i < numOfEntries; i++ {
|
||||
tbl.Values[i] = deserializeNamespaceValue(data, position)
|
||||
// Read the binlog
|
||||
binlog, err := fb.TryBinlog(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tbl.binlog.Deserialize(data, position)
|
||||
if err = tbl.binlog.Deserialize(binlog); err != nil {
|
||||
return err
|
||||
}
|
||||
// Initialize every slice
|
||||
tbl.Branches = make([]MatchExpression, fb.BranchesLength())
|
||||
tbl.Users = make([]MatchExpression, fb.UsersLength())
|
||||
tbl.Hosts = make([]MatchExpression, fb.HostsLength())
|
||||
tbl.Values = make([]NamespaceValue, fb.ValuesLength())
|
||||
// Read the branches
|
||||
for i := 0; i < fb.BranchesLength(); i++ {
|
||||
serialMatchExpr := &serial.BranchControlMatchExpression{}
|
||||
fb.Branches(serialMatchExpr, i)
|
||||
tbl.Branches[i] = deserializeMatchExpression(serialMatchExpr)
|
||||
}
|
||||
// Read the users
|
||||
for i := 0; i < fb.UsersLength(); i++ {
|
||||
serialMatchExpr := &serial.BranchControlMatchExpression{}
|
||||
fb.Users(serialMatchExpr, i)
|
||||
tbl.Users[i] = deserializeMatchExpression(serialMatchExpr)
|
||||
}
|
||||
// Read the hosts
|
||||
for i := 0; i < fb.HostsLength(); i++ {
|
||||
serialMatchExpr := &serial.BranchControlMatchExpression{}
|
||||
fb.Hosts(serialMatchExpr, i)
|
||||
tbl.Hosts[i] = deserializeMatchExpression(serialMatchExpr)
|
||||
}
|
||||
// Read the values
|
||||
for i := 0; i < fb.ValuesLength(); i++ {
|
||||
serialNamespaceValue := &serial.BranchControlNamespaceValue{}
|
||||
fb.Values(serialNamespaceValue, i)
|
||||
tbl.Values[i] = NamespaceValue{
|
||||
Branch: string(serialNamespaceValue.Branch()),
|
||||
User: string(serialNamespaceValue.User()),
|
||||
Host: string(serialNamespaceValue.Host()),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// filterBranches returns all branches that match the given collection indexes.
|
||||
@@ -219,40 +271,15 @@ func (tbl *Namespace) filterHosts(filters []uint32) []MatchExpression {
|
||||
return matchExprs
|
||||
}
|
||||
|
||||
// Serialize writes the value to the given buffer. All encoded integers are big-endian.
|
||||
func (val *NamespaceValue) Serialize(buffer *bytes.Buffer) {
|
||||
// Write the branch
|
||||
branchLen := uint16(len(val.Branch))
|
||||
writeUint16(buffer, branchLen)
|
||||
buffer.WriteString(val.Branch)
|
||||
// Write the user
|
||||
userLen := uint16(len(val.User))
|
||||
writeUint16(buffer, userLen)
|
||||
buffer.WriteString(val.User)
|
||||
// Write the host
|
||||
hostLen := uint16(len(val.Host))
|
||||
writeUint16(buffer, hostLen)
|
||||
buffer.WriteString(val.Host)
|
||||
}
|
||||
// Serialize returns the offset for the NamespaceValue written to the given builder.
|
||||
func (val *NamespaceValue) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
branch := b.CreateString(val.Branch)
|
||||
user := b.CreateString(val.User)
|
||||
host := b.CreateString(val.Host)
|
||||
|
||||
// deserializeNamespaceValue returns a NamespaceValue from the data at the given position. Also returns the new
|
||||
// position. Assumes that the given data's encoded integers are big-endian.
|
||||
func deserializeNamespaceValue(data []byte, position *uint64) NamespaceValue {
|
||||
val := NamespaceValue{}
|
||||
// Read the branch
|
||||
branchLen := uint64(binary.BigEndian.Uint16(data[*position:]))
|
||||
*position += 2
|
||||
val.Branch = string(data[*position : *position+branchLen])
|
||||
*position += branchLen
|
||||
// Read the user
|
||||
userLen := uint64(binary.BigEndian.Uint16(data[*position:]))
|
||||
*position += 2
|
||||
val.User = string(data[*position : *position+userLen])
|
||||
*position += userLen
|
||||
// Read the host
|
||||
hostLen := uint64(binary.BigEndian.Uint16(data[*position:]))
|
||||
*position += 2
|
||||
val.Host = string(data[*position : *position+hostLen])
|
||||
*position += hostLen
|
||||
return val
|
||||
serial.BranchControlNamespaceValueStart(b)
|
||||
serial.BranchControlNamespaceValueAddBranch(b, branch)
|
||||
serial.BranchControlNamespaceValueAddUser(b, user)
|
||||
serial.BranchControlNamespaceValueAddHost(b, host)
|
||||
return serial.BranchControlNamespaceValueEnd(b)
|
||||
}
|
||||
|
||||
71
go/serial/branchcontrol.fbs
Normal file
71
go/serial/branchcontrol.fbs
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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.
|
||||
|
||||
namespace serial;
|
||||
|
||||
table BranchControl {
|
||||
access_tbl: BranchControlAccess;
|
||||
namespace_tbl: BranchControlNamespace;
|
||||
}
|
||||
|
||||
table BranchControlAccess {
|
||||
binlog: BranchControlBinlog;
|
||||
branches: [BranchControlMatchExpression];
|
||||
users: [BranchControlMatchExpression];
|
||||
hosts: [BranchControlMatchExpression];
|
||||
values: [BranchControlAccessValue];
|
||||
}
|
||||
|
||||
table BranchControlAccessValue {
|
||||
branch: string;
|
||||
user: string;
|
||||
host: string;
|
||||
permissions: uint64;
|
||||
}
|
||||
|
||||
table BranchControlNamespace {
|
||||
binlog: BranchControlBinlog;
|
||||
branches: [BranchControlMatchExpression];
|
||||
users: [BranchControlMatchExpression];
|
||||
hosts: [BranchControlMatchExpression];
|
||||
values: [BranchControlNamespaceValue];
|
||||
}
|
||||
|
||||
table BranchControlNamespaceValue {
|
||||
branch: string;
|
||||
user: string;
|
||||
host: string;
|
||||
}
|
||||
|
||||
table BranchControlBinlog {
|
||||
rows: [BranchControlBinlogRow];
|
||||
}
|
||||
|
||||
table BranchControlBinlogRow {
|
||||
is_insert: bool;
|
||||
branch: string;
|
||||
user: string;
|
||||
host: string;
|
||||
permissions: uint64;
|
||||
}
|
||||
|
||||
table BranchControlMatchExpression {
|
||||
index: uint32;
|
||||
sort_orders: [int32];
|
||||
}
|
||||
|
||||
// KEEP THIS IN SYNC WITH fileidentifiers.go
|
||||
file_identifier "BRCL";
|
||||
|
||||
root_type BranchControl;
|
||||
@@ -36,6 +36,7 @@ const TableSchemaFileID = "DSCH"
|
||||
const ForeignKeyCollectionFileID = "DFKC"
|
||||
const MergeArtifactsFileID = "ARTM"
|
||||
const BlobFileID = "BLOB"
|
||||
const BranchControlFileID = "BRCL"
|
||||
|
||||
const MessageTypesKind int = 27
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ fi
|
||||
"$FLATC" -o $GEN_DIR --gen-onefile --filename-suffix "" --gen-mutable --go-namespace "serial" --go \
|
||||
addressmap.fbs \
|
||||
blob.fbs \
|
||||
branchcontrol.fbs \
|
||||
collation.fbs \
|
||||
commit.fbs \
|
||||
commitclosure.fbs \
|
||||
|
||||
Reference in New Issue
Block a user