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

gavinchou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 249f4f5cfd1 [fix](storage) check partition storage policy resource 
before BE update (#65053)
249f4f5cfd1 is described below

commit 249f4f5cfd1fcaea183b41d7993ba6e038b7c65b
Author: Gavin Chou <[email protected]>
AuthorDate: Thu Jul 2 18:06:08 2026 +0800

    [fix](storage) check partition storage policy resource before BE update 
(#65053)
    
    ### What problem does this PR solve?
    
    When `ALTER TABLE ... MODIFY PARTITION ... SET ("storage_policy" = ...)`
    changes an existing partition policy to another policy backed by a
    different storage resource, Doris should reject the operation.
    
    Before this patch, the BE tablet meta update was sent before FE checked
    the old/new policy resource compatibility. The SQL could fail later in
    FE, while BE tablet meta had already persisted the new
    `storage_policy_id`.
    
    ### Check List
    
    - [x] Add a pre-check before updating BE tablet meta.
    - [x] Keep same-resource policy switching allowed.
    - [x] Add regression coverage for `MODIFY PARTITION (*)` cross-resource
    failure.
---
 .../main/java/org/apache/doris/alter/Alter.java    |  57 +++++++++-
 .../alter/ModifyPartitionStoragePolicyTest.java    | 116 +++++++++++++++++++++
 .../modify_partition_add_policy.groovy             |  63 +++++++++++
 3 files changed, 235 insertions(+), 1 deletion(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/alter/Alter.java 
b/fe/fe-core/src/main/java/org/apache/doris/alter/Alter.java
index 1825170b320..9f9eb03ceec 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/Alter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/Alter.java
@@ -94,6 +94,8 @@ import org.apache.doris.persist.ModifyPartitionInfo;
 import org.apache.doris.persist.ModifyTableEngineOperationLog;
 import org.apache.doris.persist.ModifyTablePropertyOperationLog;
 import org.apache.doris.persist.ReplaceTableOperationLog;
+import org.apache.doris.policy.Policy;
+import org.apache.doris.policy.PolicyTypeEnum;
 import org.apache.doris.policy.StoragePolicy;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.ConnectContextUtil;
@@ -647,9 +649,11 @@ public class Alter {
                 ModifyPartitionOp clause = ((ModifyPartitionOp) alterOp);
                 Map<String, String> properties = clause.getProperties();
                 List<String> partitionNames = clause.getPartitionNames();
+                OlapTable olapTable = (OlapTable) tableIf;
+                checkModifyPartitionStoragePolicyResource(
+                        olapTable, partitionNames, properties, 
clause.isTempPartition());
                 ((SchemaChangeHandler) 
schemaChangeHandler).updatePartitionsProperties(
                         db, tableName, partitionNames, properties);
-                OlapTable olapTable = (OlapTable) tableIf;
                 olapTable.writeLockOrDdlException();
                 try {
                     modifyPartitionsProperty(db, olapTable, partitionNames, 
properties, clause.isTempPartition());
@@ -916,6 +920,57 @@ public class Alter {
         }
     }
 
+    private void checkModifyPartitionStoragePolicyResource(OlapTable 
olapTable, List<String> partitionNames,
+            Map<String, String> properties, boolean isTempPartition) throws 
DdlException, AnalysisException {
+        if (!PropertyAnalyzer.hasStoragePolicy(properties)) {
+            return;
+        }
+
+        String newStoragePolicy = 
PropertyAnalyzer.analyzeStoragePolicy(properties);
+        if (Strings.isNullOrEmpty(newStoragePolicy)) {
+            return;
+        }
+        
Env.getCurrentEnv().getPolicyMgr().checkStoragePolicyExist(newStoragePolicy);
+
+        Optional<Policy> newPolicy = Env.getCurrentEnv().getPolicyMgr()
+                .findPolicy(newStoragePolicy, PolicyTypeEnum.STORAGE);
+        if (!newPolicy.isPresent() || !(newPolicy.get() instanceof 
StoragePolicy)) {
+            return;
+        }
+
+        olapTable.readLock();
+        try {
+            PartitionInfo partitionInfo = olapTable.getPartitionInfo();
+            for (String partitionName : partitionNames) {
+                Partition partition = olapTable.getPartition(partitionName, 
isTempPartition);
+                if (partition == null) {
+                    continue;
+                }
+
+                DataProperty dataProperty = 
partitionInfo.getDataProperty(partition.getId());
+                String oldStoragePolicy = dataProperty.getStoragePolicy();
+                if (Strings.isNullOrEmpty(oldStoragePolicy) || 
oldStoragePolicy.equals(newStoragePolicy)) {
+                    continue;
+                }
+
+                Optional<Policy> oldPolicy = Env.getCurrentEnv().getPolicyMgr()
+                        .findPolicy(oldStoragePolicy, PolicyTypeEnum.STORAGE);
+                if (!oldPolicy.isPresent() || !(oldPolicy.get() instanceof 
StoragePolicy)) {
+                    continue;
+                }
+
+                String newResource = ((StoragePolicy) 
newPolicy.get()).getStorageResource();
+                String oldResource = ((StoragePolicy) 
oldPolicy.get()).getStorageResource();
+                if (!newResource.equals(oldResource)) {
+                    throw new AnalysisException("currently do not support 
change origin "
+                            + "storage policy to another one with different 
resource: ");
+                }
+            }
+        } finally {
+            olapTable.readUnlock();
+        }
+    }
+
     /**
      * Batch update partitions' properties
      * caller should hold the table lock
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/alter/ModifyPartitionStoragePolicyTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/alter/ModifyPartitionStoragePolicyTest.java
new file mode 100644
index 00000000000..37d7fadd99e
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/alter/ModifyPartitionStoragePolicyTest.java
@@ -0,0 +1,116 @@
+// 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.
+
+package org.apache.doris.alter;
+
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.Config;
+import org.apache.doris.common.ExceptionChecker;
+import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.jmockit.Deencapsulation;
+import org.apache.doris.utframe.TestWithFeService;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+public class ModifyPartitionStoragePolicyTest extends TestWithFeService {
+
+    @Override
+    protected void beforeCluster() {
+        Config.enable_storage_policy = true;
+        FeConstants.runningUnitTest = true;
+    }
+
+    @Override
+    protected void runBeforeAll() throws Exception {
+        createDatabase("test_cir_19357");
+        useDatabase("test_cir_19357");
+
+        executeSql("CREATE RESOURCE IF NOT EXISTS \"cir_19357_resource_a\" "
+                + "PROPERTIES("
+                + "\"type\"=\"s3\","
+                + "\"AWS_REGION\"=\"bj\","
+                + "\"AWS_ENDPOINT\"=\"bj.s3.comaaaa\","
+                + "\"AWS_ROOT_PATH\"=\"path/to/rootaaaa\","
+                + "\"AWS_SECRET_KEY\"=\"aaaa\","
+                + "\"AWS_ACCESS_KEY\"=\"bbba\","
+                + "\"AWS_BUCKET\"=\"test-bucket-a\","
+                + "\"s3_validity_check\"=\"false\")");
+        executeSql("CREATE RESOURCE IF NOT EXISTS \"cir_19357_resource_b\" "
+                + "PROPERTIES("
+                + "\"type\"=\"s3\","
+                + "\"AWS_REGION\"=\"bj\","
+                + "\"AWS_ENDPOINT\"=\"bj.s3.comaaaa\","
+                + "\"AWS_ROOT_PATH\"=\"path/to/rootaaaa\","
+                + "\"AWS_SECRET_KEY\"=\"aaaa\","
+                + "\"AWS_ACCESS_KEY\"=\"bbba\","
+                + "\"AWS_BUCKET\"=\"test-bucket-b\","
+                + "\"s3_validity_check\"=\"false\")");
+        executeSql("CREATE STORAGE POLICY IF NOT EXISTS cir_19357_policy_a "
+                + "PROPERTIES("
+                + "\"storage_resource\"=\"cir_19357_resource_a\","
+                + "\"cooldown_datetime\"=\"2999-01-01 00:00:00\")");
+        executeSql("CREATE STORAGE POLICY IF NOT EXISTS cir_19357_policy_b "
+                + "PROPERTIES("
+                + "\"storage_resource\"=\"cir_19357_resource_b\","
+                + "\"cooldown_datetime\"=\"2999-01-01 00:00:00\")");
+
+        createTable("CREATE TABLE tbl_with_policy (k1 int, k2 int) "
+                + "PARTITION BY RANGE(k1) ("
+                + "PARTITION p1 VALUES LESS THAN (\"10\"),"
+                + "PARTITION p2 VALUES LESS THAN (\"20\")) "
+                + "DISTRIBUTED BY HASH(k1) BUCKETS 1 "
+                + "PROPERTIES(\"replication_num\"=\"1\", 
\"storage_policy\"=\"cir_19357_policy_a\")");
+        createTable("CREATE TABLE tbl_without_policy (k1 int, k2 int) "
+                + "PARTITION BY RANGE(k1) ("
+                + "PARTITION p1 VALUES LESS THAN (\"10\"),"
+                + "PARTITION p2 VALUES LESS THAN (\"20\")) "
+                + "DISTRIBUTED BY HASH(k1) BUCKETS 1 "
+                + "PROPERTIES(\"replication_num\"=\"1\")");
+    }
+
+    @Test
+    public void testRejectDifferentResourcePolicyBeforeBeUpdate() throws 
Exception {
+        assertModifyPartitionRejectedBeforeBeUpdate(
+                "ALTER TABLE tbl_with_policy MODIFY PARTITION (*) "
+                        + "SET (\"storage_policy\"=\"cir_19357_policy_b\")",
+                "currently do not support change origin storage policy to 
another one with different resource");
+    }
+
+    @Test
+    public void testRejectUninitializedDefaultPolicyBeforeBeUpdate() throws 
Exception {
+        assertModifyPartitionRejectedBeforeBeUpdate(
+                "ALTER TABLE tbl_without_policy MODIFY PARTITION (*) "
+                        + "SET 
(\"storage_policy\"=\"default_storage_policy\")",
+                "Use default storage policy, but not give s3 info");
+    }
+
+    private void assertModifyPartitionRejectedBeforeBeUpdate(String sql, 
String errorMessage) throws Exception {
+        Alter alter = Env.getCurrentEnv().getAlterInstance();
+        SchemaChangeHandler originHandler = (SchemaChangeHandler) 
alter.getSchemaChangeHandler();
+        SchemaChangeHandler spyHandler = Mockito.spy(originHandler);
+        Deencapsulation.setField(alter, "schemaChangeHandler", spyHandler);
+        try {
+            ExceptionChecker.expectThrowsWithMsg(IllegalStateException.class, 
errorMessage,
+                    () -> executeSql(sql));
+            Mockito.verify(spyHandler, 
Mockito.never()).updatePartitionsProperties(
+                    Mockito.any(), Mockito.anyString(), Mockito.anyList(), 
Mockito.anyMap());
+        } finally {
+            Deencapsulation.setField(alter, "schemaChangeHandler", 
originHandler);
+        }
+    }
+}
diff --git 
a/regression-test/suites/cold_heat_separation/empty_table_use_policy/modify_partition_add_policy.groovy
 
b/regression-test/suites/cold_heat_separation/empty_table_use_policy/modify_partition_add_policy.groovy
index 9608c296836..bff574b0473 100644
--- 
a/regression-test/suites/cold_heat_separation/empty_table_use_policy/modify_partition_add_policy.groovy
+++ 
b/regression-test/suites/cold_heat_separation/empty_table_use_policy/modify_partition_add_policy.groovy
@@ -112,6 +112,63 @@ suite("add_table_policy_by_modify_partition") {
         "cooldown_datetime" = "$cooldownTime"
         );
     """
+    sql """ALTER STORAGE POLICY tmp2 PROPERTIES("cooldown_datetime" = 
"$cooldownTime")"""
+
+    try_sql """
+        CREATE RESOURCE IF NOT EXISTS 
"test_modify_partition_table_use_resource_diff"
+        PROPERTIES(
+            "type"="s3",
+            "AWS_REGION" = "bj",
+            "AWS_ENDPOINT" = "bj.s3.comaaaa",
+            "AWS_ROOT_PATH" = "path/to/rootaaaa",
+            "AWS_SECRET_KEY" = "aaaa",
+            "AWS_ACCESS_KEY" = "bbba",
+            "AWS_BUCKET" = "test-bucket-diff",
+            "s3_validity_check" = "false"
+        );
+    """
+    try_sql """
+        CREATE STORAGE POLICY IF NOT EXISTS 
created_create_table_partition_alter_policy_diff
+        PROPERTIES(
+        "storage_resource" = "test_modify_partition_table_use_resource_diff",
+        "cooldown_datetime" = "$cooldownTime"
+        );
+    """
+    sql """ALTER STORAGE POLICY 
created_create_table_partition_alter_policy_diff PROPERTIES("cooldown_datetime" 
= "$cooldownTime")"""
+    
assertEquals(storage_exist.call("created_create_table_partition_alter_policy_diff"),
 true)
+
+    def partitionsAfterSetPolicy = sql_return_maparray """
+    show partitions from create_table_partition
+    """
+    for (def par in partitionsAfterSetPolicy) {
+        assertTrue(par.RemoteStoragePolicy == 
"created_create_table_partition_alter_policy")
+    }
+
+    test {
+        sql """
+        ALTER TABLE create_table_partition MODIFY PARTITION (*) 
SET("storage_policy"="created_create_table_partition_alter_policy_diff");
+        """
+        exception "currently do not support change origin storage policy to 
another one with different resource"
+    }
+
+    def partitionsAfterFailedAlter = sql_return_maparray """
+    show partitions from create_table_partition
+    """
+    for (def par in partitionsAfterFailedAlter) {
+        assertTrue(par.RemoteStoragePolicy == 
"created_create_table_partition_alter_policy")
+    }
+
+    def alter_table_partition_same_resource_result = try_sql """
+        ALTER TABLE create_table_partition MODIFY PARTITION (*) 
SET("storage_policy"="tmp2");
+    """
+    assertEquals(alter_table_partition_same_resource_result.size(), 1);
+
+    def partitionsAfterSameResourceAlter = sql_return_maparray """
+    show partitions from create_table_partition
+    """
+    for (def par in partitionsAfterSameResourceAlter) {
+        assertTrue(par.RemoteStoragePolicy == "tmp2")
+    }
 
     sql """
     CREATE TABLE create_table_partion_use_created_policy_test
@@ -148,9 +205,15 @@ suite("add_table_policy_by_modify_partition") {
     DROP STORAGE POLICY created_create_table_partition_alter_policy
     """
     sql """
+    DROP STORAGE POLICY created_create_table_partition_alter_policy_diff
+    """
+    sql """
     DROP STORAGE POLICY tmp2
     """
     sql """
     DROP RESOURCE test_modify_partition_table_use_resource
     """
+    sql """
+    DROP RESOURCE test_modify_partition_table_use_resource_diff
+    """
 }


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

Reply via email to