Skip to content

Commit

Permalink
Check the file is accessible before trying to read it (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
elefeint authored Mar 26, 2024
1 parent fa638da commit 1113e7c
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 7 deletions.
12 changes: 12 additions & 0 deletions src/motherduck_destination_server.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
Expand Down Expand Up @@ -85,12 +86,23 @@ get_encryption_key(const std::string &filename,
return encryption_key_it->second;
}

void validate_file(const std::string &file_path) {
std::ifstream fs(file_path.c_str());
if (fs.good()) {
fs.close();
return;
}
throw std::invalid_argument("File <" + file_path +
"> is missing or inaccessible");
}

void process_file(
duckdb::Connection &con, const std::string &filename,
const std::string &decryption_key, std::vector<std::string> &utf8_columns,
const std::string &null_value,
const std::function<void(const std::string &view_name)> &process_view) {

validate_file(filename);
auto table = decryption_key.empty()
? read_unencrypted_csv(filename, utf8_columns, null_value)
: read_encrypted_csv(filename, decryption_key, utf8_columns,
Expand Down
39 changes: 32 additions & 7 deletions test/integration/test_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ bool NO_FAIL(const grpc::Status &status) {
return status.ok();
}

bool IS_FAIL(const grpc::Status &status, const std::string &expected_error) {
if (!status.ok() && status.error_message() != expected_error) {
fprintf(stderr, "Query failed with unexpected message: %s\n",
status.error_message().c_str());
bool REQUIRE_FAIL(const grpc::Status &status,
const std::string &expected_error) {
if (!status.ok()) {
REQUIRE(status.error_message() == expected_error);
return true;
}
return !status.ok();
return false;
}

#define REQUIRE_NO_FAIL(result) REQUIRE(NO_FAIL((result)))
#define REQUIRE_FAIL(result, expected_error) \
REQUIRE(IS_FAIL(result, expected_error))

TEST_CASE("ConfigurationForm", "[integration]") {
DestinationSdkImpl service;
Expand Down Expand Up @@ -817,4 +817,29 @@ TEST_CASE("Truncate fails if synced_column is missing") {
auto status = service.Truncate(nullptr, &request, &response);

REQUIRE_FAIL(status, "Synced column is required");
}

TEST_CASE("reading inaccessible or nonexistent files fails") {
DestinationSdkImpl service;

const std::string bad_file_name = TEST_RESOURCES_DIR + "nonexistent.csv";
::fivetran_sdk::WriteBatchRequest request;

auto token = std::getenv("motherduck_token");
REQUIRE(token);
(*request.mutable_configuration())["motherduck_token"] = token;
(*request.mutable_configuration())["motherduck_database"] = "fivetran_test";
request.mutable_csv()->set_encryption(::fivetran_sdk::Encryption::AES);
request.mutable_csv()->set_compression(::fivetran_sdk::Compression::ZSTD);
define_test_table(request, "unused_table");

request.add_replace_files(bad_file_name);
(*request.mutable_keys())[bad_file_name] = "whatever";

::fivetran_sdk::WriteBatchResponse response;
auto status = service.WriteBatch(nullptr, &request, &response);
const auto expected =
"WriteBatch endpoint failed for schema <>, table <unused_table>:File <" +
bad_file_name + "> is missing or inaccessible";
REQUIRE_FAIL(status, expected);
}

0 comments on commit 1113e7c

Please sign in to comment.