diff --git a/src/binder/bind/bind_export_database.cpp b/src/binder/bind/bind_export_database.cpp index b8bd885385..052c3e5888 100644 --- a/src/binder/bind/bind_export_database.cpp +++ b/src/binder/bind/bind_export_database.cpp @@ -3,7 +3,6 @@ #include "catalog/catalog_entry/node_table_catalog_entry.h" #include "catalog/catalog_entry/rel_table_catalog_entry.h" #include "common/exception/binder.h" -#include "common/file_system/virtual_file_system.h" #include "common/string_utils.h" #include "main/client_context.h" #include "parser/parser.h" @@ -93,12 +92,6 @@ std::unique_ptr Binder::bindExportDatabaseClause(const Statement if (fileType != FileType::CSV && parsedOptions.size() != 0) { throw BinderException{"Only export to csv can have options."}; } - auto fs = clientContext->getVFSUnsafe(); - if (!fs->fileOrPathExists(boundFilePath)) { - fs->createDir(boundFilePath); - } else { - throw BinderException(stringFormat("Directory {} already exists.", boundFilePath)); - } return std::make_unique( boundFilePath, fileType, std::move(exportData), std::move(parsedOptions)); } diff --git a/src/processor/map/map_port_db.cpp b/src/processor/map/map_port_db.cpp index 83909ec25a..9a9aec8f8e 100644 --- a/src/processor/map/map_port_db.cpp +++ b/src/processor/map/map_port_db.cpp @@ -1,3 +1,4 @@ +#include "common/file_system/virtual_file_system.h" #include "planner/operator/persistent/logical_export_db.h" #include "planner/operator/persistent/logical_import_db.h" #include "processor/operator/persistent/export_db.h" @@ -20,6 +21,17 @@ std::unique_ptr PlanMapper::mapExportDatabase( auto childPhysicalOperator = mapOperator(childCopyTo.get()); children.push_back(std::move(childPhysicalOperator)); } + // Ideally we should create directory inside operator but ExportDatabase is executed before + // CopyTo which requires the directory to exist. So we create directory in mapper inside. + auto fs = clientContext->getVFSUnsafe(); + auto boundFileInfo = exportDatabase->getBoundFileInfo(); + KU_ASSERT(boundFileInfo->filePaths.size() == 1); + auto filePath = boundFileInfo->filePaths[0]; + if (!fs->fileOrPathExists(filePath)) { + fs->createDir(filePath); + } else { + throw RuntimeException(stringFormat("Directory {} already exists.", filePath)); + } std::unique_ptr resultSetDescriptor; return std::make_unique(exportDatabase->getBoundFileInfo()->copy(), getOperatorID(), exportDatabase->getExpressionsForPrinting(), std::move(children)); diff --git a/test/main/prepare_test.cpp b/test/main/prepare_test.cpp index 3aa3902d45..5a38ddafb5 100644 --- a/test/main/prepare_test.cpp +++ b/test/main/prepare_test.cpp @@ -234,3 +234,10 @@ TEST_F(ApiTest, issueTest4) { checkTuple(result->getNext().get(), "-123456789\n"); ASSERT_FALSE(result->hasNext()); } + +TEST_F(ApiTest, PrepareExport) { + auto newDBPath = databasePath + "/newdb"; + auto preparedStatement = conn->prepare("EXPORT DATABASE '" + newDBPath + '\''); + auto result = conn->execute(preparedStatement.get()); + ASSERT_TRUE(result->isSuccess()); +} diff --git a/test/test_files/copy/export_import_db.test b/test/test_files/copy/export_import_db.test index 23b2d1275c..86a97d9aa7 100644 --- a/test/test_files/copy/export_import_db.test +++ b/test/test_files/copy/export_import_db.test @@ -41,7 +41,7 @@ 50 -STATEMENT Export Database "${KUZU_EXPORT_DB_DIRECTORY}_case2/demo-db2" (format='csv', header=true) ---- error -Binder exception: Directory ${KUZU_EXPORT_DB_DIRECTORY}_case2/demo-db2 already exists. +Runtime exception: Directory ${KUZU_EXPORT_DB_DIRECTORY}_case2/demo-db2 already exists. -CASE ExportImportDatabaseWithPARQUET -STATEMENT Export Database "${KUZU_EXPORT_DB_DIRECTORY}_case4/demo-db3" (format='parquet')