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 }