github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/doltdb/root_val.go (about) 1 // Copyright 2019 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 doltdb 16 17 import ( 18 "bytes" 19 "context" 20 "errors" 21 "fmt" 22 "sort" 23 "strings" 24 25 flatbuffers "github.com/dolthub/flatbuffers/v23/go" 26 27 "github.com/dolthub/dolt/go/gen/fb/serial" 28 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable" 29 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 30 "github.com/dolthub/dolt/go/store/datas" 31 "github.com/dolthub/dolt/go/store/hash" 32 "github.com/dolthub/dolt/go/store/prolly" 33 "github.com/dolthub/dolt/go/store/prolly/tree" 34 "github.com/dolthub/dolt/go/store/types" 35 ) 36 37 const ( 38 ddbRootStructName = "dolt_db_root" 39 40 tablesKey = "tables" 41 foreignKeyKey = "foreign_key" 42 featureVersKey = "feature_ver" 43 rootCollationKey = "root_collation_key" 44 45 // deprecated 46 superSchemasKey = "super_schemas" 47 ) 48 49 type FeatureVersion int64 50 51 // DoltFeatureVersion is described in feature_version.md. 52 // only variable for testing. 53 var DoltFeatureVersion FeatureVersion = 7 // last bumped when fixing bug related to GeomAddrs not getting pushed 54 55 // RootValue is the value of the Database and is the committed value in every Dolt or Doltgres commit. 56 type RootValue interface { 57 Rootish 58 59 // CreateDatabaseSchema creates the given schema. This differs from a table's schema. 60 CreateDatabaseSchema(ctx context.Context, dbSchema schema.DatabaseSchema) (RootValue, error) 61 // DebugString returns a human readable string with the contents of this root. If |transitive| is true, row data from 62 // all tables is also included. This method is very expensive for large root values, so |transitive| should only be used 63 // when debugging tests. 64 DebugString(ctx context.Context, transitive bool) string 65 // GetCollation returns the database collation. 66 GetCollation(ctx context.Context) (schema.Collation, error) 67 // GetDatabaseSchemas returns all schemas. These differ from a table's schema. 68 GetDatabaseSchemas(ctx context.Context) ([]schema.DatabaseSchema, error) 69 // GetFeatureVersion returns the feature version of this root, if one is written 70 GetFeatureVersion(ctx context.Context) (ver FeatureVersion, ok bool, err error) 71 // GetForeignKeyCollection returns the ForeignKeyCollection for this root. As collections are meant to be modified 72 // in-place, each returned collection may freely be altered without affecting future returned collections from this root. 73 GetForeignKeyCollection(ctx context.Context) (*ForeignKeyCollection, error) 74 // GetTable will retrieve a table by its case-sensitive name. 75 GetTable(ctx context.Context, tName TableName) (*Table, bool, error) 76 // GetTableHash returns the hash of the given case-sensitive table name. 77 GetTableHash(ctx context.Context, tName string) (hash.Hash, bool, error) 78 // GetTableNames retrieves the lists of all tables for a RootValue 79 GetTableNames(ctx context.Context, schemaName string) ([]string, error) 80 // HandlePostMerge handles merging for any root elements that are not handled by the standard merge workflow. This 81 // is called at the end of the standard merge workflow. The calling root is the merged root, so it is valid to 82 // return it if no further merge work needs to be done. This is primarily used by Doltgres. 83 HandlePostMerge(ctx context.Context, ourRoot, theirRoot, ancRoot RootValue) (RootValue, error) 84 // HasTable returns whether the root has a table with the given case-sensitive name. 85 HasTable(ctx context.Context, tName string) (bool, error) 86 // IterTables calls the callback function cb on each table in this RootValue. 87 IterTables(ctx context.Context, cb func(name string, table *Table, sch schema.Schema) (stop bool, err error)) error 88 // NodeStore returns this root's NodeStore. 89 NodeStore() tree.NodeStore 90 // NomsValue returns this root's storage as a noms value. 91 NomsValue() types.Value 92 // PutForeignKeyCollection returns a new root with the given foreign key collection. 93 PutForeignKeyCollection(ctx context.Context, fkc *ForeignKeyCollection) (RootValue, error) 94 // PutTable inserts a table by name into the map of tables. If a table already exists with that name it will be replaced 95 PutTable(ctx context.Context, tName TableName, table *Table) (RootValue, error) 96 // RemoveTables removes the given case-sensitive tables from the root, and returns a new root. 97 RemoveTables(ctx context.Context, skipFKHandling bool, allowDroppingFKReferenced bool, tables ...string) (RootValue, error) 98 // RenameTable renames a table by changing its string key in the RootValue's table map. In order to preserve 99 // column tag information, use this method instead of a table drop + add. 100 RenameTable(ctx context.Context, oldName string, newName string) (RootValue, error) 101 // ResolveTableName resolves a case-insensitive name to the exact name as stored in Dolt. Returns false if no matching 102 // name was found. 103 ResolveTableName(ctx context.Context, tName string) (string, bool, error) 104 // SetCollation sets the given database collation and returns a new root. 105 SetCollation(ctx context.Context, collation schema.Collation) (RootValue, error) 106 // SetFeatureVersion sets the feature version and returns a new root. 107 SetFeatureVersion(v FeatureVersion) (RootValue, error) 108 // SetTableHash sets the table with the given case-sensitive name to the new hash, and returns a new root. 109 SetTableHash(ctx context.Context, tName string, h hash.Hash) (RootValue, error) 110 // VRW returns this root's ValueReadWriter. 111 VRW() types.ValueReadWriter 112 } 113 114 // rootValue is Dolt's implementation of RootValue. 115 type rootValue struct { 116 vrw types.ValueReadWriter 117 ns tree.NodeStore 118 st rootValueStorage 119 fkc *ForeignKeyCollection // cache the first load 120 hash hash.Hash // cache first load 121 } 122 123 var _ RootValue = (*rootValue)(nil) 124 125 type tableEdit struct { 126 name TableName 127 ref *types.Ref 128 129 // Used for rename. 130 old_name string 131 } 132 133 // NewRootValue returns a new RootValue. This is a variable as it's changed in Doltgres. 134 var NewRootValue = func(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, v types.Value) (RootValue, error) { 135 var storage rootValueStorage 136 137 if vrw.Format().UsesFlatbuffers() { 138 srv, err := serial.TryGetRootAsRootValue([]byte(v.(types.SerialMessage)), serial.MessagePrefixSz) 139 if err != nil { 140 return nil, err 141 } 142 storage = fbRvStorage{srv} 143 } else { 144 st, ok := v.(types.Struct) 145 if !ok { 146 return nil, errors.New("invalid value passed to newRootValue") 147 } 148 149 storage = nomsRvStorage{st} 150 } 151 ver, ok, err := storage.GetFeatureVersion() 152 if err != nil { 153 return nil, err 154 } 155 if ok { 156 if DoltFeatureVersion < ver { 157 return nil, ErrClientOutOfDate{ 158 ClientVer: DoltFeatureVersion, 159 RepoVer: ver, 160 } 161 } 162 } 163 164 return &rootValue{vrw, ns, storage, nil, hash.Hash{}}, nil 165 } 166 167 // EmptyRootValue returns an empty RootValue. This is a variable as it's changed in Doltgres. 168 var EmptyRootValue = func(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore) (RootValue, error) { 169 if vrw.Format().UsesFlatbuffers() { 170 builder := flatbuffers.NewBuilder(80) 171 172 emptyam, err := prolly.NewEmptyAddressMap(ns) 173 if err != nil { 174 return nil, err 175 } 176 ambytes := []byte(tree.ValueFromNode(emptyam.Node()).(types.SerialMessage)) 177 tablesoff := builder.CreateByteVector(ambytes) 178 179 var empty hash.Hash 180 fkoff := builder.CreateByteVector(empty[:]) 181 serial.RootValueStart(builder) 182 serial.RootValueAddFeatureVersion(builder, int64(DoltFeatureVersion)) 183 serial.RootValueAddCollation(builder, serial.Collationutf8mb4_0900_bin) 184 serial.RootValueAddTables(builder, tablesoff) 185 serial.RootValueAddForeignKeyAddr(builder, fkoff) 186 bs := serial.FinishMessage(builder, serial.RootValueEnd(builder), []byte(serial.RootValueFileID)) 187 return NewRootValue(ctx, vrw, ns, types.SerialMessage(bs)) 188 } 189 190 empty, err := types.NewMap(ctx, vrw) 191 if err != nil { 192 return nil, err 193 } 194 195 sd := types.StructData{ 196 tablesKey: empty, 197 superSchemasKey: empty, 198 foreignKeyKey: empty, 199 featureVersKey: types.Int(DoltFeatureVersion), 200 } 201 202 st, err := types.NewStruct(vrw.Format(), ddbRootStructName, sd) 203 if err != nil { 204 return nil, err 205 } 206 207 return NewRootValue(ctx, vrw, ns, st) 208 } 209 210 // LoadRootValueFromRootIshAddr takes the hash of the commit or the hash of a 211 // working set and returns the corresponding RootValue. 212 func LoadRootValueFromRootIshAddr(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, h hash.Hash) (RootValue, error) { 213 val, err := datas.LoadRootNomsValueFromRootIshAddr(ctx, vrw, h) 214 if err != nil { 215 return nil, err 216 } 217 return decodeRootNomsValue(ctx, vrw, ns, val) 218 } 219 220 func decodeRootNomsValue(ctx context.Context, vrw types.ValueReadWriter, ns tree.NodeStore, val types.Value) (RootValue, error) { 221 if val == nil { 222 return nil, ErrNoRootValAtHash 223 } 224 225 if !isRootValue(vrw.Format(), val) { 226 return nil, ErrNoRootValAtHash 227 } 228 229 return NewRootValue(ctx, vrw, ns, val) 230 } 231 232 // isRootValue returns whether the value is a RootValue. This is a variable as it's changed in Doltgres. 233 func isRootValue(nbf *types.NomsBinFormat, val types.Value) bool { 234 if nbf.UsesFlatbuffers() { 235 if sm, ok := val.(types.SerialMessage); ok { 236 fileID := serial.GetFileID(sm) 237 return fileID == serial.RootValueFileID || fileID == serial.DoltgresRootValueFileID 238 } 239 } else { 240 if st, ok := val.(types.Struct); ok { 241 return st.Name() == ddbRootStructName 242 } 243 } 244 return false 245 } 246 247 func (root *rootValue) ResolveRootValue(ctx context.Context) (RootValue, error) { 248 return root, nil 249 } 250 251 func (root *rootValue) VRW() types.ValueReadWriter { 252 return root.vrw 253 } 254 255 func (root *rootValue) NodeStore() tree.NodeStore { 256 return root.ns 257 } 258 259 // GetFeatureVersion returns the feature version of this root, if one is written 260 func (root *rootValue) GetFeatureVersion(ctx context.Context) (ver FeatureVersion, ok bool, err error) { 261 return root.st.GetFeatureVersion() 262 } 263 264 func (root *rootValue) SetFeatureVersion(v FeatureVersion) (RootValue, error) { 265 newStorage, err := root.st.SetFeatureVersion(v) 266 if err != nil { 267 return nil, err 268 } 269 return root.withStorage(newStorage), nil 270 } 271 272 func (root *rootValue) GetCollation(ctx context.Context) (schema.Collation, error) { 273 return root.st.GetCollation(ctx) 274 } 275 276 func (root *rootValue) SetCollation(ctx context.Context, collation schema.Collation) (RootValue, error) { 277 newStorage, err := root.st.SetCollation(ctx, collation) 278 if err != nil { 279 return nil, err 280 } 281 return root.withStorage(newStorage), nil 282 } 283 284 func (root *rootValue) HandlePostMerge(ctx context.Context, ourRoot, theirRoot, ancRoot RootValue) (RootValue, error) { 285 return root, nil 286 } 287 288 func (root *rootValue) HasTable(ctx context.Context, tName string) (bool, error) { 289 tableMap, err := root.st.GetTablesMap(ctx, root.vrw, root.ns, DefaultSchemaName) 290 if err != nil { 291 return false, err 292 } 293 a, err := tableMap.Get(ctx, tName) 294 if err != nil { 295 return false, err 296 } 297 return !a.IsEmpty(), nil 298 } 299 300 func GenerateTagsForNewColColl(ctx context.Context, root RootValue, tableName string, cc *schema.ColCollection) (*schema.ColCollection, error) { 301 newColNames := make([]string, 0, cc.Size()) 302 newColKinds := make([]types.NomsKind, 0, cc.Size()) 303 _ = cc.Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 304 newColNames = append(newColNames, col.Name) 305 newColKinds = append(newColKinds, col.Kind) 306 return false, nil 307 }) 308 309 newTags, err := GenerateTagsForNewColumns(ctx, root, tableName, newColNames, newColKinds, nil) 310 if err != nil { 311 return nil, err 312 } 313 314 idx := 0 315 return schema.MapColCollection(cc, func(col schema.Column) schema.Column { 316 col.Tag = newTags[idx] 317 idx++ 318 return col 319 }), nil 320 } 321 322 // GenerateTagsForNewColumns deterministically generates a slice of new tags that are unique within the history of this root. The names and NomsKinds of 323 // the new columns are used to see the tag generator. 324 func GenerateTagsForNewColumns( 325 ctx context.Context, 326 root RootValue, 327 tableName string, 328 newColNames []string, 329 newColKinds []types.NomsKind, 330 headRoot RootValue, 331 ) ([]uint64, error) { 332 if len(newColNames) != len(newColKinds) { 333 return nil, fmt.Errorf("error generating tags, newColNames and newColKinds must be of equal length") 334 } 335 336 newTags := make([]*uint64, len(newColNames)) 337 338 // Get existing columns from the current root, or the head root if the table doesn't exist in the current root. The 339 // latter case is to support reusing table tags in the case of drop / create in the same session, which is common 340 // during import. 341 existingCols, err := GetExistingColumns(ctx, root, headRoot, tableName, newColNames, newColKinds) 342 if err != nil { 343 return nil, err 344 } 345 346 // If we found any existing columns set them in the newTags list. 347 for _, col := range existingCols { 348 col := col 349 for i := range newColNames { 350 // Only re-use tags if the noms kind didn't change 351 // TODO: revisit this when new storage format is further along 352 if strings.ToLower(newColNames[i]) == strings.ToLower(col.Name) && 353 newColKinds[i] == col.TypeInfo.NomsKind() { 354 newTags[i] = &col.Tag 355 break 356 } 357 } 358 } 359 360 var existingColKinds []types.NomsKind 361 for _, col := range existingCols { 362 existingColKinds = append(existingColKinds, col.Kind) 363 } 364 365 existingTags, err := GetAllTagsForRoots(ctx, headRoot, root) 366 if err != nil { 367 return nil, err 368 } 369 370 outputTags := make([]uint64, len(newTags)) 371 for i := range newTags { 372 if newTags[i] != nil { 373 outputTags[i] = *newTags[i] 374 continue 375 } 376 377 outputTags[i] = schema.AutoGenerateTag(existingTags, tableName, existingColKinds, newColNames[i], newColKinds[i]) 378 existingColKinds = append(existingColKinds, newColKinds[i]) 379 existingTags.Add(outputTags[i], tableName) 380 } 381 382 return outputTags, nil 383 } 384 385 func GetExistingColumns( 386 ctx context.Context, 387 root, headRoot RootValue, 388 tableName string, 389 newColNames []string, 390 newColKinds []types.NomsKind, 391 ) ([]schema.Column, error) { 392 393 var existingCols []schema.Column 394 tbl, found, err := root.GetTable(ctx, TableName{Name: tableName}) 395 if err != nil { 396 return nil, err 397 } 398 399 if found { 400 sch, err := tbl.GetSchema(ctx) 401 if err != nil { 402 return nil, err 403 } 404 _ = sch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 405 existingCols = append(existingCols, col) 406 return false, nil 407 }) 408 } else if headRoot != nil { 409 tbl, found, err := headRoot.GetTable(ctx, TableName{Name: tableName}) 410 if err != nil { 411 return nil, err 412 } 413 414 if found { 415 sch, err := tbl.GetSchema(ctx) 416 if err != nil { 417 return nil, err 418 } 419 420 existingCols = schema.GetSharedCols(sch, newColNames, newColKinds) 421 } 422 } 423 424 return existingCols, nil 425 } 426 427 func GetAllSchemas(ctx context.Context, root RootValue) (map[string]schema.Schema, error) { 428 m := make(map[string]schema.Schema) 429 err := root.IterTables(ctx, func(name string, table *Table, sch schema.Schema) (stop bool, err error) { 430 m[name] = sch 431 return false, nil 432 }) 433 434 if err != nil { 435 return nil, err 436 } 437 438 return m, nil 439 } 440 441 func (root *rootValue) GetTableHash(ctx context.Context, tName string) (hash.Hash, bool, error) { 442 // TODO: schema 443 tableMap, err := root.getTableMap(ctx, DefaultSchemaName) 444 if err != nil { 445 return hash.Hash{}, false, err 446 } 447 448 tVal, err := tableMap.Get(ctx, tName) 449 if err != nil { 450 return hash.Hash{}, false, err 451 } 452 453 return tVal, !tVal.IsEmpty(), nil 454 } 455 456 func (root *rootValue) SetTableHash(ctx context.Context, tName string, h hash.Hash) (RootValue, error) { 457 val, err := root.vrw.ReadValue(ctx, h) 458 459 if err != nil { 460 return nil, err 461 } 462 463 ref, err := types.NewRef(val, root.vrw.Format()) 464 465 if err != nil { 466 return nil, err 467 } 468 469 // TODO: schema 470 return root.putTable(ctx, TableName{Name: tName}, ref) 471 } 472 473 // ResolveTableName resolves a case-insensitive name to the exact name as stored in Dolt. Returns false if no matching 474 // name was found. 475 func (root *rootValue) ResolveTableName(ctx context.Context, tName string) (string, bool, error) { 476 // TODO: schema name 477 tableMap, err := root.getTableMap(ctx, DefaultSchemaName) 478 if err != nil { 479 return "", false, err 480 } 481 482 a, err := tableMap.Get(ctx, tName) 483 if err != nil { 484 return "", false, err 485 } 486 if !a.IsEmpty() { 487 return tName, true, nil 488 } 489 490 found := false 491 lwrName := strings.ToLower(tName) 492 err = tmIterAll(ctx, tableMap, func(name string, addr hash.Hash) { 493 if found == false && lwrName == strings.ToLower(name) { 494 tName = name 495 found = true 496 } 497 }) 498 if err != nil { 499 return "", false, nil 500 } 501 return tName, found, nil 502 } 503 504 // GetTable will retrieve a table by its case-sensitive name. 505 func (root *rootValue) GetTable(ctx context.Context, tName TableName) (*Table, bool, error) { 506 tableMap, err := root.getTableMap(ctx, tName.Schema) 507 if err != nil { 508 return nil, false, err 509 } 510 511 addr, err := tableMap.Get(ctx, tName.Name) 512 if err != nil { 513 return nil, false, err 514 } 515 516 return GetTable(ctx, root, addr) 517 } 518 519 func GetTable(ctx context.Context, root RootValue, addr hash.Hash) (*Table, bool, error) { 520 if addr.IsEmpty() { 521 return nil, false, nil 522 } 523 table, err := durable.TableFromAddr(ctx, root.VRW(), root.NodeStore(), addr) 524 if err != nil { 525 return nil, false, err 526 } 527 return &Table{table: table}, true, err 528 } 529 530 // GetTableInsensitive will retrieve a table by its case-insensitive name. 531 func GetTableInsensitive(ctx context.Context, root RootValue, tName string) (*Table, string, bool, error) { 532 resolvedName, ok, err := root.ResolveTableName(ctx, tName) 533 if err != nil { 534 return nil, "", false, err 535 } 536 if !ok { 537 return nil, "", false, nil 538 } 539 tbl, ok, err := root.GetTable(ctx, TableName{Name: resolvedName}) 540 if err != nil { 541 return nil, "", false, err 542 } 543 return tbl, resolvedName, ok, nil 544 } 545 546 // GetTableByColTag looks for the table containing the given column tag. 547 func GetTableByColTag(ctx context.Context, root RootValue, tag uint64) (tbl *Table, name string, found bool, err error) { 548 err = root.IterTables(ctx, func(tn string, t *Table, s schema.Schema) (bool, error) { 549 _, found = s.GetAllCols().GetByTag(tag) 550 if found { 551 name, tbl = tn, t 552 } 553 554 return found, nil 555 }) 556 557 if err != nil { 558 return nil, "", false, err 559 } 560 561 return tbl, name, found, nil 562 } 563 564 // GetTableNames retrieves the lists of all tables for a RootValue 565 func (root *rootValue) GetTableNames(ctx context.Context, schemaName string) ([]string, error) { 566 tableMap, err := root.getTableMap(ctx, schemaName) 567 if err != nil { 568 return nil, err 569 } 570 571 var names []string 572 err = tmIterAll(ctx, tableMap, func(name string, _ hash.Hash) { 573 names = append(names, name) 574 }) 575 if err != nil { 576 return nil, err 577 } 578 579 return names, nil 580 } 581 582 func (root *rootValue) getTableMap(ctx context.Context, schemaName string) (tableMap, error) { 583 if schemaName == "" { 584 schemaName = DefaultSchemaName 585 } 586 return root.st.GetTablesMap(ctx, root.vrw, root.ns, schemaName) 587 } 588 589 func TablesWithDataConflicts(ctx context.Context, root RootValue) ([]string, error) { 590 names, err := root.GetTableNames(ctx, DefaultSchemaName) 591 if err != nil { 592 return nil, err 593 } 594 595 conflicted := make([]string, 0, len(names)) 596 for _, name := range names { 597 tbl, _, err := root.GetTable(ctx, TableName{Name: name}) 598 if err != nil { 599 return nil, err 600 } 601 602 ok, err := tbl.HasConflicts(ctx) 603 if err != nil { 604 return nil, err 605 } 606 if ok { 607 conflicted = append(conflicted, name) 608 } 609 } 610 611 return conflicted, nil 612 } 613 614 // TablesWithConstraintViolations returns all tables that have constraint violations. 615 func TablesWithConstraintViolations(ctx context.Context, root RootValue) ([]string, error) { 616 // TODO: schema name 617 names, err := root.GetTableNames(ctx, DefaultSchemaName) 618 if err != nil { 619 return nil, err 620 } 621 622 violating := make([]string, 0, len(names)) 623 for _, name := range names { 624 tbl, _, err := root.GetTable(ctx, TableName{Name: name}) 625 if err != nil { 626 return nil, err 627 } 628 629 n, err := tbl.NumConstraintViolations(ctx) 630 if err != nil { 631 return nil, err 632 } 633 634 if n > 0 { 635 violating = append(violating, name) 636 } 637 } 638 639 return violating, nil 640 } 641 642 func HasConflicts(ctx context.Context, root RootValue) (bool, error) { 643 cnfTbls, err := TablesWithDataConflicts(ctx, root) 644 645 if err != nil { 646 return false, err 647 } 648 649 return len(cnfTbls) > 0, nil 650 } 651 652 // HasConstraintViolations returns whether any tables have constraint violations. 653 func HasConstraintViolations(ctx context.Context, root RootValue) (bool, error) { 654 tbls, err := TablesWithConstraintViolations(ctx, root) 655 if err != nil { 656 return false, err 657 } 658 return len(tbls) > 0, nil 659 } 660 661 // IterTables calls the callback function cb on each table in this RootValue. 662 func (root *rootValue) IterTables(ctx context.Context, cb func(name string, table *Table, sch schema.Schema) (stop bool, err error)) error { 663 // TODO: schema name 664 tm, err := root.getTableMap(ctx, DefaultSchemaName) 665 if err != nil { 666 return err 667 } 668 669 return tm.Iter(ctx, func(name string, addr hash.Hash) (bool, error) { 670 nt, err := durable.TableFromAddr(ctx, root.VRW(), root.ns, addr) 671 if err != nil { 672 return true, err 673 } 674 tbl := &Table{table: nt} 675 676 sch, err := tbl.GetSchema(ctx) 677 if err != nil { 678 return true, err 679 } 680 681 return cb(name, tbl, sch) 682 }) 683 } 684 685 func (root *rootValue) withStorage(st rootValueStorage) *rootValue { 686 return &rootValue{root.vrw, root.ns, st, nil, hash.Hash{}} 687 } 688 689 func (root *rootValue) NomsValue() types.Value { 690 return root.st.nomsValue() 691 } 692 693 // TableName identifies a table in a database uniquely. 694 type TableName struct { 695 // Name is the name of the table 696 Name string 697 // Schema is the name of the schema that the table belongs to, empty in the case of the default schema. 698 Schema string 699 } 700 701 // DefaultSchemaName is the name of the default schema. Tables with this schema name will be stored in the 702 // primary (unnamed) table store in a root. 703 var DefaultSchemaName = "" 704 705 // PutTable inserts a table by name into the map of tables. If a table already exists with that name it will be replaced 706 func (root *rootValue) PutTable(ctx context.Context, tName TableName, table *Table) (RootValue, error) { 707 err := ValidateTagUniqueness(ctx, root, tName.Name, table) 708 if err != nil { 709 return nil, err 710 } 711 712 tableRef, err := RefFromNomsTable(ctx, table) 713 if err != nil { 714 return nil, err 715 } 716 717 return root.putTable(ctx, tName, tableRef) 718 } 719 720 func RefFromNomsTable(ctx context.Context, table *Table) (types.Ref, error) { 721 return durable.RefFromNomsTable(ctx, table.table) 722 } 723 724 func (root *rootValue) putTable(ctx context.Context, tName TableName, ref types.Ref) (RootValue, error) { 725 if !IsValidTableName(tName.Name) { 726 panic("Don't attempt to put a table with a name that fails the IsValidTableName check") 727 } 728 729 newStorage, err := root.st.EditTablesMap(ctx, root.VRW(), root.NodeStore(), []tableEdit{{name: tName, ref: &ref}}) 730 if err != nil { 731 return nil, err 732 } 733 734 return root.withStorage(newStorage), nil 735 } 736 737 // CreateEmptyTable creates an empty table in this root with the name and schema given, returning the new root value. 738 func CreateEmptyTable(ctx context.Context, root RootValue, tName TableName, sch schema.Schema) (RootValue, error) { 739 ns := root.NodeStore() 740 vrw := root.VRW() 741 empty, err := durable.NewEmptyIndex(ctx, vrw, ns, sch) 742 if err != nil { 743 return nil, err 744 } 745 746 indexes, err := durable.NewIndexSet(ctx, vrw, ns) 747 if err != nil { 748 return nil, err 749 } 750 err = sch.Indexes().Iter(func(index schema.Index) (stop bool, err error) { 751 // create an empty map for every index 752 indexes, err = indexes.PutIndex(ctx, index.Name(), empty) 753 return 754 }) 755 if err != nil { 756 return nil, err 757 } 758 759 tbl, err := NewTable(ctx, vrw, ns, sch, empty, indexes, nil) 760 if err != nil { 761 return nil, err 762 } 763 764 newRoot, err := root.PutTable(ctx, tName, tbl) 765 if err != nil { 766 return nil, err 767 } 768 769 return newRoot, nil 770 } 771 772 func (root *rootValue) GetDatabaseSchemas(ctx context.Context) ([]schema.DatabaseSchema, error) { 773 existingSchemas, err := root.st.GetSchemas(ctx) 774 if err != nil { 775 return nil, err 776 } 777 778 return existingSchemas, nil 779 } 780 781 func (root *rootValue) CreateDatabaseSchema(ctx context.Context, dbSchema schema.DatabaseSchema) (RootValue, error) { 782 existingSchemas, err := root.st.GetSchemas(ctx) 783 if err != nil { 784 return nil, err 785 } 786 787 for _, s := range existingSchemas { 788 if strings.EqualFold(s.Name, dbSchema.Name) { 789 return nil, fmt.Errorf("A schema with the name %s already exists", dbSchema.Name) 790 } 791 } 792 793 existingSchemas = append(existingSchemas, dbSchema) 794 sort.Slice(existingSchemas, func(i, j int) bool { 795 return existingSchemas[i].Name < existingSchemas[j].Name 796 }) 797 798 r, err := root.st.SetSchemas(ctx, existingSchemas) 799 if err != nil { 800 return nil, err 801 } 802 803 return root.withStorage(r), nil 804 } 805 806 // HashOf gets the hash of the root value 807 func (root *rootValue) HashOf() (hash.Hash, error) { 808 if root.hash.IsEmpty() { 809 var err error 810 root.hash, err = root.st.nomsValue().Hash(root.vrw.Format()) 811 if err != nil { 812 return hash.Hash{}, nil 813 } 814 } 815 return root.hash, nil 816 } 817 818 // RenameTable renames a table by changing its string key in the RootValue's table map. In order to preserve 819 // column tag information, use this method instead of a table drop + add. 820 func (root *rootValue) RenameTable(ctx context.Context, oldName, newName string) (RootValue, error) { 821 newStorage, err := root.st.EditTablesMap(ctx, root.vrw, root.ns, []tableEdit{{old_name: oldName, name: TableName{Name: newName}}}) 822 if err != nil { 823 return nil, err 824 } 825 return root.withStorage(newStorage), nil 826 } 827 828 func (root *rootValue) RemoveTables(ctx context.Context, skipFKHandling bool, allowDroppingFKReferenced bool, tables ...string) (RootValue, error) { 829 // TODO: schema name 830 tableMap, err := root.getTableMap(ctx, DefaultSchemaName) 831 if err != nil { 832 return nil, err 833 } 834 835 edits := make([]tableEdit, len(tables)) 836 for i, name := range tables { 837 a, err := tableMap.Get(ctx, name) 838 if err != nil { 839 return nil, err 840 } 841 if a.IsEmpty() { 842 return nil, fmt.Errorf("%w: '%s'", ErrTableNotFound, name) 843 } 844 edits[i].name = TableName{ 845 Name: name, 846 } 847 } 848 849 newStorage, err := root.st.EditTablesMap(ctx, root.vrw, root.ns, edits) 850 if err != nil { 851 return nil, err 852 } 853 854 newRoot := root.withStorage(newStorage) 855 if skipFKHandling { 856 return newRoot, nil 857 } 858 859 fkc, err := newRoot.GetForeignKeyCollection(ctx) 860 if err != nil { 861 return nil, err 862 } 863 864 if allowDroppingFKReferenced { 865 err = fkc.RemoveAndUnresolveTables(ctx, root, tables...) 866 } else { 867 err = fkc.RemoveTables(ctx, tables...) 868 } 869 870 if err != nil { 871 return nil, err 872 } 873 874 return newRoot.PutForeignKeyCollection(ctx, fkc) 875 } 876 877 // GetForeignKeyCollection returns the ForeignKeyCollection for this root. As collections are meant to be modified 878 // in-place, each returned collection may freely be altered without affecting future returned collections from this root. 879 func (root *rootValue) GetForeignKeyCollection(ctx context.Context) (*ForeignKeyCollection, error) { 880 if root.fkc == nil { 881 fkMap, ok, err := root.st.GetForeignKeys(ctx, root.vrw) 882 if err != nil { 883 return nil, err 884 } 885 if !ok { 886 fkc := &ForeignKeyCollection{ 887 foreignKeys: map[string]ForeignKey{}, 888 } 889 return fkc, nil 890 } 891 892 root.fkc, err = DeserializeForeignKeys(ctx, root.vrw.Format(), fkMap) 893 if err != nil { 894 return nil, err 895 } 896 } 897 return root.fkc.Copy(), nil 898 } 899 900 // PutForeignKeyCollection returns a new root with the given foreign key collection. 901 func (root *rootValue) PutForeignKeyCollection(ctx context.Context, fkc *ForeignKeyCollection) (RootValue, error) { 902 value, err := SerializeForeignKeys(ctx, root.vrw, fkc) 903 if err != nil { 904 return nil, err 905 } 906 newStorage, err := root.st.SetForeignKeyMap(ctx, root.vrw, value) 907 if err != nil { 908 return nil, err 909 } 910 return root.withStorage(newStorage), nil 911 } 912 913 // ValidateForeignKeysOnSchemas ensures that all foreign keys' tables are present, removing any foreign keys where the declared 914 // table is missing, and returning an error if a key is in an invalid state or a referenced table is missing. Does not 915 // check any tables' row data. 916 func ValidateForeignKeysOnSchemas(ctx context.Context, root RootValue) (RootValue, error) { 917 fkCollection, err := root.GetForeignKeyCollection(ctx) 918 if err != nil { 919 return nil, err 920 } 921 922 // TODO: schema name 923 allTablesSlice, err := root.GetTableNames(ctx, DefaultSchemaName) 924 if err != nil { 925 return nil, err 926 } 927 allTablesSet := make(map[string]schema.Schema) 928 for _, tableName := range allTablesSlice { 929 tbl, ok, err := root.GetTable(ctx, TableName{Name: tableName}) 930 if err != nil { 931 return nil, err 932 } 933 if !ok { 934 return nil, fmt.Errorf("found table `%s` in staging but could not load for foreign key check", tableName) 935 } 936 tblSch, err := tbl.GetSchema(ctx) 937 if err != nil { 938 return nil, err 939 } 940 allTablesSet[tableName] = tblSch 941 } 942 943 // some of these checks are sanity checks and should never happen 944 allForeignKeys := fkCollection.AllKeys() 945 for _, foreignKey := range allForeignKeys { 946 tblSch, existsInRoot := allTablesSet[foreignKey.TableName] 947 if existsInRoot { 948 if err := foreignKey.ValidateTableSchema(tblSch); err != nil { 949 return nil, err 950 } 951 parentSch, existsInRoot := allTablesSet[foreignKey.ReferencedTableName] 952 if !existsInRoot { 953 return nil, fmt.Errorf("foreign key `%s` requires the referenced table `%s`", foreignKey.Name, foreignKey.ReferencedTableName) 954 } 955 if err := foreignKey.ValidateReferencedTableSchema(parentSch); err != nil { 956 return nil, err 957 } 958 } else { 959 if !fkCollection.RemoveKeyByName(foreignKey.Name) { 960 return nil, fmt.Errorf("`%s` does not exist as a foreign key", foreignKey.Name) 961 } 962 } 963 } 964 965 return root.PutForeignKeyCollection(ctx, fkCollection) 966 } 967 968 // GetAllTagsForRoots gets all tags for |roots|. 969 func GetAllTagsForRoots(ctx context.Context, roots ...RootValue) (tags schema.TagMapping, err error) { 970 tags = make(schema.TagMapping) 971 for _, root := range roots { 972 if root == nil { 973 continue 974 } 975 err = root.IterTables(ctx, func(tblName string, _ *Table, sch schema.Schema) (stop bool, err error) { 976 for _, t := range sch.GetAllCols().Tags { 977 tags.Add(t, tblName) 978 } 979 return 980 }) 981 if err != nil { 982 break 983 } 984 } 985 return 986 } 987 988 // UnionTableNames returns an array of all table names in all roots passed as params. 989 // The table names are in order of the RootValues passed in. 990 func UnionTableNames(ctx context.Context, roots ...RootValue) ([]string, error) { 991 seenTblNamesMap := make(map[string]bool) 992 tblNames := []string{} 993 for _, root := range roots { 994 // TODO: schema name 995 rootTblNames, err := root.GetTableNames(ctx, DefaultSchemaName) 996 if err != nil { 997 return nil, err 998 } 999 for _, tn := range rootTblNames { 1000 if _, ok := seenTblNamesMap[tn]; !ok { 1001 seenTblNamesMap[tn] = true 1002 tblNames = append(tblNames, tn) 1003 } 1004 } 1005 } 1006 1007 return tblNames, nil 1008 } 1009 1010 // FilterIgnoredTables takes a slice of table names and divides it into new slices based on whether the table is ignored, not ignored, or matches conflicting ignore patterns. 1011 func FilterIgnoredTables(ctx context.Context, tables []string, roots Roots) (ignoredTables IgnoredTables, err error) { 1012 ignorePatterns, err := GetIgnoredTablePatterns(ctx, roots) 1013 if err != nil { 1014 return ignoredTables, err 1015 } 1016 for _, tableName := range tables { 1017 ignored, err := ignorePatterns.IsTableNameIgnored(tableName) 1018 if conflict := AsDoltIgnoreInConflict(err); conflict != nil { 1019 ignoredTables.Conflicts = append(ignoredTables.Conflicts, *conflict) 1020 } else if err != nil { 1021 return ignoredTables, err 1022 } else if ignored == DontIgnore { 1023 ignoredTables.DontIgnore = append(ignoredTables.DontIgnore, tableName) 1024 } else if ignored == Ignore { 1025 ignoredTables.Ignore = append(ignoredTables.Ignore, tableName) 1026 } else { 1027 panic("IsTableNameIgnored returned ErrorOccurred but no error!") 1028 } 1029 } 1030 1031 return ignoredTables, nil 1032 } 1033 1034 // ValidateTagUniqueness checks for tag collisions between the given table and the set of tables in then given root. 1035 func ValidateTagUniqueness(ctx context.Context, root RootValue, tableName string, table *Table) error { 1036 prev, ok, err := root.GetTable(ctx, TableName{Name: tableName}) 1037 if err != nil { 1038 return err 1039 } 1040 if ok { 1041 prevHash, err := prev.GetSchemaHash(ctx) 1042 if err != nil { 1043 return err 1044 } 1045 1046 newHash, err := table.GetSchemaHash(ctx) 1047 if err != nil { 1048 return err 1049 } 1050 1051 // short-circuit if schema unchanged 1052 if prevHash == newHash { 1053 return nil 1054 } 1055 } 1056 1057 sch, err := table.GetSchema(ctx) 1058 if err != nil { 1059 return err 1060 } 1061 1062 existing, err := GetAllTagsForRoots(ctx, root) 1063 if err != nil { 1064 return err 1065 } 1066 1067 err = sch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 1068 oldTableName, ok := existing.Get(tag) 1069 if ok && oldTableName != tableName { 1070 return true, schema.ErrTagPrevUsed(tag, col.Name, tableName, oldTableName) 1071 } 1072 return false, nil 1073 }) 1074 if err != nil { 1075 return err 1076 } 1077 return nil 1078 } 1079 1080 // DebugString returns a human readable string with the contents of this root. If |transitive| is true, row data from 1081 // all tables is also included. This method is very expensive for large root values, so |transitive| should only be used 1082 // when debugging tests. 1083 func (root *rootValue) DebugString(ctx context.Context, transitive bool) string { 1084 var buf bytes.Buffer 1085 buf.WriteString(root.st.DebugString(ctx)) 1086 1087 if transitive { 1088 buf.WriteString("\nTables:") 1089 root.IterTables(ctx, func(name string, table *Table, sch schema.Schema) (stop bool, err error) { 1090 buf.WriteString("\nTable ") 1091 buf.WriteString(name) 1092 buf.WriteString(":\n") 1093 1094 buf.WriteString(table.DebugString(ctx, root.ns)) 1095 1096 return false, nil 1097 }) 1098 } 1099 1100 return buf.String() 1101 } 1102 1103 // MapTableHashes returns a map of each table name and hash. 1104 func MapTableHashes(ctx context.Context, root RootValue) (map[string]hash.Hash, error) { 1105 // TODO: schema name 1106 names, err := root.GetTableNames(ctx, DefaultSchemaName) 1107 if err != nil { 1108 return nil, err 1109 } 1110 nameToHash := make(map[string]hash.Hash) 1111 for _, name := range names { 1112 h, ok, err := root.GetTableHash(ctx, name) 1113 if err != nil { 1114 return nil, err 1115 } else if !ok { 1116 return nil, fmt.Errorf("root found a table with name '%s' but no hash", name) 1117 } else { 1118 nameToHash[name] = h 1119 } 1120 } 1121 return nameToHash, nil 1122 } 1123 1124 type DataCacheKey struct { 1125 hash.Hash 1126 } 1127 1128 func NewDataCacheKey(rv RootValue) (DataCacheKey, error) { 1129 hash, err := rv.HashOf() 1130 if err != nil { 1131 return DataCacheKey{}, err 1132 } 1133 1134 return DataCacheKey{hash}, nil 1135 }