github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/sugar/path.go (about) 1 package sugar 2 3 import ( 4 "context" 5 "fmt" 6 "path" 7 "strings" 8 9 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 10 "github.com/ydb-platform/ydb-go-sdk/v3/scheme" 11 "github.com/ydb-platform/ydb-go-sdk/v3/table" 12 "github.com/ydb-platform/ydb-go-sdk/v3/topic" 13 ) 14 15 const ( 16 sysDirectory = ".sys" 17 ) 18 19 type dbName interface { 20 Name() string 21 } 22 23 type dbScheme interface { 24 Scheme() scheme.Client 25 } 26 27 type dbTable interface { 28 Table() table.Client 29 } 30 31 type dbTopic interface { 32 Topic() topic.Client 33 } 34 35 type dbForMakeRecursive interface { 36 dbName 37 dbScheme 38 } 39 40 type dbFoRemoveRecursive interface { 41 dbName 42 dbScheme 43 dbTable 44 dbTopic 45 } 46 47 // MakeRecursive creates path inside database 48 // pathToCreate is a database root relative path 49 // MakeRecursive method equal bash command `mkdir -p ~/path/to/create` 50 // where `~` - is a root of database 51 func MakeRecursive(ctx context.Context, db dbForMakeRecursive, pathToCreate string) error { 52 if strings.HasPrefix(pathToCreate, sysDirectory+"/") { 53 return xerrors.WithStackTrace( 54 fmt.Errorf("making directory %q inside system path %q not supported", pathToCreate, sysDirectory), 55 ) 56 } 57 58 absPath := path.Join(db.Name(), pathToCreate) 59 60 err := db.Scheme().MakeDirectory(ctx, absPath) 61 if err != nil { 62 return xerrors.WithStackTrace( 63 fmt.Errorf("cannot make directory %q: %w", absPath, err), 64 ) 65 } 66 67 info, err := db.Scheme().DescribePath(ctx, absPath) 68 if err != nil { 69 return xerrors.WithStackTrace( 70 fmt.Errorf("cannot describe path %q: %w", absPath, err), 71 ) 72 } 73 74 switch info.Type { 75 case 76 scheme.EntryDatabase, 77 scheme.EntryDirectory: 78 return nil 79 default: 80 return xerrors.WithStackTrace( 81 fmt.Errorf("entry %q exists but it is not a directory: %s", absPath, info.Type), 82 ) 83 } 84 } 85 86 // RemoveRecursive removes selected directory or table names in the database. 87 // pathToRemove is a database root relative path. 88 // All database entities in the prefix path will be removed if the names list is empty. 89 // An empty prefix means using the root of the database. 90 // RemoveRecursive method is equivalent to the bash command `rm -rf ~/path/to/remove` 91 // where `~` is the root of the database. 92 func RemoveRecursive(ctx context.Context, db dbFoRemoveRecursive, pathToRemove string) error { 93 fullSysTablePath := path.Join(db.Name(), sysDirectory) 94 95 var rmPath func(int, string) error 96 rmPath = func(depth int, currentPath string) error { 97 exists, err := IsDirectoryExists(ctx, db.Scheme(), currentPath) 98 if err != nil { 99 return xerrors.WithStackTrace( 100 fmt.Errorf("failed to check if directory %q exists: %w", currentPath, err), 101 ) 102 } else if !exists { 103 return nil 104 } 105 106 entry, err := db.Scheme().DescribePath(ctx, currentPath) 107 if err != nil { 108 return xerrors.WithStackTrace( 109 fmt.Errorf("cannot describe path %q: %w", currentPath, err), 110 ) 111 } 112 113 if entry.Type != scheme.EntryDirectory && entry.Type != scheme.EntryDatabase { 114 return nil 115 } 116 117 dir, err := db.Scheme().ListDirectory(ctx, currentPath) 118 if err != nil { 119 return xerrors.WithStackTrace( 120 fmt.Errorf("failed to list directory %q: %w", currentPath, err), 121 ) 122 } 123 124 for i := range dir.Children { 125 child := &dir.Children[i] 126 childPath := path.Join(currentPath, child.Name) 127 if childPath == fullSysTablePath { 128 continue 129 } 130 if err := handleEntry(ctx, db, rmPath, depth, child, childPath); err != nil { 131 return err 132 } 133 } 134 135 if entry.Type == scheme.EntryDirectory { 136 if err := db.Scheme().RemoveDirectory(ctx, currentPath); err != nil { 137 return xerrors.WithStackTrace( 138 fmt.Errorf("failed to remove directory %q: %w", currentPath, err), 139 ) 140 } 141 } 142 143 return nil 144 } 145 146 if !strings.HasPrefix(pathToRemove, db.Name()) { 147 pathToRemove = path.Join(db.Name(), pathToRemove) 148 } 149 150 return rmPath(0, pathToRemove) 151 } 152 153 // handleEntry processes and removes different types of database entries 154 func handleEntry( 155 ctx context.Context, 156 db dbFoRemoveRecursive, 157 rmPath func(int, string) error, 158 depth int, 159 entry *scheme.Entry, 160 entryPath string, 161 ) error { 162 switch entry.Type { 163 case scheme.EntryDirectory: 164 if err := rmPath(depth+1, entryPath); err != nil { 165 return xerrors.WithStackTrace( 166 fmt.Errorf("failed to recursively remove directory %q: %w", entryPath, err), 167 ) 168 } 169 case scheme.EntryTable, scheme.EntryColumnTable: 170 if err := removeTable(ctx, db, entryPath); err != nil { 171 return xerrors.WithStackTrace( 172 fmt.Errorf("failed to remove table %q: %w", entryPath, err), 173 ) 174 } 175 case scheme.EntryTopic: 176 if err := db.Topic().Drop(ctx, entryPath); err != nil { 177 return xerrors.WithStackTrace( 178 fmt.Errorf("failed to remove topic %q: %w", entryPath, err), 179 ) 180 } 181 default: 182 return xerrors.WithStackTrace( 183 fmt.Errorf("unknown entry type: %s", entry.Type.String()), 184 ) 185 } 186 187 return nil 188 } 189 190 // removeTable removes a table in the database 191 func removeTable(ctx context.Context, db dbFoRemoveRecursive, tablePath string) error { 192 return db.Table().Do(ctx, func(ctx context.Context, session table.Session) error { 193 return session.DropTable(ctx, tablePath) 194 }, table.WithIdempotent()) 195 }