This is an automated email from the ASF dual-hosted git repository.

morrysnow pushed a commit to branch branch-3.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-3.1 by this push:
     new 44119ab3044 branch-3.1: [fix](export) cancel job before adding task 
#57488 (#57562)
44119ab3044 is described below

commit 44119ab30443ee58814759e284d38557af8424b3
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Mon Nov 3 11:21:46 2025 +0800

    branch-3.1: [fix](export) cancel job before adding task #57488 (#57562)
    
    bp #57488
---
 .../org/apache/doris/analysis/OutFileClause.java   |  8 ++-
 .../main/java/org/apache/doris/load/ExportMgr.java | 18 +++---
 .../trees/plans/commands/ExportCommand.java        | 22 ++++++-
 .../export_p0/test_export_delete_disallow.groovy   | 75 ++++++++++++++++++++++
 4 files changed, 108 insertions(+), 15 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/OutFileClause.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/OutFileClause.java
index d38bf28e055..ec2544f320c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/OutFileClause.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/OutFileClause.java
@@ -526,8 +526,12 @@ public class OutFileClause {
         }
 
         if (copiedProps.containsKey(PROP_DELETE_EXISTING_FILES)) {
-            deleteExistingFiles = 
Boolean.parseBoolean(copiedProps.get(PROP_DELETE_EXISTING_FILES))
-                    & Config.enable_delete_existing_files;
+            deleteExistingFiles = 
Boolean.parseBoolean(copiedProps.get(PROP_DELETE_EXISTING_FILES));
+            if (deleteExistingFiles && !Config.enable_delete_existing_files) {
+                throw new AnalysisException("Deleting existing files is not 
allowed."
+                        + " To enable this feature, you need to add 
`enable_delete_existing_files=true`"
+                        + " in fe.conf");
+            }
             copiedProps.remove(PROP_DELETE_EXISTING_FILES);
         }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java 
b/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java
index 89baa252d46..d9d0f75ce68 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/load/ExportMgr.java
@@ -111,17 +111,14 @@ public class ExportMgr {
         } finally {
             writeUnlock();
         }
