github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/branch_control/namespace.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  	"github.com/dolthub/go-mysql-server/sql"
    23  
    24  	"github.com/dolthub/dolt/go/gen/fb/serial"
    25  )
    26  
    27  // Namespace contains all of the expressions that comprise the "dolt_branch_namespace_control" table, which controls
    28  // which users may use which branch names when creating branches. Modification of this table is handled by the Access
    29  // table.
    30  type Namespace struct {
    31  	access *Access
    32  	binlog *Binlog
    33  
    34  	Databases []MatchExpression
    35  	Branches  []MatchExpression
    36  	Users     []MatchExpression
    37  	Hosts     []MatchExpression
    38  	Values    []NamespaceValue
    39  	RWMutex   *sync.RWMutex
    40  }
    41  
    42  // NamespaceValue contains the user-facing values of a particular row.
    43  type NamespaceValue struct {
    44  	Database string
    45  	Branch   string
    46  	User     string
    47  	Host     string
    48  }
    49  
    50  // newNamespace returns a new Namespace.
    51  func newNamespace(accessTbl *Access) *Namespace {
    52  	return &Namespace{
    53  		binlog:    NewNamespaceBinlog(nil),
    54  		access:    accessTbl,
    55  		Databases: nil,
    56  		Branches:  nil,
    57  		Users:     nil,
    58  		Hosts:     nil,
    59  		Values:    nil,
    60  		RWMutex:   accessTbl.RWMutex,
    61  	}
    62  }
    63  
    64  // CanCreate checks the given database and branch, and returns whether the given user and host combination is able to
    65  // create that branch. Handles the super user case.
    66  func (tbl *Namespace) CanCreate(database string, branch string, user string, host string) bool {
    67  	filteredIndexes := Match(tbl.Databases, database, sql.Collation_utf8mb4_0900_ai_ci)
    68  	// If there are no database entries, then the Namespace is unrestricted
    69  	if len(filteredIndexes) == 0 {
    70  		indexPool.Put(filteredIndexes)
    71  		return true
    72  	}
    73  
    74  	filteredBranches := tbl.filterBranches(filteredIndexes)
    75  	indexPool.Put(filteredIndexes)
    76  	matchedSet := Match(filteredBranches, branch, sql.Collation_utf8mb4_0900_ai_ci)
    77  	matchExprPool.Put(filteredBranches)
    78  	// If there are no branch entries, then the Namespace is unrestricted
    79  	if len(matchedSet) == 0 {
    80  		indexPool.Put(matchedSet)
    81  		return true
    82  	}
    83  
    84  	// We take either the longest match, or the set of longest matches if multiple matches have the same length
    85  	longest := -1
    86  	filteredIndexes = indexPool.Get().([]uint32)[:0]
    87  	for _, matched := range matchedSet {
    88  		matchedValue := tbl.Values[matched]
    89  		// If we've found a longer match, then we reset the slice. We append to it in the following if statement.
    90  		if len(matchedValue.Branch) > longest {
    91  			filteredIndexes = filteredIndexes[:0]
    92  		}
    93  		if len(matchedValue.Branch) >= longest {
    94  			filteredIndexes = append(filteredIndexes, matched)
    95  		}
    96  	}
    97  	indexPool.Put(matchedSet)
    98  
    99  	filteredUsers := tbl.filterUsers(filteredIndexes)
   100  	indexPool.Put(filteredIndexes)
   101  	filteredIndexes = Match(filteredUsers, user, sql.Collation_utf8mb4_0900_bin)
   102  	matchExprPool.Put(filteredUsers)
   103  
   104  	filteredHosts := tbl.filterHosts(filteredIndexes)
   105  	indexPool.Put(filteredIndexes)
   106  	filteredIndexes = Match(filteredHosts, host, sql.Collation_utf8mb4_0900_ai_ci)
   107  	matchExprPool.Put(filteredHosts)
   108  
   109  	result := len(filteredIndexes) > 0
   110  	indexPool.Put(filteredIndexes)
   111  	return result
   112  }
   113  
   114  // GetIndex returns the index of the given database, branch, user, and host expressions. If the expressions cannot be
   115  // found, returns -1. Assumes that the given expressions have already been folded.
   116  func (tbl *Namespace) GetIndex(databaseExpr string, branchExpr string, userExpr string, hostExpr string) int {
   117  	for i, value := range tbl.Values {
   118  		if value.Database == databaseExpr && value.Branch == branchExpr && value.User == userExpr && value.Host == hostExpr {
   119  			return i
   120  		}
   121  	}
   122  	return -1
   123  }
   124  
   125  // GetBinlog returns the table's binlog.
   126  func (tbl *Namespace) GetBinlog() *Binlog {
   127  	return tbl.binlog
   128  }
   129  
   130  // Access returns the Access table.
   131  func (tbl *Namespace) Access() *Access {
   132  	return tbl.access
   133  }
   134  
   135  // Serialize returns the offset for the Namespace table written to the given builder.
   136  func (tbl *Namespace) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
   137  	// Serialize the binlog
   138  	binlog := tbl.binlog.Serialize(b)
   139  	// Initialize field offset slices
   140  	databaseOffsets := make([]flatbuffers.UOffsetT, len(tbl.Databases))
   141  	branchOffsets := make([]flatbuffers.UOffsetT, len(tbl.Branches))
   142  	userOffsets := make([]flatbuffers.UOffsetT, len(tbl.Users))
   143  	hostOffsets := make([]flatbuffers.UOffsetT, len(tbl.Hosts))
   144  	valueOffsets := make([]flatbuffers.UOffsetT, len(tbl.Values))
   145  	// Get field offsets
   146  	for i, matchExpr := range tbl.Databases {
   147  		databaseOffsets[i] = matchExpr.Serialize(b)
   148  	}
   149  	for i, matchExpr := range tbl.Branches {
   150  		branchOffsets[i] = matchExpr.Serialize(b)
   151  	}
   152  	for i, matchExpr := range tbl.Users {
   153  		userOffsets[i] = matchExpr.Serialize(b)
   154  	}
   155  	for i, matchExpr := range tbl.Hosts {
   156  		hostOffsets[i] = matchExpr.Serialize(b)
   157  	}
   158  	for i, val := range tbl.Values {
   159  		valueOffsets[i] = val.Serialize(b)
   160  	}
   161  	// Get the field vectors
   162  	serial.BranchControlNamespaceStartDatabasesVector(b, len(databaseOffsets))
   163  	for i := len(databaseOffsets) - 1; i >= 0; i-- {
   164  		b.PrependUOffsetT(databaseOffsets[i])
   165  	}
   166  	databases := b.EndVector(len(databaseOffsets))
   167  	serial.BranchControlNamespaceStartBranchesVector(b, len(branchOffsets))
   168  	for i := len(branchOffsets) - 1; i >= 0; i-- {
   169  		b.PrependUOffsetT(branchOffsets[i])
   170  	}
   171  	branches := b.EndVector(len(branchOffsets))
   172  	serial.BranchControlNamespaceStartUsersVector(b, len(userOffsets))
   173  	for i := len(userOffsets) - 1; i >= 0; i-- {
   174  		b.PrependUOffsetT(userOffsets[i])
   175  	}
   176  	users := b.EndVector(len(userOffsets))
   177  	serial.BranchControlNamespaceStartHostsVector(b, len(hostOffsets))
   178  	for i := len(hostOffsets) - 1; i >= 0; i-- {
   179  		b.PrependUOffsetT(hostOffsets[i])
   180  	}
   181  	hosts := b.EndVector(len(hostOffsets))
   182  	serial.BranchControlNamespaceStartValuesVector(b, len(valueOffsets))
   183  	for i := len(valueOffsets) - 1; i >= 0; i-- {
   184  		b.PrependUOffsetT(valueOffsets[i])
   185  	}
   186  	values := b.EndVector(len(valueOffsets))
   187  	// Write the table
   188  	serial.BranchControlNamespaceStart(b)
   189  	serial.BranchControlNamespaceAddBinlog(b, binlog)
   190  	serial.BranchControlNamespaceAddDatabases(b, databases)
   191  	serial.BranchControlNamespaceAddBranches(b, branches)
   192  	serial.BranchControlNamespaceAddUsers(b, users)
   193  	serial.BranchControlNamespaceAddHosts(b, hosts)
   194  	serial.BranchControlNamespaceAddValues(b, values)
   195  	return serial.BranchControlNamespaceEnd(b)
   196  }
   197  
   198  func (tbl *Namespace) reinit() {
   199  	tbl.binlog = NewNamespaceBinlog(nil)
   200  	tbl.Databases = nil
   201  	tbl.Branches = nil
   202  	tbl.Users = nil
   203  	tbl.Hosts = nil
   204  	tbl.Values = nil
   205  }
   206  
   207  // Deserialize populates the table with the data from the flatbuffers representation.
   208  func (tbl *Namespace) Deserialize(fb *serial.BranchControlNamespace) error {
   209  	// Verify that all fields have the same length
   210  	if fb.DatabasesLength() != fb.BranchesLength() ||
   211  		fb.BranchesLength() != fb.UsersLength() ||
   212  		fb.UsersLength() != fb.HostsLength() ||
   213  		fb.HostsLength() != fb.ValuesLength() {
   214  		return fmt.Errorf("cannot deserialize a namespace table with differing field lengths")
   215  	}
   216  	// Read the binlog
   217  	binlog, err := fb.TryBinlog(nil)
   218  	if err != nil {
   219  		return err
   220  	}
   221  	if err = tbl.binlog.Deserialize(binlog); err != nil {
   222  		return err
   223  	}
   224  
   225  	tbl.reinit()
   226  
   227  	// Initialize every slice
   228  	tbl.Databases = make([]MatchExpression, fb.DatabasesLength())
   229  	tbl.Branches = make([]MatchExpression, fb.BranchesLength())
   230  	tbl.Users = make([]MatchExpression, fb.UsersLength())
   231  	tbl.Hosts = make([]MatchExpression, fb.HostsLength())
   232  	tbl.Values = make([]NamespaceValue, fb.ValuesLength())
   233  	// Read the databases
   234  	for i := 0; i < fb.DatabasesLength(); i++ {
   235  		serialMatchExpr := &serial.BranchControlMatchExpression{}
   236  		_, err = fb.TryDatabases(serialMatchExpr, i)
   237  		if err != nil {
   238  			return err
   239  		}
   240  		tbl.Databases[i] = deserializeMatchExpression(serialMatchExpr)
   241  	}
   242  	// Read the branches
   243  	for i := 0; i < fb.BranchesLength(); i++ {
   244  		serialMatchExpr := &serial.BranchControlMatchExpression{}
   245  		_, err = fb.TryBranches(serialMatchExpr, i)
   246  		if err != nil {
   247  			return err
   248  		}
   249  		tbl.Branches[i] = deserializeMatchExpression(serialMatchExpr)
   250  	}
   251  	// Read the users
   252  	for i := 0; i < fb.UsersLength(); i++ {
   253  		serialMatchExpr := &serial.BranchControlMatchExpression{}
   254  		_, err = fb.TryUsers(serialMatchExpr, i)
   255  		if err != nil {
   256  			return err
   257  		}
   258  		tbl.Users[i] = deserializeMatchExpression(serialMatchExpr)
   259  	}
   260  	// Read the hosts
   261  	for i := 0; i < fb.HostsLength(); i++ {
   262  		serialMatchExpr := &serial.BranchControlMatchExpression{}
   263  		_, err = fb.TryHosts(serialMatchExpr, i)
   264  		if err != nil {
   265  			return err
   266  		}
   267  		tbl.Hosts[i] = deserializeMatchExpression(serialMatchExpr)
   268  	}
   269  	// Read the values
   270  	for i := 0; i < fb.ValuesLength(); i++ {
   271  		serialNamespaceValue := &serial.BranchControlNamespaceValue{}
   272  		_, err = fb.TryValues(serialNamespaceValue, i)
   273  		if err != nil {
   274  			return err
   275  		}
   276  		tbl.Values[i] = NamespaceValue{
   277  			Database: string(serialNamespaceValue.Database()),
   278  			Branch:   string(serialNamespaceValue.Branch()),
   279  			User:     string(serialNamespaceValue.User()),
   280  			Host:     string(serialNamespaceValue.Host()),
   281  		}
   282  	}
   283  	return nil
   284  }
   285  
   286  // filterDatabases returns all databases that match the given collection indexes.
   287  func (tbl *Namespace) filterDatabases(filters []uint32) []MatchExpression {
   288  	if len(filters) == 0 {
   289  		return nil
   290  	}
   291  	matchExprs := matchExprPool.Get().([]MatchExpression)[:0]
   292  	for _, filter := range filters {
   293  		matchExprs = append(matchExprs, tbl.Databases[filter])
   294  	}
   295  	return matchExprs
   296  }
   297  
   298  // filterBranches returns all branches that match the given collection indexes.
   299  func (tbl *Namespace) filterBranches(filters []uint32) []MatchExpression {
   300  	if len(filters) == 0 {
   301  		return nil
   302  	}
   303  	matchExprs := matchExprPool.Get().([]MatchExpression)[:0]
   304  	for _, filter := range filters {
   305  		matchExprs = append(matchExprs, tbl.Branches[filter])
   306  	}
   307  	return matchExprs
   308  }
   309  
   310  // filterUsers returns all users that match the given collection indexes.
   311  func (tbl *Namespace) filterUsers(filters []uint32) []MatchExpression {
   312  	if len(filters) == 0 {
   313  		return nil
   314  	}
   315  	matchExprs := matchExprPool.Get().([]MatchExpression)[:0]
   316  	for _, filter := range filters {
   317  		matchExprs = append(matchExprs, tbl.Users[filter])
   318  	}
   319  	return matchExprs
   320  }
   321  
   322  // filterHosts returns all hosts that match the given collection indexes.
   323  func (tbl *Namespace) filterHosts(filters []uint32) []MatchExpression {
   324  	if len(filters) == 0 {
   325  		return nil
   326  	}
   327  	matchExprs := matchExprPool.Get().([]MatchExpression)[:0]
   328  	for _, filter := range filters {
   329  		matchExprs = append(matchExprs, tbl.Hosts[filter])
   330  	}
   331  	return matchExprs
   332  }
   333  
   334  // Serialize returns the offset for the NamespaceValue written to the given builder.
   335  func (val *NamespaceValue) Serialize(b *flatbuffers.Builder) flatbuffers.UOffsetT {
   336  	database := b.CreateSharedString(val.Database)
   337  	branch := b.CreateSharedString(val.Branch)
   338  	user := b.CreateSharedString(val.User)
   339  	host := b.CreateSharedString(val.Host)
   340  
   341  	serial.BranchControlNamespaceValueStart(b)
   342  	serial.BranchControlNamespaceValueAddDatabase(b, database)
   343  	serial.BranchControlNamespaceValueAddBranch(b, branch)
   344  	serial.BranchControlNamespaceValueAddUser(b, user)
   345  	serial.BranchControlNamespaceValueAddHost(b, host)
   346  	return serial.BranchControlNamespaceValueEnd(b)
   347  }