You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2020/07/09 13:48:57 UTC

[incubator-doris] branch master updated: [Feature] Batch update partition's property in one command (#3981)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new d2ab38a  [Feature] Batch update partition's property in one command (#3981)
d2ab38a is described below

commit d2ab38a5e00d95949a349c213c33a8cf78796b89
Author: xy720 <22...@users.noreply.github.com>
AuthorDate: Thu Jul 9 21:48:43 2020 +0800

    [Feature] Batch update partition's property in one command (#3981)
    
    Support following command.
    ```
    alter table tbl_name modify partition (p1, p2, p3) set ("replication_num" = "3");
    ```
---
 .../sql-statements/Data Definition/ALTER TABLE.md  |  14 +-
 .../sql-statements/Data Definition/ALTER TABLE.md  |  14 +-
 fe/src/main/cup/sql_parser.cup                     |  12 +-
 fe/src/main/java/org/apache/doris/alter/Alter.java | 229 ++++++++++++++++++++-
 .../apache/doris/alter/SchemaChangeHandler.java    |  45 ++++
 .../doris/analysis/ModifyPartitionClause.java      |  84 +++++++-
 .../java/org/apache/doris/catalog/Catalog.java     | 118 -----------
 .../org/apache/doris/catalog/DataProperty.java     |   3 +
 .../org/apache/doris/journal/JournalEntity.java    |   6 +
 .../doris/journal/local/LocalJournalCursor.java    |   6 +
 .../doris/persist/BatchModifyPartitionsInfo.java   |  74 +++++++
 .../java/org/apache/doris/persist/EditLog.java     |  13 +-
 .../apache/doris/persist/ModifyPartitionInfo.java  |  21 ++
 .../org/apache/doris/persist/OperationType.java    |   1 +
 .../java/org/apache/doris/alter/AlterTest.java     |  88 ++++++++
 .../persist/BatchModifyPartitionsInfoTest.java     |  61 ++++++
 16 files changed, 646 insertions(+), 143 deletions(-)

diff --git a/docs/en/sql-reference/sql-statements/Data Definition/ALTER TABLE.md b/docs/en/sql-reference/sql-statements/Data Definition/ALTER TABLE.md
index d7c3b54..c57f3ee 100644
--- a/docs/en/sql-reference/sql-statements/Data Definition/ALTER TABLE.md	
+++ b/docs/en/sql-reference/sql-statements/Data Definition/ALTER TABLE.md	
@@ -64,7 +64,7 @@ under the License.
             
     3. Modify the partition properties
         grammar:
-            MODIFY PARTITION partition_name SET ("key" = "value", ...)
+            MODIFY PARTITION p1|(p1[, p2, ...]) SET ("key" = "value", ...)
         Description:
             1) The following attributes of the modified partition are currently supported.
                 - storage_medium
@@ -233,12 +233,20 @@ under the License.
     4. Modify the number of partition copies
         ALTER TABLE example_db.my_table
         MODIFY PARTITION p1 SET("replication_num"="1");
+        
+    5. Batch modify the specified partitions
+        ALTER TABLE example_db.my_table
+        MODIFY PARTITION (p1, p2, p4) SET("in_memory"="true");
+        
+    6. Batch modify all partitions
+        ALTER TABLE example_db.my_table
+        MODIFY PARTITION (*) SET("storage_medium"="HDD");
 
-    5. Delete the partition
+    7. Delete the partition
         ALTER TABLE example_db.my_table
         DROP PARTITION p1;
         
-    6. Add a partition that specifies the upper and lower bounds
+    8. Add a partition that specifies the upper and lower bounds
 
         ALTER TABLE example_db.my_table
         ADD PARTITION p1 VALUES [("2014-01-01"), ("2014-02-01"));
diff --git a/docs/zh-CN/sql-reference/sql-statements/Data Definition/ALTER TABLE.md b/docs/zh-CN/sql-reference/sql-statements/Data Definition/ALTER TABLE.md
index 552ab28..4f5f9e6 100644
--- a/docs/zh-CN/sql-reference/sql-statements/Data Definition/ALTER TABLE.md	
+++ b/docs/zh-CN/sql-reference/sql-statements/Data Definition/ALTER TABLE.md	
@@ -64,7 +64,7 @@ under the License.
             
     3. 修改分区属性
         语法:
-            MODIFY PARTITION partition_name SET ("key" = "value", ...)
+            MODIFY PARTITION p1|(p1[, p2, ...]) SET ("key" = "value", ...)
         说明:
             1) 当前支持修改分区的下列属性:
                 - storage_medium
@@ -230,12 +230,20 @@ under the License.
     4. 修改分区副本数
         ALTER TABLE example_db.my_table
         MODIFY PARTITION p1 SET("replication_num"="1");
+        
+    5. 批量修改指定分区
+        ALTER TABLE example_db.my_table
+        MODIFY PARTITION (p1, p2, p4) SET("in_memory"="true");
+        
+    6. 批量修改所有分区
+        ALTER TABLE example_db.my_table
+        MODIFY PARTITION (*) SET("storage_medium"="HDD");
 
-    5. 删除分区
+    7. 删除分区
         ALTER TABLE example_db.my_table
         DROP PARTITION p1;
         
-    6. 增加一个指定上下界的分区
+    8. 增加一个指定上下界的分区
 
         ALTER TABLE example_db.my_table
         ADD PARTITION p1 VALUES [("2014-01-01"), ("2014-02-01"));
