diff --git a/src/sql_generator.cpp b/src/sql_generator.cpp index 2d1ca9e..28540d7 100644 --- a/src/sql_generator.cpp +++ b/src/sql_generator.cpp @@ -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 &added_columns, @@ -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; diff --git a/test/integration/test_server.cpp b/test/integration/test_server.cpp index 8051670..94bcca6 100644 --- a/test/integration/test_server.cpp +++ b/test/integration/test_server.cpp @@ -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 = @@ -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); + } +} \ No newline at end of file