Skip to content

Add a workaround to drop the bad table instead of truncating #52

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/sql_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,15 @@ void alter_table_recreate(duckdb::Connection &con, const table_def &table,
"Could not drop table <" + absolute_temp_table_name + ">");
}

// this is only used for a temporary workaround --
// https://github.com/MotherDuck-Open-Source/motherduck-fivetran-connector/issues/51
void drop_table(duckdb::Connection &con, const table_def &table) {
auto absolute_table_name = table.to_escaped_string();

run_query(con, "Dropping table", "DROP TABLE " + absolute_table_name,
"Could not directly drop table <" + absolute_table_name + ">");
}

void alter_table_in_place(
duckdb::Connection &con, const std::string &absolute_table_name,
const std::set<std::string> &added_columns,
Expand Down Expand Up @@ -494,8 +503,19 @@ void truncate_table(duckdb::Connection &con, const table_def &table,
const std::string &deleted_column) {
const std::string absolute_table_name = table.to_escaped_string();
std::ostringstream sql;

mdlog::info("truncate_table request: deleted column = " + deleted_column);

// this is only used for a temporary workaround --
// https://github.com/MotherDuck-Open-Source/motherduck-fivetran-connector/issues/51
auto duckdb_columns = describe_table(con, table);
if (duckdb_columns.size() == 1 &&
duckdb_columns[0].name == "_fivetran_deleted") {
mdlog::info("truncate_table: dropping bad table with only "
"_fivetran_deleted column");
drop_table(con, table);
return;
}

if (deleted_column.empty()) {
// hard delete
sql << "DELETE FROM " << absolute_table_name;
Expand Down
65 changes: 63 additions & 2 deletions test/integration/test_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1522,8 +1522,7 @@ TEST_CASE("AlterTable with constraints", "[integration]") {
}
}

TEST_CASE("Invalid truncate with nonexisting delete column",
"[integration][current]") {
TEST_CASE("Invalid truncate with nonexisting delete column", "[integration]") {
DestinationSdkImpl service;

const std::string table_name =
Expand Down Expand Up @@ -1572,3 +1571,65 @@ TEST_CASE("Invalid truncate with nonexisting delete column",
"Referenced column \"_fivetran_synced\" not found in FROM clause"));
}
}

TEST_CASE("Temp fix to drop table with only _fivetran_deleted column",
"[integration]") {
DestinationSdkImpl service;

const std::string table_name =
"table_with_only_deleted_column_" + std::to_string(Catch::rngSeed());
auto token = std::getenv("motherduck_token");
REQUIRE(token);

{
// Create Table that is missing the _fivetran_deleted column
::fivetran_sdk::CreateTableRequest request;
(*request.mutable_configuration())["motherduck_token"] = token;
(*request.mutable_configuration())["motherduck_database"] =
TEST_DATABASE_NAME;
request.mutable_table()->set_name(table_name);
auto col1 = request.mutable_table()->add_columns();
col1->set_name("_fivetran_deleted");
col1->set_type(::fivetran_sdk::DataType::BOOLEAN);

::fivetran_sdk::CreateTableResponse response;
auto status = service.CreateTable(nullptr, &request, &response);
REQUIRE_NO_FAIL(status);
}

auto con = get_test_connection(token);
const std::string table_exists_query =
"SELECT table_name FROM information_schema.tables WHERE table_catalog='" +
TEST_DATABASE_NAME + "' AND table_schema='main' AND table_name='" +
table_name + "'";
{
// make sure table exists
auto res = con->Query(table_exists_query);
REQUIRE(res->RowCount() == 1);
}

{
// Attempt to truncate the bad table
::fivetran_sdk::TruncateRequest request;
(*request.mutable_configuration())["motherduck_token"] = token;
(*request.mutable_configuration())["motherduck_database"] =
TEST_DATABASE_NAME;
request.set_table_name(table_name);
request.set_synced_column(
"_fivetran_synced"); // does not exist but the code won't get there
request.mutable_soft()->set_deleted_column("_fivetran_deleted");

const auto cutoff_datetime = 1707436800; // 2024-02-09 0:0:0 GMT, trust me
request.mutable_utc_delete_before()->set_seconds(cutoff_datetime);
request.mutable_utc_delete_before()->set_nanos(0);
::fivetran_sdk::TruncateResponse response;
auto status = service.Truncate(nullptr, &request, &response);
REQUIRE_NO_FAIL(status);
}

{
// make sure table no longer exists
auto res = con->Query(table_exists_query);
REQUIRE(res->RowCount() == 0);
}
}
Loading