diff --git a/fe/src/main/cup/sql_parser.cup b/fe/src/main/cup/sql_parser.cup
index 5589fbd..6f40dfe 100644
--- a/fe/src/main/cup/sql_parser.cup
+++ b/fe/src/main/cup/sql_parser.cup
@@ -883,7 +883,17 @@ alter_table_clause ::=
     :}
     | KW_MODIFY KW_PARTITION ident:partitionName KW_SET LPAREN key_value_map:properties RPAREN
     {:
-        RESULT = new ModifyPartitionClause(partitionName, properties);
+        ArrayList<String> partitions = new ArrayList<String>();
+        partitions.add(partitionName);
+        RESULT = new ModifyPartitionClause(partitions, properties);
+    :}
+    | KW_MODIFY KW_PARTITION LPAREN ident_list:partitions RPAREN KW_SET LPAREN key_value_map:properties RPAREN
+    {:
+        RESULT = new ModifyPartitionClause(partitions, properties);
+    :}
+    | KW_MODIFY KW_PARTITION LPAREN STAR RPAREN KW_SET LPAREN key_value_map:properties RPAREN
+    {:
+        RESULT = ModifyPartitionClause.createStarClause(properties);
     :}
     | KW_REPLACE opt_partition_names:partitions KW_WITH opt_partition_names:tempPartitions opt_properties:properties
     {:
diff --git a/fe/src/main/java/org/apache/doris/alter/Alter.java b/fe/src/main/java/org/apache/doris/alter/Alter.java
index 017d4b1..2bda6b1 100644
--- a/fe/src/main/java/org/apache/doris/alter/Alter.java
+++ b/fe/src/main/java/org/apache/doris/alter/Alter.java
@@ -35,9 +35,12 @@ import org.apache.doris.analysis.TableName;
 import org.apache.doris.analysis.TableRenameClause;
 import org.apache.doris.catalog.Catalog;
 import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.DataProperty;
 import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.OlapTable.OlapTableState;
+import org.apache.doris.catalog.Partition;
+import org.apache.doris.catalog.PartitionInfo;
 import org.apache.doris.catalog.Table;
 import org.apache.doris.catalog.Table.TableType;
 import org.apache.doris.catalog.View;
@@ -50,9 +53,13 @@ import org.apache.doris.common.UserException;
 import org.apache.doris.common.util.DynamicPartitionUtil;
 import org.apache.doris.common.util.PropertyAnalyzer;
 import org.apache.doris.persist.AlterViewInfo;
+import org.apache.doris.persist.BatchModifyPartitionsInfo;
+import org.apache.doris.persist.ModifyPartitionInfo;
 import org.apache.doris.qe.ConnectContext;
-
+import org.apache.doris.thrift.TTabletType;
+import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -203,12 +210,20 @@ public class Alter {
                     Catalog.getCurrentCatalog().replaceTempPartition(db, tableName, (ReplacePartitionClause) alterClause);
                 } else if (alterClause instanceof ModifyPartitionClause) {
                     ModifyPartitionClause clause = ((ModifyPartitionClause) alterClause);
+                    // expand the partition names if it is 'Modify Partition(*)'
+                    if (clause.isNeedExpand()) {
+                        List<String> partitionNames = clause.getPartitionNames();
+                        partitionNames.clear();
+                        for (Partition partition : olapTable.getPartitions()) {
+                            partitionNames.add(partition.getName());
+                        }
+                    }
                     Map<String, String> properties = clause.getProperties();
                     if (properties.containsKey(PropertyAnalyzer.PROPERTIES_INMEMORY)) {
                         needProcessOutsideDatabaseLock = true;
                     } else {
-                        String partitionName = clause.getPartitionName();
-                        Catalog.getCurrentCatalog().modifyPartitionProperty(db, olapTable, partitionName, properties);
+                        List<String> partitionNames = clause.getPartitionNames();
+                        modifyPartitionsProperty(db, olapTable, partitionNames, properties);
                     }
                 } else if (alterClause instanceof AddPartitionClause) {
                     needProcessOutsideDatabaseLock = true;
@@ -238,17 +253,16 @@ public class Alter {
             } else if (alterClause instanceof ModifyPartitionClause) {
                 ModifyPartitionClause clause = ((ModifyPartitionClause) alterClause);
                 Map<String, String> properties = clause.getProperties();
-                String partitionName = clause.getPartitionName();
+                List<String> partitionNames = clause.getPartitionNames();
                 // currently, only in memory property could reach here
                 Preconditions.checkState(properties.containsKey(PropertyAnalyzer.PROPERTIES_INMEMORY));
-                boolean isInMemory = Boolean.parseBoolean(properties.get(PropertyAnalyzer.PROPERTIES_INMEMORY));
-                ((SchemaChangeHandler) schemaChangeHandler).updatePartitionInMemoryMeta(
-                        db, tableName, partitionName, isInMemory);
+                ((SchemaChangeHandler) schemaChangeHandler).updatePartitionsInMemoryMeta(
+                        db, tableName, partitionNames, properties);
 
                 db.writeLock();
                 try {
                     OlapTable olapTable = (OlapTable) db.getTable(tableName);
-                    Catalog.getCurrentCatalog().modifyPartitionProperty(db, olapTable, partitionName, properties);
+                    modifyPartitionsProperty(db, olapTable, partitionNames, properties);
                 } finally {
                     db.writeUnlock();
                 }
@@ -362,6 +376,205 @@ public class Alter {
         }
     }
 
+    /**
+     * Batch update partitions' properties
+     * caller should hold the db lock
+     */
+    public void modifyPartitionsProperty(Database db,
+                                         OlapTable olapTable,
+                                         List<String> partitionNames,
+                                         Map<String, String> properties)
+            throws DdlException, AnalysisException {
+        Preconditions.checkArgument(db.isWriteLockHeldByCurrentThread());
+        List<ModifyPartitionInfo> modifyPartitionInfos = Lists.newArrayList();
+        if (olapTable.getState() != OlapTableState.NORMAL) {
+            throw new DdlException("Table[" + olapTable.getName() + "]'s state is not NORMAL");
+        }
+
+        for (String partitionName : partitionNames) {
+            Partition partition = olapTable.getPartition(partitionName);
+            if (partition == null) {
+                throw new DdlException(
+                        "Partition[" + partitionName + "] does not exist in table[" + olapTable.getName() + "]");
+            }
+        }
+
+        boolean hasInMemory = false;
+        if (properties.containsKey(PropertyAnalyzer.PROPERTIES_INMEMORY)) {
+            hasInMemory = true;
+        }
+
+        // get value from properties here
+        // 1. data property
+        DataProperty newDataProperty =
+                PropertyAnalyzer.analyzeDataProperty(properties, null);
+        // 2. replication num
+        short newReplicationNum =
+                PropertyAnalyzer.analyzeReplicationNum(properties, (short) -1);
+        // 3. in memory
+        boolean newInMemory = PropertyAnalyzer.analyzeBooleanProp(properties,
+                PropertyAnalyzer.PROPERTIES_INMEMORY, false);
+        // 4. tablet type
+        TTabletType tTabletType =
+                PropertyAnalyzer.analyzeTabletType(properties);
+
+        // modify meta here
+        PartitionInfo partitionInfo = olapTable.getPartitionInfo();
+        for (String partitionName : partitionNames) {
+            Partition partition = olapTable.getPartition(partitionName);
+            // 1. date property
+            if (newDataProperty != null) {
+                partitionInfo.setDataProperty(partition.getId(), newDataProperty);
+            }
+            // 2. replication num
+            if (newReplicationNum != (short) -1) {
+                partitionInfo.setReplicationNum(partition.getId(), newReplicationNum);
+            }
+            // 3. in memory
+            boolean oldInMemory = partitionInfo.getIsInMemory(partition.getId());
+            if (hasInMemory && (newInMemory != oldInMemory)) {
+                partitionInfo.setIsInMemory(partition.getId(), newInMemory);
+            }
+            // 4. tablet type
+            if (tTabletType != partitionInfo.getTabletType(partition.getId())) {
+                partitionInfo.setTabletType(partition.getId(), tTabletType);
+            }
+            ModifyPartitionInfo info = new ModifyPartitionInfo(db.getId(), olapTable.getId(), partition.getId(),
+                    newDataProperty, newReplicationNum, hasInMemory ? newInMemory : oldInMemory);
+            modifyPartitionInfos.add(info);
+        }
+
+        // log here
+        BatchModifyPartitionsInfo info = new BatchModifyPartitionsInfo(modifyPartitionInfos);
+        Catalog.getCurrentCatalog().getEditLog().logBatchModifyPartition(info);
+    }
+
+    /**
+     * Update partition's properties
+     * caller should hold the db lock
+     */
+    public ModifyPartitionInfo modifyPartitionProperty(Database db,
+                                                       OlapTable olapTable,
+                                                       String partitionName,
+                                                       Map<String, String> properties)
+            throws DdlException {
+        Preconditions.checkArgument(db.isWriteLockHeldByCurrentThread());
+        if (olapTable.getState() != OlapTableState.NORMAL) {
+            throw new DdlException("Table[" + olapTable.getName() + "]'s state is not NORMAL");
+        }
+
+        Partition partition = olapTable.getPartition(partitionName);
+        if (partition == null) {
+            throw new DdlException(
+                    "Partition[" + partitionName + "] does not exist in table[" + olapTable.getName() + "]");
+        }
+
+        PartitionInfo partitionInfo = olapTable.getPartitionInfo();
+
+        // 1. data property
+        DataProperty oldDataProperty = partitionInfo.getDataProperty(partition.getId());
+        DataProperty newDataProperty = null;
+        try {
+            newDataProperty = PropertyAnalyzer.analyzeDataProperty(properties, oldDataProperty);
+        } catch (AnalysisException e) {
+            throw new DdlException(e.getMessage());
+        }
+        Preconditions.checkNotNull(newDataProperty);
+
+        if (newDataProperty.equals(oldDataProperty)) {
+            newDataProperty = null;
+        }
+
+        // 2. replication num
+        short oldReplicationNum = partitionInfo.getReplicationNum(partition.getId());
+        short newReplicationNum = (short) -1;
+        try {
+            newReplicationNum = PropertyAnalyzer.analyzeReplicationNum(properties, oldReplicationNum);
+        } catch (AnalysisException e) {
+            throw new DdlException(e.getMessage());
+        }
+
+        if (newReplicationNum == oldReplicationNum) {
+            newReplicationNum = (short) -1;
+        } else if (Catalog.getCurrentColocateIndex().isColocateTable(olapTable.getId())) {
+            ErrorReport.reportDdlException(ErrorCode.ERR_COLOCATE_TABLE_MUST_HAS_SAME_REPLICATION_NUM, oldReplicationNum);
+        }
+
+        // 3. in memory
+        boolean isInMemory = PropertyAnalyzer.analyzeBooleanProp(properties,
+                PropertyAnalyzer.PROPERTIES_INMEMORY, partitionInfo.getIsInMemory(partition.getId()));
+
+        // 4. tablet type
+        TTabletType tabletType = TTabletType.TABLET_TYPE_DISK;
+        try {
+            tabletType = PropertyAnalyzer.analyzeTabletType(properties);
+        } catch (AnalysisException e) {
+            throw new DdlException(e.getMessage());
+        }
+
+        // check if has other undefined properties
+        if (properties != null && !properties.isEmpty()) {
+            Joiner.MapJoiner mapJoiner = Joiner.on(", ").withKeyValueSeparator(" = ");
+            throw new DdlException("Unknown properties: " + mapJoiner.join(properties));
+        }
+
+        // modify meta here
+        // date property
+        if (newDataProperty != null) {
+            partitionInfo.setDataProperty(partition.getId(), newDataProperty);
+            LOG.debug("modify partition[{}-{}-{}] data property to {}", db.getId(), olapTable.getId(), partitionName,
+                    newDataProperty.toString());
+        }
+
+        // replication num
+        if (newReplicationNum != (short) -1) {
+            partitionInfo.setReplicationNum(partition.getId(), newReplicationNum);
+            LOG.debug("modify partition[{}-{}-{}] replication num to {}", db.getId(), olapTable.getId(), partitionName,
+                    newReplicationNum);
+        }
+
+        // in memory
+        if (isInMemory != partitionInfo.getIsInMemory(partition.getId())) {
+            partitionInfo.setIsInMemory(partition.getId(), isInMemory);
+            LOG.debug("modify partition[{}-{}-{}] in memory to {}", db.getId(), olapTable.getId(), partitionName,
+                    isInMemory);
+        }
+
+        // tablet type
+        // TODO: serialize to edit log
+        if (tabletType != partitionInfo.getTabletType(partition.getId())) {
+            partitionInfo.setTabletType(partition.getId(), tabletType);
+            LOG.debug("modify partition[{}-{}-{}] tablet type to {}", db.getId(), olapTable.getId(), partitionName,
+                    tabletType);
+        }
+
+        // log
+        ModifyPartitionInfo info = new ModifyPartitionInfo(db.getId(), olapTable.getId(), partition.getId(),
+                newDataProperty, newReplicationNum, isInMemory);
+        Catalog.getCurrentCatalog().getEditLog().logModifyPartition(info);
+
+        LOG.info("finish modify partition[{}-{}-{}]", db.getId(), olapTable.getId(), partitionName);
+        return info;
+    }
+
+    public void replayModifyPartition(ModifyPartitionInfo info) {
+        Database db = Catalog.getCurrentCatalog().getDb(info.getDbId());
+        db.writeLock();
+        try {
+            OlapTable olapTable = (OlapTable) db.getTable(info.getTableId());
+            PartitionInfo partitionInfo = olapTable.getPartitionInfo();
+            if (info.getDataProperty() != null) {
+                partitionInfo.setDataProperty(info.getPartitionId(), info.getDataProperty());
+            }
+            if (info.getReplicationNum() != (short) -1) {
+                partitionInfo.setReplicationNum(info.getPartitionId(), info.getReplicationNum());
+            }
+            partitionInfo.setIsInMemory(info.getPartitionId(), info.isInMemory());
+        } finally {
+            db.writeUnlock();
+        }
+    }
+
     public AlterHandler getSchemaChangeHandler() {
         return this.schemaChangeHandler;
     }
diff --git a/fe/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java b/fe/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
index 3e5b71d..98074f8 100644
--- a/fe/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
+++ b/fe/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
@@ -1463,6 +1463,9 @@ public class SchemaChangeHandler extends AlterHandler {
         LOG.info("send clear alter task for table {}, number: {}", olapTable.getName(), batchTask.getTaskNum());
     }
 
+    /**
+     * Update all partitions' in-memory property of table
+     */
     public void updateTableInMemoryMeta(Database db, String tableName, Map<String, String> properties) throws DdlException {
         List<Partition> partitions = Lists.newArrayList();
         OlapTable olapTable;
@@ -1491,6 +1494,48 @@ public class SchemaChangeHandler extends AlterHandler {
         }
     }
 
+    /**
+     * Update some specified partitions' in-memory property of table
+     */
+    public void updatePartitionsInMemoryMeta(Database db,
+                                             String tableName,
+                                             List<String> partitionNames,
+                                             Map<String, String> properties) throws DdlException {
+        OlapTable olapTable;
+        db.readLock();
+        try {
+            olapTable = (OlapTable)db.getTable(tableName);
+            for (String partitionName : partitionNames) {
+                Partition partition = olapTable.getPartition(partitionName);
+                if (partition == null) {
+                    throw new DdlException("Partition[" + partitionName + "] does not exist in " +
+                            "table[" + olapTable.getName() + "]");
+                }
+            }
+        } finally {
+            db.readUnlock();
+        }
+
+        boolean isInMemory = Boolean.parseBoolean(properties.get(PropertyAnalyzer.PROPERTIES_INMEMORY));
+        if (isInMemory == olapTable.isInMemory()) {
+            return;
+        }
+
+        for(String partitionName : partitionNames) {
+            try {
+                updatePartitionInMemoryMeta(db, olapTable.getName(), partitionName, isInMemory);
+            } catch (Exception e) {
+                String errMsg = "Failed to update partition[" + partitionName + "]'s 'in_memory' property. " +
+                        "The reason is [" + e.getMessage() + "]";
+                throw new DdlException(errMsg);
+            }
+        }
+    }
+
+    /**
+     * Update one specified partition's in-memory property by partition name of table
+     * This operation may return partial successfully, with a exception to inform user to retry
+     */
     public void updatePartitionInMemoryMeta(Database db,
                                             String tableName,
                                             String partitionName,
diff --git a/fe/src/main/java/org/apache/doris/analysis/ModifyPartitionClause.java b/fe/src/main/java/org/apache/doris/analysis/ModifyPartitionClause.java
index 6f9c21f..cba71ae 100644
--- a/fe/src/main/java/org/apache/doris/analysis/ModifyPartitionClause.java
+++ b/fe/src/main/java/org/apache/doris/analysis/ModifyPartitionClause.java
@@ -18,27 +18,40 @@
 package org.apache.doris.analysis;
 
 import org.apache.doris.alter.AlterOpType;
+import org.apache.doris.catalog.DataProperty;
 import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.FeConstants;
 import org.apache.doris.common.util.PrintableMap;
-
+import org.apache.doris.common.util.PropertyAnalyzer;
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
+import java.util.List;
 import java.util.Map;
 
 // clause which is used to modify partition properties
+// 1. Modify Partition p1 set ("replication_num" = "3")
+// 2. Modify Partition (p1, p3, p4) set ("replication_num" = "3")
+// 3. Modify Partition (*) set ("replication_num" = "3")
 public class ModifyPartitionClause extends AlterTableClause {
 
-    private String partitionName;
+    private List<String> partitionNames;
     private Map<String, String> properties;
+    private boolean needExpand = false;
 
-    public String getPartitionName() {
-        return partitionName;
+    public List<String> getPartitionNames() {
+        return partitionNames;
     }
 
-    public ModifyPartitionClause(String partitionName, Map<String, String> properties) {
+    // c'tor for non-star clause
+    public ModifyPartitionClause(List<String> partitionNames, Map<String, String> properties) {
         super(AlterOpType.MODIFY_PARTITION);
-        this.partitionName = partitionName;
+        this.partitionNames = partitionNames;
         this.properties = properties;
+        this.needExpand = false;
         // ATTN: currently, modify partition only allow 3 kinds of operations:
         // 1. modify replication num
         // 2. modify data property
@@ -48,15 +61,58 @@ public class ModifyPartitionClause extends AlterTableClause {
         this.needTableStable = false;
     }
 
+    // c'tor for 'Modify Partition(*)' clause
+    private ModifyPartitionClause(Map<String, String> properties) {
+        super(AlterOpType.MODIFY_PARTITION);
+        this.partitionNames = Lists.newArrayList();
+        this.properties = properties;
+        this.needExpand = true;
+        this.needTableStable = false;
+    }
+
+    public static ModifyPartitionClause createStarClause(Map<String, String> properties) {
+        return new ModifyPartitionClause(properties);
+    }
+
     @Override
     public void analyze(Analyzer analyzer) throws AnalysisException {
-        if (Strings.isNullOrEmpty(partitionName)) {
-            throw new AnalysisException("Partition name is not set");
+        if (partitionNames == null || (!needExpand && partitionNames.isEmpty())) {
+            throw new AnalysisException("Partition names is not set or empty");
+        }
+
+        if (partitionNames.stream().anyMatch(entity -> Strings.isNullOrEmpty(entity))) {
+            throw new AnalysisException("there are empty partition name");
         }
 
         if (properties == null || properties.isEmpty()) {
             throw new AnalysisException("Properties is not set");
         }
+
+        // check properties here
+        checkProperties(Maps.newHashMap(properties));
+    }
+
+    // Check the following properties' legality before modifying partition.
+    // 1. replication_num
+    // 2. storage_medium && storage_cooldown_time
+    // 3. in_memory
+    // 4. tablet type
+    private void checkProperties(Map<String, String> properties) throws AnalysisException {
+        // 1. data property
+        DataProperty newDataProperty = null;
+        newDataProperty = PropertyAnalyzer.analyzeDataProperty(properties, DataProperty.DEFAULT_DATA_PROPERTY);
+        Preconditions.checkNotNull(newDataProperty);
+
+        // 2. replication num
+        short newReplicationNum = (short) -1;
+        newReplicationNum = PropertyAnalyzer.analyzeReplicationNum(properties, FeConstants.default_replication_num);
+        Preconditions.checkState(newReplicationNum != (short) -1);
+
+        // 3. in memory
+        PropertyAnalyzer.analyzeBooleanProp(properties, PropertyAnalyzer.PROPERTIES_INMEMORY, false);
+
+        // 4. tablet type
+        PropertyAnalyzer.analyzeTabletType(properties);
     }
 
     @Override
@@ -64,11 +120,21 @@ public class ModifyPartitionClause extends AlterTableClause {
         return this.properties;
     }
 
+    public boolean isNeedExpand() {
+        return this.needExpand;
+    }
+
     @Override
     public String toSql() {
         StringBuilder sb = new StringBuilder();
         sb.append("MODIFY PARTITION ");
-        sb.append(partitionName);
+        sb.append("(");
+        if (needExpand) {
+            sb.append("*");
+        } else {
+            sb.append(Joiner.on(", ").join(partitionNames));
+        }
+        sb.append(")");
         sb.append(" SET (");
         sb.append(new PrintableMap<String, String>(properties, "=", true, false));
         sb.append(")");
diff --git a/fe/src/main/java/org/apache/doris/catalog/Catalog.java b/fe/src/main/java/org/apache/doris/catalog/Catalog.java
index 0c0251b..d1a4460 100755
--- a/fe/src/main/java/org/apache/doris/catalog/Catalog.java
+++ b/fe/src/main/java/org/apache/doris/catalog/Catalog.java
@@ -208,9 +208,7 @@ import org.apache.doris.thrift.TTabletType;
 import org.apache.doris.thrift.TTaskType;
 import org.apache.doris.transaction.GlobalTransactionMgr;
 import org.apache.doris.transaction.PublishVersionDaemon;
-
 import com.google.common.base.Joiner;
-import com.google.common.base.Joiner.MapJoiner;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.common.collect.HashMultimap;
@@ -3349,122 +3347,6 @@ public class Catalog {
         }
     }
 
-    public void modifyPartitionProperty(Database db, OlapTable olapTable, String partitionName, Map<String, String> properties)
-            throws DdlException {
-        Preconditions.checkArgument(db.isWriteLockHeldByCurrentThread());
-        if (olapTable.getState() != OlapTableState.NORMAL) {
-            throw new DdlException("Table[" + olapTable.getName() + "]'s state is not NORMAL");
-        }
-
-        Partition partition = olapTable.getPartition(partitionName);
-        if (partition == null) {
-            throw new DdlException(
-                    "Partition[" + partitionName + "] does not exist in table[" + olapTable.getName() + "]");
-        }
-
-        PartitionInfo partitionInfo = olapTable.getPartitionInfo();
-
-        // 1. data property
-        DataProperty oldDataProperty = partitionInfo.getDataProperty(partition.getId());
-        DataProperty newDataProperty = null;
-        try {
-            newDataProperty = PropertyAnalyzer.analyzeDataProperty(properties, oldDataProperty);
-        } catch (AnalysisException e) {
-            throw new DdlException(e.getMessage());
-        }
-        Preconditions.checkNotNull(newDataProperty);
-
-        if (newDataProperty.equals(oldDataProperty)) {
-            newDataProperty = null;
-        }
-
-        // 2. replication num
-        short oldReplicationNum = partitionInfo.getReplicationNum(partition.getId());
-        short newReplicationNum = (short) -1;
-        try {
-            newReplicationNum = PropertyAnalyzer.analyzeReplicationNum(properties, oldReplicationNum);
-        } catch (AnalysisException e) {
-            throw new DdlException(e.getMessage());
-        }
-
-        if (newReplicationNum == oldReplicationNum) {
-            newReplicationNum = (short) -1;
-        } else if (Catalog.getCurrentColocateIndex().isColocateTable(olapTable.getId())) {
-            ErrorReport.reportDdlException(ErrorCode.ERR_COLOCATE_TABLE_MUST_HAS_SAME_REPLICATION_NUM, oldReplicationNum);
-        }
-
-        // 3. in memory
-        boolean isInMemory = PropertyAnalyzer.analyzeBooleanProp(properties,
-                PropertyAnalyzer.PROPERTIES_INMEMORY, partitionInfo.getIsInMemory(partition.getId()));
-
-        // 4. tablet type
-        TTabletType tabletType = TTabletType.TABLET_TYPE_DISK;
-        try {
-            tabletType = PropertyAnalyzer.analyzeTabletType(properties);
-        } catch (AnalysisException e) {
-            throw new DdlException(e.getMessage());
-        }
-
-        // check if has other undefined properties
-        if (properties != null && !properties.isEmpty()) {
-            MapJoiner mapJoiner = Joiner.on(", ").withKeyValueSeparator(" = ");
-            throw new DdlException("Unknown properties: " + mapJoiner.join(properties));
-        }
-
-        // modify meta here
-        // date property
-        if (newDataProperty != null) {
-            partitionInfo.setDataProperty(partition.getId(), newDataProperty);
-            LOG.debug("modify partition[{}-{}-{}] data property to {}", db.getId(), olapTable.getId(), partitionName,
-                    newDataProperty.toString());
-        }
-
-        // replication num
-        if (newReplicationNum != (short) -1) {
-            partitionInfo.setReplicationNum(partition.getId(), newReplicationNum);
-            LOG.debug("modify partition[{}-{}-{}] replication num to {}", db.getId(), olapTable.getId(), partitionName,
-                    newReplicationNum);
-        }
-
-        // in memory
-        if (isInMemory != partitionInfo.getIsInMemory(partition.getId())) {
-            partitionInfo.setIsInMemory(partition.getId(), isInMemory);
-            LOG.debug("modify partition[{}-{}-{}] in memory to {}", db.getId(), olapTable.getId(), partitionName,
-                    isInMemory);
-        }
-
-        // tablet type
-        // TODO: serialize to edit log
-        if (tabletType != partitionInfo.getTabletType(partition.getId())) {
-            partitionInfo.setTabletType(partition.getId(), tabletType);
-            LOG.debug("modify partition[{}-{}-{}] tablet type to {}", db.getId(), olapTable.getId(), partitionName,
-                    tabletType);
-        }
-
-        // log
-        ModifyPartitionInfo info = new ModifyPartitionInfo(db.getId(), olapTable.getId(), partition.getId(),
-                newDataProperty, newReplicationNum, isInMemory);
-        editLog.logModifyPartition(info);
-    }
-
-    public void replayModifyPartition(ModifyPartitionInfo info) {
-        Database db = this.getDb(info.getDbId());
-        db.writeLock();
-        try {
-            OlapTable olapTable = (OlapTable) db.getTable(info.getTableId());
-            PartitionInfo partitionInfo = olapTable.getPartitionInfo();
-            if (info.getDataProperty() != null) {
-                partitionInfo.setDataProperty(info.getPartitionId(), info.getDataProperty());
-            }
-            if (info.getReplicationNum() != (short) -1) {
-                partitionInfo.setReplicationNum(info.getPartitionId(), info.getReplicationNum());
-            }
-            partitionInfo.setIsInMemory(info.getPartitionId(), info.isInMemory());
-        } finally {
-            db.writeUnlock();
-        }
-    }
-
     private Partition createPartitionWithIndices(String clusterName, long dbId, long tableId,
                                                  long baseIndexId, long partitionId, String partitionName,
                                                  Map<Long, MaterializedIndexMeta> indexIdToMeta,
diff --git a/fe/src/main/java/org/apache/doris/catalog/DataProperty.java b/fe/src/main/java/org/apache/doris/catalog/DataProperty.java
index ec5fb9a..593f3d3 100644
--- a/fe/src/main/java/org/apache/doris/catalog/DataProperty.java
+++ b/fe/src/main/java/org/apache/doris/catalog/DataProperty.java
@@ -22,6 +22,7 @@ import org.apache.doris.common.io.Text;
 import org.apache.doris.common.io.Writable;
 import org.apache.doris.common.util.TimeUtils;
 import org.apache.doris.thrift.TStorageMedium;
+import com.google.gson.annotations.SerializedName;
 
 import java.io.DataInput;
 import java.io.DataOutput;
@@ -32,7 +33,9 @@ public class DataProperty implements Writable {
             "SSD".equalsIgnoreCase(Config.default_storage_medium) ? TStorageMedium.SSD : TStorageMedium.HDD);
     public static final long MAX_COOLDOWN_TIME_MS = 253402271999000L; // 9999-12-31 23:59:59
 
+    @SerializedName(value =  "storageMedium")
     private TStorageMedium storageMedium;
+    @SerializedName(value =  "cooldownTimeMs")
     private long cooldownTimeMs;
 
     private DataProperty() {
diff --git a/fe/src/main/java/org/apache/doris/journal/JournalEntity.java b/fe/src/main/java/org/apache/doris/journal/JournalEntity.java
index 5704bc7..f76dfed 100644
--- a/fe/src/main/java/org/apache/doris/journal/JournalEntity.java
+++ b/fe/src/main/java/org/apache/doris/journal/JournalEntity.java
@@ -50,6 +50,7 @@ import org.apache.doris.persist.AlterViewInfo;
 import org.apache.doris.persist.BackendIdsUpdateInfo;
 import org.apache.doris.persist.BackendTabletsInfo;
 import org.apache.doris.persist.BatchDropInfo;
+import org.apache.doris.persist.BatchModifyPartitionsInfo;
 import org.apache.doris.persist.ClusterInfo;
 import org.apache.doris.persist.ColocatePersistInfo;
 import org.apache.doris.persist.ConsistencyCheckInfo;
@@ -189,6 +190,11 @@ public class JournalEntity implements Writable {
                 isRead = true;
                 break;
             }
+            case OperationType.OP_BATCH_MODIFY_PARTITION: {
+                data = BatchModifyPartitionsInfo.read(in);
+                isRead = true;
+                break;
+            }
             case OperationType.OP_ERASE_DB:
             case OperationType.OP_ERASE_TABLE:
             case OperationType.OP_ERASE_PARTITION: {
diff --git a/fe/src/main/java/org/apache/doris/journal/local/LocalJournalCursor.java b/fe/src/main/java/org/apache/doris/journal/local/LocalJournalCursor.java
index 57f1bf2..34f39d9 100644
--- a/fe/src/main/java/org/apache/doris/journal/local/LocalJournalCursor.java
+++ b/fe/src/main/java/org/apache/doris/journal/local/LocalJournalCursor.java
@@ -29,6 +29,7 @@ import org.apache.doris.load.DeleteInfo;
 import org.apache.doris.load.LoadErrorHub;
 import org.apache.doris.load.LoadJob;
 import org.apache.doris.persist.BatchDropInfo;
+import org.apache.doris.persist.BatchModifyPartitionsInfo;
 import org.apache.doris.persist.ConsistencyCheckInfo;
 import org.apache.doris.persist.CreateTableInfo;
 import org.apache.doris.persist.DatabaseInfo;
@@ -250,6 +251,11 @@ public final class LocalJournalCursor implements JournalCursor {
                 ret.setData(info);
                 break;
             }
+            case OperationType.OP_BATCH_MODIFY_PARTITION: {
+                BatchModifyPartitionsInfo info = BatchModifyPartitionsInfo.read(in);
+                ret.setData(info);
+                break;
+            }
             case OperationType.OP_ERASE_DB:
             case OperationType.OP_ERASE_TABLE:
             case OperationType.OP_ERASE_PARTITION: {
diff --git a/fe/src/main/java/org/apache/doris/persist/BatchModifyPartitionsInfo.java b/fe/src/main/java/org/apache/doris/persist/BatchModifyPartitionsInfo.java
new file mode 100644
index 0000000..86f32ed
--- /dev/null
+++ b/fe/src/main/java/org/apache/doris/persist/BatchModifyPartitionsInfo.java
@@ -0,0 +1,74 @@
+// 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.persist;
+
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.persist.gson.GsonUtils;
+import com.google.gson.annotations.SerializedName;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * used for batch modify multi partitions in one atomic operation
+ */
+
+public class BatchModifyPartitionsInfo implements Writable {
+    @SerializedName(value = "infos")
+    private List<ModifyPartitionInfo> infos;
+
+    public BatchModifyPartitionsInfo(List<ModifyPartitionInfo> infos) {
+        this.infos = infos;
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        Text.writeString(out, GsonUtils.GSON.toJson(this));
+    }
+
+    public static BatchModifyPartitionsInfo read(DataInput in) throws IOException {
+        String json = Text.readString(in);
+        return GsonUtils.GSON.fromJson(json, BatchModifyPartitionsInfo.class);
+    }
+
+    public List<ModifyPartitionInfo> getModifyPartitionInfos() {
+        return infos;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof BatchModifyPartitionsInfo)) {
+            return false;
+        }
+        List<ModifyPartitionInfo> otherInfos = ((BatchModifyPartitionsInfo) other).getModifyPartitionInfos();
+        for (ModifyPartitionInfo thisInfo : infos) {
+            for (ModifyPartitionInfo otherInfo : otherInfos) {
+                if (!thisInfo.equals(otherInfo)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+}
diff --git a/fe/src/main/java/org/apache/doris/persist/EditLog.java b/fe/src/main/java/org/apache/doris/persist/EditLog.java
index 14bb416..728ee0e 100644
--- a/fe/src/main/java/org/apache/doris/persist/EditLog.java
+++ b/fe/src/main/java/org/apache/doris/persist/EditLog.java
@@ -212,7 +212,14 @@ public class EditLog {
                     ModifyPartitionInfo info = (ModifyPartitionInfo) journal.getData();
                     LOG.info("Begin to unprotect modify partition. db = " + info.getDbId()
                             + " table = " + info.getTableId() + " partitionId = " + info.getPartitionId());
-                    catalog.replayModifyPartition(info);
+                    catalog.getAlterInstance().replayModifyPartition(info);
+                    break;
+                }
+                case OperationType.OP_BATCH_MODIFY_PARTITION: {
+                    BatchModifyPartitionsInfo info = (BatchModifyPartitionsInfo) journal.getData();
+                    for(ModifyPartitionInfo modifyPartitionInfo : info.getModifyPartitionInfos()) {
+                        catalog.getAlterInstance().replayModifyPartition(modifyPartitionInfo);
+                    }
                     break;
                 }
                 case OperationType.OP_ERASE_TABLE: {
@@ -922,6 +929,10 @@ public class EditLog {
         logEdit(OperationType.OP_MODIFY_PARTITION, info);
     }
 
+    public void logBatchModifyPartition(BatchModifyPartitionsInfo info) {
+        logEdit(OperationType.OP_BATCH_MODIFY_PARTITION, info);
+    }
+
     public void logDropTable(DropInfo info) {
         logEdit(OperationType.OP_DROP_TABLE, info);
     }
diff --git a/fe/src/main/java/org/apache/doris/persist/ModifyPartitionInfo.java b/fe/src/main/java/org/apache/doris/persist/ModifyPartitionInfo.java
index 56a6d0d..7996906 100644
--- a/fe/src/main/java/org/apache/doris/persist/ModifyPartitionInfo.java
+++ b/fe/src/main/java/org/apache/doris/persist/ModifyPartitionInfo.java
@@ -21,6 +21,7 @@ import org.apache.doris.catalog.Catalog;
 import org.apache.doris.catalog.DataProperty;
 import org.apache.doris.common.FeMetaVersion;
 import org.apache.doris.common.io.Writable;
+import com.google.gson.annotations.SerializedName;
 
 import java.io.DataInput;
 import java.io.DataOutput;
@@ -28,11 +29,17 @@ import java.io.IOException;
 
 public class ModifyPartitionInfo implements Writable {
 
+    @SerializedName(value = "dbId")
     private long dbId;
+    @SerializedName(value = "tableId")
     private long tableId;
+    @SerializedName(value = "partitionId")
     private long partitionId;
+    @SerializedName(value = "dataProperty")
     private DataProperty dataProperty;
+    @SerializedName(value = "replicationNum")
     private short replicationNum;
+    @SerializedName(value = "isInMemory")
     private boolean isInMemory;
 
     public ModifyPartitionInfo() {
@@ -81,6 +88,20 @@ public class ModifyPartitionInfo implements Writable {
     }
 
     @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof ModifyPartitionInfo)) {
+            return false;
+        }
+        ModifyPartitionInfo otherInfo = (ModifyPartitionInfo) other;
+        return dbId == otherInfo.getDbId() && tableId == otherInfo.getTableId() &&
+                dataProperty.equals(otherInfo.getDataProperty()) && replicationNum == otherInfo.getReplicationNum()
+                && isInMemory == otherInfo.isInMemory();
+    }
+
+    @Override
     public void write(DataOutput out) throws IOException {
         out.writeLong(dbId);
         out.writeLong(tableId);
diff --git a/fe/src/main/java/org/apache/doris/persist/OperationType.java b/fe/src/main/java/org/apache/doris/persist/OperationType.java
index 01171e6..969160c 100644
--- a/fe/src/main/java/org/apache/doris/persist/OperationType.java
+++ b/fe/src/main/java/org/apache/doris/persist/OperationType.java
@@ -44,6 +44,7 @@ public class OperationType {
     public static final short OP_TRUNCATE_TABLE = 118;
     public static final short OP_MODIFY_VIEW_DEF = 119;
     public static final short OP_REPLACE_TEMP_PARTITION = 210;
+    public static final short OP_BATCH_MODIFY_PARTITION = 211;
 
     // 20~29 120~129 220~229 ...
     public static final short OP_START_ROLLUP = 20;
diff --git a/fe/src/test/java/org/apache/doris/alter/AlterTest.java b/fe/src/test/java/org/apache/doris/alter/AlterTest.java
index 26669be..41dadc0 100644
--- a/fe/src/test/java/org/apache/doris/alter/AlterTest.java
+++ b/fe/src/test/java/org/apache/doris/alter/AlterTest.java
@@ -20,14 +20,20 @@ package org.apache.doris.alter;
 import org.apache.doris.analysis.AlterTableStmt;
 import org.apache.doris.analysis.CreateDbStmt;
 import org.apache.doris.analysis.CreateTableStmt;
+import org.apache.doris.analysis.DateLiteral;
 import org.apache.doris.catalog.Catalog;
+import org.apache.doris.catalog.DataProperty;
 import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.Partition;
+import org.apache.doris.catalog.Type;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.util.TimeUtils;
 import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.thrift.TStorageMedium;
 import org.apache.doris.utframe.UtFrameUtils;
+import com.google.common.collect.Lists;
 
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -35,6 +41,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 import java.io.File;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -94,6 +101,28 @@ public class AlterTest {
                 ")\n" +
                 "DISTRIBUTED BY HASH(k2) BUCKETS 3\n" +
                 "PROPERTIES('replication_num' = '1');");
+
+        createTable("CREATE TABLE test.tbl4\n" +
+                "(\n" +
+                "    k1 date,\n" +
+                "    k2 int,\n" +
+                "    v1 int sum\n" +
+                ")\n" +
+                "PARTITION BY RANGE(k1)\n" +
+                "(\n" +
+                "    PARTITION p1 values less than('2020-02-01'),\n" +
+                "    PARTITION p2 values less than('2020-03-01'),\n" +
+                "    PARTITION p3 values less than('2020-04-01'),\n" +
+                "    PARTITION p4 values less than('2020-05-01')\n" +
+                ")\n" +
+                "DISTRIBUTED BY HASH(k2) BUCKETS 3\n" +
+                "PROPERTIES" +
+                "(" +
+                "    'replication_num' = '1',\n" +
+                "    'in_memory' = 'false',\n" +
+                "    'storage_medium' = 'SSD',\n" +
+                "    'storage_cooldown_time' = '9999-12-31 00:00:00'\n" +
+                ");");
     }
 
     @AfterClass
@@ -213,6 +242,65 @@ public class AlterTest {
         alterTable(stmt, false);
     }
 
+    // test batch update range partitions' properties
+    @Test
+    public void testBatchUpdatePartitionProperties() throws Exception {
+        Database db = Catalog.getCurrentCatalog().getDb("default_cluster:test");
+        OlapTable tbl4 = (OlapTable) db.getTable("tbl4");
+        Partition p1 = tbl4.getPartition("p1");
+        Partition p2 = tbl4.getPartition("p2");
+        Partition p3 = tbl4.getPartition("p3");
+        Partition p4 = tbl4.getPartition("p4");
+
+        // batch update replication_num property
+        String stmt = "alter table test.tbl4 modify partition (p1, p2, p4) set ('replication_num' = '3')";
+        List<Partition> partitionList = Lists.newArrayList(p1, p2, p4);
+        for (Partition partition : partitionList) {
+            Assert.assertEquals(Short.valueOf("1"), Short.valueOf(tbl4.getPartitionInfo().getReplicationNum(partition.getId())));
+        }
+        alterTable(stmt, false);
+        for (Partition partition : partitionList) {
+            Assert.assertEquals(Short.valueOf("3"), Short.valueOf(tbl4.getPartitionInfo().getReplicationNum(partition.getId())));
+        }
+        Assert.assertEquals(Short.valueOf("1"), Short.valueOf(tbl4.getPartitionInfo().getReplicationNum(p3.getId())));
+
+        // batch update in_memory property
+        stmt = "alter table test.tbl4 modify partition (p1, p2, p3) set ('in_memory' = 'true')";
+        partitionList = Lists.newArrayList(p1, p2, p3);
+        for (Partition partition : partitionList) {
+            Assert.assertEquals(false, tbl4.getPartitionInfo().getIsInMemory(partition.getId()));
+        }
+        alterTable(stmt, false);
+        for (Partition partition : partitionList) {
+            Assert.assertEquals(true, tbl4.getPartitionInfo().getIsInMemory(partition.getId()));
+        }
+        Assert.assertEquals(false, tbl4.getPartitionInfo().getIsInMemory(p4.getId()));
+
+        // batch update storage_medium and storage_cool_down properties
+        stmt = "alter table test.tbl4 modify partition (p2, p3, p4) set ('storage_medium' = 'HDD')";
+        DateLiteral dateLiteral = new DateLiteral("9999-12-31 00:00:00", Type.DATETIME);
+        long coolDownTimeMs = dateLiteral.unixTimestamp(TimeUtils.getTimeZone());
+        DataProperty oldDataProperty = new DataProperty(TStorageMedium.SSD, coolDownTimeMs);
+        partitionList = Lists.newArrayList(p2, p3, p4);
+        for (Partition partition : partitionList) {
+            Assert.assertEquals(oldDataProperty, tbl4.getPartitionInfo().getDataProperty(partition.getId()));
+        }
+        alterTable(stmt, false);
+        DataProperty newDataProperty = new DataProperty(TStorageMedium.HDD, DataProperty.MAX_COOLDOWN_TIME_MS);
+        for (Partition partition : partitionList) {
+            Assert.assertEquals(newDataProperty, tbl4.getPartitionInfo().getDataProperty(partition.getId()));
+        }
+        Assert.assertEquals(oldDataProperty, tbl4.getPartitionInfo().getDataProperty(p1.getId()));
+
+        // batch update range partitions' properties with *
+        stmt = "alter table test.tbl4 modify partition (*) set ('replication_num' = '1')";
+        partitionList = Lists.newArrayList(p1, p2, p3, p4);
+        alterTable(stmt, false);
+        for (Partition partition : partitionList) {
+            Assert.assertEquals(Short.valueOf("1"), Short.valueOf(tbl4.getPartitionInfo().getReplicationNum(partition.getId())));
+        }
+    }
+
     @Test
     public void testDynamicPartitionDropAndAdd() throws Exception {
         // test day range
diff --git a/fe/src/test/java/org/apache/doris/persist/BatchModifyPartitionsInfoTest.java b/fe/src/test/java/org/apache/doris/persist/BatchModifyPartitionsInfoTest.java
new file mode 100644
index 0000000..e2b8c18
--- /dev/null
+++ b/fe/src/test/java/org/apache/doris/persist/BatchModifyPartitionsInfoTest.java
@@ -0,0 +1,61 @@
+package org.apache.doris.persist;
+
+import org.apache.doris.catalog.DataProperty;
+import org.apache.doris.common.AnalysisException;
+import com.google.common.collect.Lists;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+public class BatchModifyPartitionsInfoTest {
+    private static String fileName = "./BatchModifyPartitionsInfoTest";
+
+    private final long DB_ID = 10000L;
+    private final long TB_ID = 30000L;
+    private final long PARTITION_ID_1 = 40000L;
+    private final long PARTITION_ID_2 = 40001L;
+    private final long PARTITION_ID_3 = 40002L;
+
+    @After
+    public void tearDown() {
+        File file = new File(fileName);
+        file.delete();
+    }
+
+    @Test
+    public void testSerialzeBatchModifyPartitionsInfo() throws IOException, AnalysisException {
+        List<ModifyPartitionInfo> ModifyInfos = Lists.newArrayList();
+        // 1. Write objects to file
+        File file = new File(fileName);
+        file.createNewFile();
+        DataOutputStream out = new DataOutputStream(new FileOutputStream(file));
+
+        List<Long> partitionIds = Lists.newArrayList(PARTITION_ID_1, PARTITION_ID_2, PARTITION_ID_3);
+        for (long partitionId : partitionIds) {
+            ModifyInfos.add(new ModifyPartitionInfo(DB_ID, TB_ID, partitionId,
+                    DataProperty.DEFAULT_DATA_PROPERTY, (short) 3, true));
+        }
+
+        BatchModifyPartitionsInfo batchModifyPartitionsInfo = new BatchModifyPartitionsInfo(ModifyInfos);
+        batchModifyPartitionsInfo.write(out);
+        out.flush();
+        out.close();
+
+        // 2. Read objects from file
+        DataInputStream in = new DataInputStream(new FileInputStream(file));
+
+        BatchModifyPartitionsInfo readBatchModifyPartitionsInfo = BatchModifyPartitionsInfo.read(in);
+        Assert.assertEquals(batchModifyPartitionsInfo, readBatchModifyPartitionsInfo);
+
+        in.close();
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org