#include "capi_tester.hpp" using namespace duckdb; using namespace std; static void test_file_system(duckdb_file_system fs, string file_name) { REQUIRE(fs != nullptr); duckdb_state state = DuckDBSuccess; duckdb_file_handle file; auto file_path = TestDirectoryPath() + "/" + file_name; auto options = duckdb_create_file_open_options(); state = duckdb_file_open_options_set_flag(options, DUCKDB_FILE_FLAG_WRITE, true); REQUIRE(state == DuckDBSuccess); state = duckdb_file_open_options_set_flag(options, DUCKDB_FILE_FLAG_READ, true); REQUIRE(state == DuckDBSuccess); // Try to open non-existing file without create flag state = duckdb_file_system_open(fs, file_path.c_str(), options, &file); REQUIRE(state != DuckDBSuccess); auto error_data = duckdb_file_system_error_data(fs); auto error_type = duckdb_error_data_error_type(error_data); REQUIRE(error_type == DUCKDB_ERROR_IO); duckdb_destroy_error_data(&error_data); // Try to write to a null file handle auto failed_bytes_written = duckdb_file_handle_write(file, "data", 4); REQUIRE(failed_bytes_written == -1); auto file_error_data = duckdb_file_handle_error_data(file); auto has_error = duckdb_error_data_has_error(file_error_data); REQUIRE(has_error == false); duckdb_destroy_error_data(&file_error_data); // Set create flag state = duckdb_file_open_options_set_flag(options, DUCKDB_FILE_FLAG_CREATE, true); REQUIRE(state == DuckDBSuccess); // Create and open a file state = duckdb_file_system_open(fs, file_path.c_str(), options, &file); REQUIRE(state == DuckDBSuccess); REQUIRE(file != nullptr); // Write to the file const char *data = "Hello, DuckDB File System!"; auto bytes_written = duckdb_file_handle_write(file, data, strlen(data)); REQUIRE(bytes_written == (int64_t)strlen(data)); auto position = duckdb_file_handle_tell(file); REQUIRE(position == bytes_written); auto size = duckdb_file_handle_size(file); REQUIRE(size == bytes_written); // Sync state = duckdb_file_handle_sync(file); // Seek to the beginning state = duckdb_file_handle_seek(file, 0); REQUIRE(state == DuckDBSuccess); position = duckdb_file_handle_tell(file); REQUIRE(position == 0); // Read from the file char buffer[30]; memset(buffer, 0, sizeof(buffer)); auto bytes_read = duckdb_file_handle_read(file, buffer, sizeof(buffer) - 1); REQUIRE(bytes_read == bytes_written); REQUIRE(strcmp(buffer, data) == 0); position = duckdb_file_handle_tell(file); REQUIRE(position == bytes_read); size = duckdb_file_handle_size(file); REQUIRE(size == bytes_written); // Seek to the end state = duckdb_file_handle_seek(file, bytes_written); REQUIRE(state == DuckDBSuccess); position = duckdb_file_handle_tell(file); REQUIRE(position == bytes_written); size = duckdb_file_handle_size(file); REQUIRE(size == bytes_written); // Try to read from the end of the file memset(buffer, 0, sizeof(buffer)); bytes_read = duckdb_file_handle_read(file, buffer, sizeof(buffer) - 1); REQUIRE(bytes_read == 0); // EOF position = duckdb_file_handle_tell(file); REQUIRE(position == bytes_written); size = duckdb_file_handle_size(file); REQUIRE(size == bytes_written); // Seek back to the beginning state = duckdb_file_handle_seek(file, 0); REQUIRE(state == DuckDBSuccess); position = duckdb_file_handle_tell(file); REQUIRE(position == 0); size = duckdb_file_handle_size(file); REQUIRE(size == bytes_written); // Close the file duckdb_file_handle_close(file); duckdb_destroy_file_handle(&file); // Open file again for reading state = duckdb_file_system_open(fs, file_path.c_str(), options, &file); REQUIRE(state == DuckDBSuccess); REQUIRE(file != nullptr); size = duckdb_file_handle_size(file); REQUIRE(size == bytes_written); // Check that the data is still there memset(buffer, 0, sizeof(buffer)); bytes_read = duckdb_file_handle_read(file, buffer, sizeof(buffer) - 1); REQUIRE(bytes_read == bytes_written); REQUIRE(strcmp(buffer, data) == 0); position = duckdb_file_handle_tell(file); REQUIRE(position == bytes_read); size = duckdb_file_handle_size(file); REQUIRE(size == bytes_written); // Close the file again duckdb_file_handle_close(file); duckdb_destroy_file_handle(&file); duckdb_destroy_file_open_options(&options); REQUIRE(file == nullptr); REQUIRE(options == nullptr); // Try destroy again for good measure duckdb_destroy_file_handle(&file); duckdb_destroy_file_open_options(&options); REQUIRE(file == nullptr); REQUIRE(options == nullptr); } TEST_CASE("Test File System in C API", "[capi]") { CAPITester tester; duckdb_client_context context; duckdb_file_system fs; duckdb::unique_ptr result; REQUIRE(tester.OpenDatabase(nullptr)); // get a file system from the client context duckdb_connection_get_client_context(tester.connection, &context); fs = duckdb_client_context_get_file_system(context); REQUIRE(fs != nullptr); test_file_system(fs, "test_file_capi_1.txt"); duckdb_destroy_file_system(&fs); REQUIRE(fs == nullptr); duckdb_destroy_client_context(&context); // Try to destory fs again duckdb_destroy_file_system(&fs); REQUIRE(fs == nullptr); }