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  }