-        // delete existing files
-        if (Config.enable_delete_existing_files && 
Boolean.parseBoolean(job.getDeleteExistingFiles())) {
-            if (job.getBrokerDesc() == null) {
-                throw new AnalysisException("Local file system does not 
support delete existing files");
-            }
-            String fullPath = job.getExportPath();
-            BrokerUtil.deleteDirectoryWithFileSystem(fullPath.substring(0, 
fullPath.lastIndexOf('/') + 1),
-                    job.getBrokerDesc());
-        }
-        // ATTN: Must add task after edit log, otherwise the job may finish 
before adding job.
         try {
+            // delete existing files
+            if (Boolean.parseBoolean(job.getDeleteExistingFiles())) {
+                String fullPath = job.getExportPath();
+                BrokerUtil.deleteDirectoryWithFileSystem(fullPath.substring(0, 
fullPath.lastIndexOf('/') + 1),
+                        job.getBrokerDesc());
+            }
+            // ATTN: Must add task after edit log, otherwise the job may 
finish before adding job.
             for (int i = 0; i < job.getCopiedTaskExecutors().size(); i++) {
                 
Env.getCurrentEnv().getTransientTaskManager().addMemoryTask(job.getCopiedTaskExecutors().get(i));
             }
@@ -546,3 +543,4 @@ public class ExportMgr {
         return size;
     }
 }
+
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExportCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExportCommand.java
index a19160379ec..35ddf3d86a2 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExportCommand.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExportCommand.java
@@ -23,6 +23,7 @@ import org.apache.doris.analysis.OutFileClause;
 import org.apache.doris.analysis.Separator;
 import org.apache.doris.analysis.StmtType;
 import org.apache.doris.analysis.StorageBackend;
+import org.apache.doris.analysis.StorageBackend.StorageType;
 import org.apache.doris.analysis.TableName;
 import org.apache.doris.catalog.BrokerMgr;
 import org.apache.doris.catalog.DatabaseIf;
@@ -305,9 +306,6 @@ public class ExportCommand extends Command implements 
ForwardWithSync {
 
         // set max_file_size
         
exportJob.setMaxFileSize(fileProperties.getOrDefault(OutFileClause.PROP_MAX_FILE_SIZE,
 ""));
-        // set delete_existing_files
-        exportJob.setDeleteExistingFiles(fileProperties.getOrDefault(
-                OutFileClause.PROP_DELETE_EXISTING_FILES, ""));
 
         // null means not specified
         // "" means user specified zero columns
@@ -322,6 +320,23 @@ public class ExportCommand extends Command implements 
ForwardWithSync {
         // set broker desc
         exportJob.setBrokerDesc(this.brokerDesc.get());
 
+        // set delete_existing_files
+        exportJob.setDeleteExistingFiles(fileProperties.getOrDefault(
+                OutFileClause.PROP_DELETE_EXISTING_FILES, ""));
+
+        if (!Config.enable_delete_existing_files && 
exportJob.getDeleteExistingFiles().equalsIgnoreCase("true")) {
+            throw new AnalysisException(("Deleting existing files is not 
allowed."
+                    + " To enable this feature, you need to add 
`enable_delete_existing_files=true`"
+                    + " in fe.conf"));
+        }
+
+        if (exportJob.getDeleteExistingFiles().equalsIgnoreCase("true")
+                && (exportJob.getBrokerDesc() == null
+                || exportJob.getBrokerDesc().storageType() == 
StorageType.LOCAL)) {
+            throw new org.apache.doris.common.AnalysisException(
+                    "Local file system does not support delete existing 
files");
+        }
+
         // set sessions
         exportJob.setQualifiedUser(ctx.getQualifiedUser());
         exportJob.setUserIdentity(ctx.getCurrentUserIdentity());
@@ -391,3 +406,4 @@ public class ExportCommand extends Command implements 
ForwardWithSync {
         return StmtType.EXPORT;
     }
 }
+
diff --git 
a/regression-test/suites/export_p0/test_export_delete_disallow.groovy 
b/regression-test/suites/export_p0/test_export_delete_disallow.groovy
new file mode 100644
index 00000000000..f5f508afb52
--- /dev/null
+++ b/regression-test/suites/export_p0/test_export_delete_disallow.groovy
@@ -0,0 +1,75 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import org.codehaus.groovy.runtime.IOGroovyMethods
+
+import java.nio.charset.StandardCharsets
+import java.nio.file.Files
+import java.nio.file.Paths
+
+suite("test_export_delete_disallow", "p0") {
+    def db = "regression_test_export_p0"
+    
+    // check whether the FE config 'enable_outfile_to_local' is true
+    StringBuilder strBuilder = new StringBuilder()
+    strBuilder.append("curl --location-trusted -u " + context.config.jdbcUser 
+ ":" + context.config.jdbcPassword)
+    strBuilder.append(" http://"; + context.config.feHttpAddress + 
"/rest/v1/config/fe")
+
+    String command = strBuilder.toString()
+    def process = command.toString().execute()
+    def code = process.waitFor()
+    def err = IOGroovyMethods.getText(new BufferedReader(new 
InputStreamReader(process.getErrorStream())));
+    def out = process.getText()
+    logger.info("Request FE Config: code=" + code + ", out=" + out + ", err=" 
+ err)
+    assertEquals(code, 0)
+    def response = parseJson(out.trim())
+    assertEquals(response.code, 0)
+    assertEquals(response.msg, "success")
+    def configJson = response.data.rows
+    boolean enableOutfileToLocal = false
+    for (Object conf: configJson) {
+        assert conf instanceof Map
+        if (((Map<String, String>) conf).get("Name").toLowerCase() == 
"enable_outfile_to_local") {
+            enableOutfileToLocal = ((Map<String, String>) 
conf).get("Value").toLowerCase() == "true"
+        }
+    }
+    if (!enableOutfileToLocal) {
+        logger.warn("Please set enable_outfile_to_local to true to run 
test_outfile")
+        return
+    }
+    
+    // create table and insert
+    sql """ DROP TABLE IF EXISTS test_export_delete_disallow_tbl"""
+    sql """
+    CREATE TABLE IF NOT EXISTS test_export_delete_disallow_tbl (
+        `id` int(11) NULL,
+        `Name` string NULL
+        )
+        PROPERTIES("replication_num" = "1");
+    """
+    sql """insert into test_export_delete_disallow_tbl values(1, "abc");"""
+    
+    test {
+         sql """select * from test_export_delete_disallow_tbl limit 10 INTO 
OUTFILE "file:///tmp/" properties("delete_existing_files" = "true");"""
+         exception """Deleting existing files is not allowed"""
+    }
+
+    test {
+        sql """EXPORT TABLE test_export_delete_disallow_tbl TO "file:///tmp" 
PROPERTIES ("delete_existing_files" = "true");"""
+        exception """Deleting existing files is not allowe"""
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to