github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/branch_control/binlog.go (about)

     1  // Copyright 2022 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 branch_control
    16  
    17  import (
    18  	"fmt"
    19  	"sync"
    20  
    21  	flatbuffers "github.com/dolthub/flatbuffers/v23/go"
    22  
    23  	"github.com/dolthub/dolt/go/gen/fb/serial"
    24  )
    25  
    26  //TODO: add stored procedure functions for modifying the binlog
    27  
    28  // Binlog is a running log file that tracks changes to tables within branch control. This is used for history purposes,
    29  // as well as transactional purposes through the use of the BinlogOverlay.
    30  type Binlog struct {
    31  	rows    []BinlogRow
    32  	RWMutex *sync.RWMutex
    33  }
    34  
    35  // BinlogRow is a row within the Binlog.
    36  type BinlogRow struct {
    37  	IsInsert    bool
    38  	Database    string
    39  	Branch      string
    40  	User        string
    41  	Host        string
    42  	Permissions uint64
    43  }
    44  
    45  // BinlogOverlay enables transactional use cases over Binlog. Unlike a Binlog, a BinlogOverlay requires external
    46  // synchronization.
    47  type BinlogOverlay struct {
    48  	parentLength int
    49  	rows         []BinlogRow
    50  }
    51  
    52  // NewAccessBinlog returns a new Binlog that represents the construction of the given Access values. May be used to
    53  // truncate the Binlog's history.
    54  func NewAccessBinlog(vals []AccessRow) *Binlog {
    55  	rows := make([]BinlogRow, len(vals))
    56  	for i, val := range vals {
    57  		rows[i] = BinlogRow{
    58  			IsInsert:    true,
    59  			Database:    val.Database,
    60  			Branch:      val.Branch,
    61  			User:        val.User,
    62  			Host:        val.Host,
    63  			Permissions: uint64(val.Permissions),
    64  		}
    65  	}
    66  	return &Binlog{
    67  		rows:    rows,
    68  		RWMutex: &sync.RWMutex{},
    69  	}
    70  }
    71  
    72  // NewNamespaceBinlog returns a new Binlog that represents the construction of the given Namespace values. May be used
    73  // to truncate the Binlog's history.
    74  func NewNamespaceBinlog(vals []NamespaceValue) *Binlog {
    75  	rows := make([]BinlogRow, len(vals))
    76  	for i, val := range vals {
    77  		rows[i] = BinlogRow{
    78  			IsInsert:    true,
    79  			Database:    val.Database,
    80  			Branch:      val.Branch,
    81  			User:        val.User,
    82  			Host:        val.Host,
    83  			Permissions: 0,
    84  		}
    85  	}
    86  	return &Binlog{
    87  		rows:    rows,
    88  		RWMutex: &sync.RWMutex{},
    89  	}
    90  }
    91  
    92  // Serialize returns the offset for the Binlog written to the given builder.
    93  func (binlog *Binlog) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
    94  	binlog.RWMutex.RLock()
    95  	defer binlog.RWMutex.RUnlock()
    96  
    97  	// Initialize row offset slice
    98  	rowOffsets := make([]flatbuffers.UOffsetT, len(binlog.rows))
    99  	// Get each row's offset
   100  	for i, row := range binlog.rows {
   101  		rowOffsets[i] = row.Serialize(b)
   102  	}
   103  	// Get the row vector
   104  	serial.BranchControlBinlogStartRowsVector(b, len(binlog.rows))
   105  	for i := len(rowOffsets) - 1; i >= 0; i-- {
   106  		b.PrependUOffsetT(rowOffsets[i])
   107  	}
   108  	rows := b.EndVector(len(binlog.rows))
   109  	// Write the binlog
   110  	serial.BranchControlBinlogStart(b)
   111  	serial.BranchControlBinlogAddRows(b, rows)
   112  	return serial.BranchControlBinlogEnd(b)
   113  }
   114  
   115  // Deserialize populates the binlog with the data from the flatbuffers representation.
   116  func (binlog *Binlog) Deserialize(fb *serial.BranchControlBinlog) error {
   117  	binlog.RWMutex.Lock()
   118  	defer binlog.RWMutex.Unlock()
   119  
   120  	// Verify that the binlog is empty
   121  	if len(binlog.rows) != 0 {
   122  		return fmt.Errorf("cannot deserialize to a non-empty binlog")
   123  	}
   124  	// Initialize the rows
   125  	binlog.rows = make([]BinlogRow, fb.RowsLength())
   126  	// Read the rows
   127  	for i := 0; i < fb.RowsLength(); i++ {
   128  		serialBinlogRow := &serial.BranchControlBinlogRow{}
   129  		_, err := fb.TryRows(serialBinlogRow, i)
   130  		if err != nil {
   131  			return fmt.Errorf("cannot deserialize binlog, it was created with a later version of Dolt")
   132  		}
   133  		binlog.rows[i] = BinlogRow{
   134  			IsInsert:    serialBinlogRow.IsInsert(),
   135  			Database:    string(serialBinlogRow.Database()),
   136  			Branch:      string(serialBinlogRow.Branch()),
   137  			User:        string(serialBinlogRow.User()),
   138  			Host:        string(serialBinlogRow.Host()),
   139  			Permissions: serialBinlogRow.Permissions(),
   140  		}
   141  	}
   142  	return nil
   143  }
   144  
   145  // Insert adds an insert entry to the Binlog.
   146  func (binlog *Binlog) Insert(database string, branch string, user string, host string, permissions uint64) {
   147  	binlog.RWMutex.Lock()
   148  	defer binlog.RWMutex.Unlock()
   149  
   150  	binlog.rows = append(binlog.rows, BinlogRow{
   151  		IsInsert:    true,
   152  		Database:    database,
   153  		Branch:      branch,
   154  		User:        user,
   155  		Host:        host,
   156  		Permissions: permissions,
   157  	})
   158  }
   159  
   160  // Delete adds a delete entry to the Binlog.
   161  func (binlog *Binlog) Delete(database string, branch string, user string, host string, permissions uint64) {
   162  	binlog.RWMutex.Lock()
   163  	defer binlog.RWMutex.Unlock()
   164  
   165  	binlog.rows = append(binlog.rows, BinlogRow{
   166  		IsInsert:    false,
   167  		Database:    database,
   168  		Branch:      branch,
   169  		User:        user,
   170  		Host:        host,
   171  		Permissions: permissions,
   172  	})
   173  }
   174  
   175  // Rows returns the underlying rows.
   176  func (binlog *Binlog) Rows() []BinlogRow {
   177  	return binlog.rows
   178  }
   179  
   180  // Serialize returns the offset for the BinlogRow written to the given builder.
   181  func (row *BinlogRow) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
   182  	database := b.CreateSharedString(row.Database)
   183  	branch := b.CreateSharedString(row.Branch)
   184  	user := b.CreateSharedString(row.User)
   185  	host := b.CreateSharedString(row.Host)
   186  
   187  	serial.BranchControlBinlogRowStart(b)
   188  	serial.BranchControlBinlogRowAddIsInsert(b, row.IsInsert)
   189  	serial.BranchControlBinlogRowAddDatabase(b, database)
   190  	serial.BranchControlBinlogRowAddBranch(b, branch)
   191  	serial.BranchControlBinlogRowAddUser(b, user)
   192  	serial.BranchControlBinlogRowAddHost(b, host)
   193  	serial.BranchControlBinlogRowAddPermissions(b, row.Permissions)
   194  	return serial.BranchControlBinlogRowEnd(b)
   195  }