github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/cmd/noms/noms_root.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 // This file incorporates work covered by the following copyright and 16 // permission notice: 17 // 18 // Copyright 2016 Attic Labs, Inc. All rights reserved. 19 // Licensed under the Apache License, version 2.0: 20 // http://www.apache.org/licenses/LICENSE-2.0 21 22 package main 23 24 import ( 25 "context" 26 "fmt" 27 "os" 28 "strings" 29 30 flag "github.com/juju/gnuflag" 31 32 "github.com/dolthub/dolt/go/store/cmd/noms/util" 33 "github.com/dolthub/dolt/go/store/config" 34 "github.com/dolthub/dolt/go/store/d" 35 "github.com/dolthub/dolt/go/store/datas" 36 "github.com/dolthub/dolt/go/store/hash" 37 "github.com/dolthub/dolt/go/store/types" 38 ) 39 40 var nomsRoot = &util.Command{ 41 Run: runRoot, 42 UsageLine: "root <db-spec>", 43 Short: "Get or set the current root hash of the entire database", 44 Long: "See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the database argument.", 45 Flags: setupRootFlags, 46 Nargs: 1, 47 } 48 49 var updateRoot = "" 50 51 func setupRootFlags() *flag.FlagSet { 52 flagSet := flag.NewFlagSet("root", flag.ExitOnError) 53 flagSet.StringVar(&updateRoot, "update", "", "Replaces the entire database with the one with the given hash") 54 return flagSet 55 } 56 57 func runRoot(ctx context.Context, args []string) int { 58 if len(args) < 1 { 59 fmt.Fprintln(os.Stderr, "Not enough arguments") 60 return 0 61 } 62 63 cfg := config.NewResolver() 64 cs, err := cfg.GetChunkStore(ctx, args[0]) 65 util.CheckErrorNoUsage(err) 66 67 currRoot, err := cs.Root(ctx) 68 69 if err != nil { 70 fmt.Fprintln(os.Stderr, "error getting root.", err) 71 return 1 72 } 73 74 if updateRoot == "" { 75 fmt.Println(currRoot) 76 return 0 77 } 78 79 if updateRoot[0] == '#' { 80 updateRoot = updateRoot[1:] 81 } 82 h, ok := hash.MaybeParse(updateRoot) 83 if !ok { 84 fmt.Fprintf(os.Stderr, "Invalid hash: %s\n", h.String()) 85 return 1 86 } 87 88 // If BUG 3407 is correct, we might be able to just take cs and make a Database directly from that. 89 db, err := cfg.GetDatabase(ctx, args[0]) 90 util.CheckErrorNoUsage(err) 91 defer db.Close() 92 v, err := db.ReadValue(ctx, h) 93 util.CheckErrorNoUsage(err) 94 if !validate(ctx, db.Format(), v) { 95 return 1 96 } 97 98 fmt.Println(`WARNING 99 100 This operation replaces the entire database with the instance having the given 101 hash. The old database becomes eligible for GC. 102 103 ANYTHING NOT SAVED WILL BE LOST 104 105 Continue?`) 106 107 var input string 108 n, err := fmt.Scanln(&input) 109 util.CheckErrorNoUsage(err) 110 if n != 1 || strings.ToLower(input) != "y" { 111 return 0 112 } 113 114 ok, err = cs.Commit(ctx, h, currRoot) 115 116 if err != nil { 117 fmt.Fprintf(os.Stderr, "commit error: %s", err.Error()) 118 return 1 119 } 120 121 if !ok { 122 fmt.Fprintln(os.Stderr, "Optimistic concurrency failure") 123 return 1 124 } 125 126 fmt.Printf("Success. Previous root was: %s\n", currRoot) 127 return 0 128 } 129 130 func mustType(t *types.Type, err error) *types.Type { 131 d.PanicIfError(err) 132 return t 133 } 134 135 func mustString(str string, err error) string { 136 d.PanicIfError(err) 137 return str 138 } 139 140 func mustValue(v types.Value, err error) types.Value { 141 d.PanicIfError(err) 142 return v 143 } 144 145 func mustGetValue(v types.Value, ok bool, err error) types.Value { 146 d.PanicIfError(err) 147 d.PanicIfFalse(ok) 148 return v 149 } 150 151 func mustSet(s types.Set, err error) types.Set { 152 d.PanicIfError(err) 153 return s 154 } 155 156 func mustList(l types.List, err error) types.List { 157 d.PanicIfError(err) 158 return l 159 } 160 161 func validate(ctx context.Context, nbf *types.NomsBinFormat, r types.Value) bool { 162 rootType := mustType(types.MakeMapType(types.PrimitiveTypeMap[types.StringKind], mustType(types.MakeRefType(types.PrimitiveTypeMap[types.ValueKind])))) 163 if isSub, err := types.IsValueSubtypeOf(nbf, r, rootType); err != nil { 164 panic(err) 165 } else if !isSub { 166 fmt.Fprintf(os.Stderr, "Root of database must be %s, but you specified: %s\n", mustString(rootType.Describe(ctx)), mustString(mustType(types.TypeOf(r)).Describe(ctx))) 167 return false 168 } 169 170 yep, err := r.(types.Map).Any(ctx, func(k, v types.Value) bool { 171 if !datas.IsRefOfCommitType(nbf, mustType(types.TypeOf(v))) { 172 fmt.Fprintf(os.Stderr, "Invalid root map. Value for key '%s' is not a ref of commit.", string(k.(types.String))) 173 return false 174 } 175 return true 176 }) 177 178 d.PanicIfError(err) 179 180 return yep 181 }