#include <Interpreters/Context.h> #include "Common/Exception.h" #include <Common/TerminalSize.h> #include "DisksClient.h" #include "ICommand.h" namespace DB { namespace ErrorCodes { extern const int BAD_ARGUMENTS; } class CommandCopy final : public ICommand { public: explicit CommandCopy() : ICommand() { command_name = "copy"; description = "Recursively copy data from `path-from` to `path-to`"; options_description.add_options()( "disk-from", po::value<String>(), "disk from which we copy is executed (default value is a current disk)")( "disk-to", po::value<String>(), "disk to which copy is executed (default value is a current disk)")( "path-from", po::value<String>(), "path from which copy is executed (mandatory, positional)")( "path-to", po::value<String>(), "path to which copy is executed (mandatory, positional)")( "recursive,r", "recursively copy the directory (required to remove a directory)"); positional_options_description.add("path-from", 1); positional_options_description.add("path-to", 1); } void executeImpl(const CommandLineOptions & options, DisksClient & client) override { auto disk_from = getDiskWithPath(client, options, "disk-from"); auto disk_to = getDiskWithPath(client, options, "disk-to"); String path_from = disk_from.getRelativeFromRoot(getValueFromCommandLineOptionsThrow<String>(options, "path-from")); String path_to = disk_to.getRelativeFromRoot(getValueFromCommandLineOptionsThrow<String>(options, "path-to")); bool recursive = options.count("recursive"); if (!disk_from.getDisk()->exists(path_from)) { throw Exception( ErrorCodes::BAD_ARGUMENTS, "cannot stat '{}' on disk '{}': No such file or directory", path_from, disk_from.getDisk()->getName()); } else if (disk_from.getDisk()->isFile(path_from)) { auto target_location = getTargetLocation(path_from, disk_to, path_to); if (!disk_to.getDisk()->exists(target_location) || disk_to.getDisk()->isFile(target_location)) { disk_from.getDisk()->copyFile( path_from, *disk_to.getDisk(), target_location, /* read_settings= */ {}, /* write_settings= */ {}, /* cancellation_hook= */ {}); } else { throw Exception( ErrorCodes::BAD_ARGUMENTS, "cannot overwrite directory {} with non-directory {}", target_location, path_from); } } else if (disk_from.getDisk()->isDirectory(path_from)) { if (!recursive) { throw Exception(ErrorCodes::BAD_ARGUMENTS, "--recursive not specified; omitting directory {}", path_from); } auto target_location = getTargetLocation(path_from, disk_to, path_to); if (disk_to.getDisk()->isFile(target_location)) { throw Exception(ErrorCodes::BAD_ARGUMENTS, "cannot overwrite non-directory {} with directory {}", path_to, target_location); } else if (!disk_to.getDisk()->exists(target_location)) { disk_to.getDisk()->createDirectory(target_location); } disk_from.getDisk()->copyDirectoryContent( path_from, disk_to.getDisk(), target_location, /* read_settings= */ {}, /* write_settings= */ {}, /* cancellation_hook= */ {}); } } }; CommandPtr makeCommandCopy() { return std::make_shared<DB::CommandCopy>(); } }