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

jlfsdtc pushed a commit to branch kylin5
in repository https://gitbox.apache.org/repos/asf/kylin.git


The following commit(s) were added to refs/heads/kylin5 by this push:
     new b715b530e3 KYLIN-6076 Add tblproperties validation for InternalTable
b715b530e3 is described below

commit b715b530e344c81de8259cc93e3f8e36557d0b38
Author: jlf <1251489...@qq.com>
AuthorDate: Wed Aug 27 18:41:25 2025 +0800

    KYLIN-6076 Add tblproperties validation for InternalTable
---
 .../kylin/rest/request/InternalTableRequest.java   |  4 +-
 .../org/apache/kylin/common/msg/CnMessage.java     | 10 ++++
 .../java/org/apache/kylin/common/msg/Message.java  |  8 +++
 .../org/apache/kylin/common/util/ArrayUtils.java   | 28 +++++++++
 .../kylin/metadata/cube/model/NBatchConstants.java | 29 +++++++++
 .../kylin/metadata/table/InternalTableDesc.java    | 51 ++++++++--------
 .../metadata/table/InternalTableDescTest.java      | 10 +++-
 .../rest/service/InternalTableServiceTest.java     | 55 +++++++++++++++++
 .../kylin/rest/service/InternalTableService.java   | 68 ++++++++++++++++++++++
 .../rest/controller/InternalTableController.java   |  2 +
 10 files changed, 239 insertions(+), 26 deletions(-)

diff --git 
a/src/common-service/src/main/java/org/apache/kylin/rest/request/InternalTableRequest.java
 
b/src/common-service/src/main/java/org/apache/kylin/rest/request/InternalTableRequest.java
index 7335df38cd..6b1aede324 100644
--- 
a/src/common-service/src/main/java/org/apache/kylin/rest/request/InternalTableRequest.java
+++ 
b/src/common-service/src/main/java/org/apache/kylin/rest/request/InternalTableRequest.java
@@ -17,7 +17,7 @@
  */
 package org.apache.kylin.rest.request;
 
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -36,7 +36,7 @@ public class InternalTableRequest {
     private String datePartitionFormat;
 
     @JsonProperty("tbl_properties")
-    private Map<String, String> tblProperties = new HashMap<>();
+    private Map<String, String> tblProperties = new LinkedHashMap<>();
 
     @JsonProperty("storage_type")
     private String storageType;
diff --git 
a/src/core-common/src/main/java/org/apache/kylin/common/msg/CnMessage.java 
b/src/core-common/src/main/java/org/apache/kylin/common/msg/CnMessage.java
index 3c2a8a953b..e457e5139f 100644
--- a/src/core-common/src/main/java/org/apache/kylin/common/msg/CnMessage.java
+++ b/src/core-common/src/main/java/org/apache/kylin/common/msg/CnMessage.java
@@ -1780,4 +1780,14 @@ public class CnMessage extends Message {
     public String getModelStorageUpdateFailed() {
         return "更新模型存储类型失败,请确认模型中没有 segment 后重试。";
     }
+
+    @Override
+    public String getInternalTableStorageTypeFailed() {
+        return "不支持的内表存储类型 %s,请检查后再重试。";
+    }
+
+    @Override
+    public String getInternalTableTblPropertiesFailed() {
+        return "不支持的内表表属性 %s,请检查后再重试。";
+    }
 }
diff --git 
a/src/core-common/src/main/java/org/apache/kylin/common/msg/Message.java 
b/src/core-common/src/main/java/org/apache/kylin/common/msg/Message.java
index 9e11f10831..ef13e429d3 100644
--- a/src/core-common/src/main/java/org/apache/kylin/common/msg/Message.java
+++ b/src/core-common/src/main/java/org/apache/kylin/common/msg/Message.java
@@ -1625,4 +1625,12 @@ public class Message {
     public String getModelStorageUpdateFailed() {
         return "Update model storage failed, please make sure model not 
contain segment.";
     }
+
+    public String getInternalTableStorageTypeFailed() {
+        return "Unsupported StorageType \"%s\", please check and try again.";
+    }
+
+    public String getInternalTableTblPropertiesFailed() {
+        return "Unsupported tbl_properties parameter \"%s\", please check and 
try again.";
+    }
 }
diff --git 
a/src/core-common/src/main/java/org/apache/kylin/common/util/ArrayUtils.java 
b/src/core-common/src/main/java/org/apache/kylin/common/util/ArrayUtils.java
index 0894324ba0..caea69471c 100644
--- a/src/core-common/src/main/java/org/apache/kylin/common/util/ArrayUtils.java
+++ b/src/core-common/src/main/java/org/apache/kylin/common/util/ArrayUtils.java
@@ -35,4 +35,32 @@ public class ArrayUtils {
         }
         return result;
     }
+
+    /**
+     * Checks if the given subset is a prefix of the superset.
+     *
+     * A subset is considered a prefix of the superset if:
+     * 1. The subset is shorter than or equal to the superset in length.
+     * 2. All elements in the subset match the corresponding elements in the 
superset at the same positions.
+     *
+     * Examples:
+     * - subset = ["a", "b"], superset = ["a", "b", "c"] -> returns true
+     * - subset = ["a", "b"], superset = ["a", "c", "b"] -> returns false
+     * - subset = ["a", "b", "c"], superset = ["a", "b"] -> returns false
+     *
+     * @param subset    The list to check if it is a prefix subset.
+     * @param superset  The list to check against (the larger list).
+     * @return          True if the subset is a prefix of the superset, 
otherwise false.
+     */
+    public static boolean isPrefixSubset(List<String> subset, List<String> 
superset) {
+        if (subset.size() > superset.size()) {
+            return false;
+        }
+        for (int i = 0; i < subset.size(); i++) {
+            if (!subset.get(i).equals(superset.get(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git 
a/src/core-metadata/src/main/java/org/apache/kylin/metadata/cube/model/NBatchConstants.java
 
b/src/core-metadata/src/main/java/org/apache/kylin/metadata/cube/model/NBatchConstants.java
index 415e59f1a9..fab3fab21a 100644
--- 
a/src/core-metadata/src/main/java/org/apache/kylin/metadata/cube/model/NBatchConstants.java
+++ 
b/src/core-metadata/src/main/java/org/apache/kylin/metadata/cube/model/NBatchConstants.java
@@ -18,6 +18,8 @@
 
 package org.apache.kylin.metadata.cube.model;
 
+import lombok.Getter;
+
 public interface NBatchConstants {
     String P_DATAFLOW_ID = "dataflowId";
     String P_SEGMENT_IDS = "segmentIds";
@@ -68,8 +70,12 @@ public interface NBatchConstants {
     String P_END_DATE = "endTime";
     String P_PRIMARY_KEY = "primaryKey";
     String P_ORDER_BY_KEY = "orderByKey";
+    String P_BUCKET_COLUMN = "bucketCol";
+    String P_BUCKET_NUM = "bucketNum";
     String P_DELETE_PARTITION_VALUES = "deletePartitionValues";
     String P_DELETE_PARTITION = "deletePartition";
+    String P_SORT_BY_PARTITION_BEFORE_SAVE = "sortByPartition";
+    String P_PRELOADED_CACHE = "preloadedCache";
 
 
     /** index planner job parameters */
@@ -83,4 +89,27 @@ public interface NBatchConstants {
 
     // ut only
     String P_BREAK_POINT_LAYOUTS = "breakPointLayouts";
+
+    @Getter
+    enum TblPropertyKey {
+        PRIMARY_KEY(P_PRIMARY_KEY),
+        ORDER_BY_KEY(P_ORDER_BY_KEY),
+        BUCKET_COLUMN(P_BUCKET_COLUMN),
+        BUCKET_NUM(P_BUCKET_NUM);
+
+        private final String value;
+
+        TblPropertyKey(String value) {
+            this.value = value;
+        }
+
+        public static boolean contains(String key) {
+            for (TblPropertyKey propertyKey : values()) {
+                if (propertyKey.getValue().equals(key)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
 }
diff --git 
a/src/core-metadata/src/main/java/org/apache/kylin/metadata/table/InternalTableDesc.java
 
b/src/core-metadata/src/main/java/org/apache/kylin/metadata/table/InternalTableDesc.java
index 93edb409dd..2bb0e5df6c 100644
--- 
a/src/core-metadata/src/main/java/org/apache/kylin/metadata/table/InternalTableDesc.java
+++ 
b/src/core-metadata/src/main/java/org/apache/kylin/metadata/table/InternalTableDesc.java
@@ -18,6 +18,13 @@
 
 package org.apache.kylin.metadata.table;
 
+import static 
org.apache.kylin.metadata.cube.model.NBatchConstants.P_BUCKET_COLUMN;
+import static 
org.apache.kylin.metadata.cube.model.NBatchConstants.P_BUCKET_NUM;
+import static 
org.apache.kylin.metadata.cube.model.NBatchConstants.P_ORDER_BY_KEY;
+import static 
org.apache.kylin.metadata.cube.model.NBatchConstants.P_PRELOADED_CACHE;
+import static 
org.apache.kylin.metadata.cube.model.NBatchConstants.P_PRIMARY_KEY;
+import static 
org.apache.kylin.metadata.cube.model.NBatchConstants.P_SORT_BY_PARTITION_BEFORE_SAVE;
+
 import java.io.Serializable;
 import java.util.Arrays;
 import java.util.List;
@@ -48,12 +55,6 @@ import lombok.Setter;
 @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, 
getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = 
JsonAutoDetect.Visibility.NONE, setterVisibility = 
JsonAutoDetect.Visibility.NONE)
 public class InternalTableDesc extends ATable implements Serializable {
 
-    private static final String BUCKET_COLUMN = "bucketCol";
-    private static final String BUCKET_NUM = "bucketNum";
-    private static final String PRELOADED_CACHE = "preloadedCache";
-    private static final String PRIMARY_KEY = "primaryKey";
-    private static final String SORT_BY_KEY = "sortByKey";
-    private static final String SORT_BY_PARTITION_BEFORE_SAVE = 
"sortByPartition";
     public static final int INIT_SIZE = 0;
 
     @Getter
@@ -68,6 +69,15 @@ public class InternalTableDesc extends ATable implements 
Serializable {
         StorageType(String format) {
             this.format = format;
         }
+
+        public static boolean contains(String format) {
+            for (StorageType storageType : StorageType.values()) {
+                if (storageType.name().equalsIgnoreCase(format)) {
+                    return true;
+                }
+            }
+            return false;
+        }
     }
 
     @JsonProperty("tbl_properties")
@@ -97,7 +107,6 @@ public class InternalTableDesc extends ATable implements 
Serializable {
     @JsonProperty("table_partition")
     private InternalTablePartition tablePartition;
 
-
     public InternalTableDesc(InternalTableDesc other) {
         this.project = other.project;
         this.database.setName(other.getDatabase());
@@ -150,47 +159,43 @@ public class InternalTableDesc extends ATable implements 
Serializable {
     }
 
     public void setStorageType(String storageType) {
-        if (storageType == null) {
-            this.storageType = StorageType.GLUTEN;
-        } else {
-            String storageTypeUpper = storageType.toUpperCase(Locale.ROOT);
-            this.storageType = StorageType.valueOf(storageTypeUpper);
-        }
+        String storageTypeUpper = storageType.toUpperCase(Locale.ROOT);
+        this.storageType = StorageType.valueOf(storageTypeUpper);
     }
 
     public String getBucketColumn() {
-        if (null == tblProperties.get(BUCKET_COLUMN)) {
+        if (null == tblProperties.get(P_BUCKET_COLUMN)) {
             return null;
         } else {
-            return tblProperties.get(BUCKET_COLUMN).trim();
+            return tblProperties.get(P_BUCKET_COLUMN).trim();
         }
     }
 
     public int getBucketNumber() {
-        if (null == tblProperties.get(BUCKET_NUM)) {
+        if (null == tblProperties.get(P_BUCKET_NUM)) {
             return 0;
         } else {
-            return Integer.parseInt(tblProperties.get(BUCKET_NUM).trim());
+            return Integer.parseInt(tblProperties.get(P_BUCKET_NUM).trim());
         }
     }
 
     public List<String> getPrimaryKey() {
-        return Arrays.stream(StringUtils.split(tblProperties.get(PRIMARY_KEY), 
",")).map(String::trim)
+        return 
Arrays.stream(StringUtils.split(tblProperties.get(P_PRIMARY_KEY), 
",")).map(String::trim)
                 .collect(Collectors.toList());
     }
 
-    public List<String> getSortByKey() {
-        return Arrays.stream(StringUtils.split(tblProperties.get(SORT_BY_KEY), 
",")).map(String::trim)
+    public List<String> getOrderByKey() {
+        return 
Arrays.stream(StringUtils.split(tblProperties.get(P_ORDER_BY_KEY), 
",")).map(String::trim)
                 .collect(Collectors.toList());
     }
 
     public boolean isPreloadedCacheEnable() {
-        return 
Boolean.parseBoolean(tblProperties.getOrDefault(PRELOADED_CACHE, 
KylinConfig.FALSE));
+        return 
Boolean.parseBoolean(tblProperties.getOrDefault(P_PRELOADED_CACHE, 
KylinConfig.FALSE));
     }
 
     public boolean isSortByPartitionEnabled() {
-        if (tblProperties.containsKey(SORT_BY_PARTITION_BEFORE_SAVE)) {
-            return 
Boolean.parseBoolean(tblProperties.get(SORT_BY_PARTITION_BEFORE_SAVE));
+        if (tblProperties.containsKey(P_SORT_BY_PARTITION_BEFORE_SAVE)) {
+            return 
Boolean.parseBoolean(tblProperties.get(P_SORT_BY_PARTITION_BEFORE_SAVE));
         } else {
             return 
NProjectManager.getProjectConfig(project).isInternalTableSortByPartitionEnabled();
         }
diff --git 
a/src/core-metadata/src/test/java/org/apache/kylin/metadata/table/InternalTableDescTest.java
 
b/src/core-metadata/src/test/java/org/apache/kylin/metadata/table/InternalTableDescTest.java
index b4237f94a2..9d7e732a38 100644
--- 
a/src/core-metadata/src/test/java/org/apache/kylin/metadata/table/InternalTableDescTest.java
+++ 
b/src/core-metadata/src/test/java/org/apache/kylin/metadata/table/InternalTableDescTest.java
@@ -37,7 +37,7 @@ class InternalTableDescTest {
         TableDesc originTable = 
tableMetadataManager.getTableDesc("DEFAULT.TEST_KYLIN_FACT");
         InternalTableDesc table = new InternalTableDesc(originTable);
 
-        table.setStorageType(null);
+        table.setStorageType(InternalTableDesc.StorageType.GLUTEN.name());
         Assertions.assertEquals(InternalTableDesc.StorageType.GLUTEN, 
table.getStorageType());
         table.setStorageType(InternalTableDesc.StorageType.PARQUET.name());
         Assertions.assertEquals(InternalTableDesc.StorageType.PARQUET, 
table.getStorageType());
@@ -99,4 +99,12 @@ class InternalTableDescTest {
         table.optimizeTblProperties();
         Assertions.assertTrue(table.isSortByPartitionEnabled());
     }
+
+    @Test
+    void testStorageType() {
+        String format = "clickhouse";
+        Assertions.assertFalse(InternalTableDesc.StorageType.contains(format));
+        format = "gluten";
+        Assertions.assertTrue(InternalTableDesc.StorageType.contains(format));
+    }
 }
diff --git 
a/src/data-loading-service/src/test/java/org/apache/kylin/rest/service/InternalTableServiceTest.java
 
b/src/data-loading-service/src/test/java/org/apache/kylin/rest/service/InternalTableServiceTest.java
index b22e2c7d57..3cda8cac17 100644
--- 
a/src/data-loading-service/src/test/java/org/apache/kylin/rest/service/InternalTableServiceTest.java
+++ 
b/src/data-loading-service/src/test/java/org/apache/kylin/rest/service/InternalTableServiceTest.java
@@ -19,6 +19,8 @@
 package org.apache.kylin.rest.service;
 
 import static org.apache.kylin.common.exception.QueryErrorCode.EMPTY_TABLE;
+import static 
org.apache.kylin.metadata.cube.model.NBatchConstants.P_ORDER_BY_KEY;
+import static 
org.apache.kylin.metadata.cube.model.NBatchConstants.P_PRIMARY_KEY;
 import static org.awaitility.Awaitility.await;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -30,7 +32,9 @@ import static org.mockito.Mockito.when;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -65,6 +69,7 @@ import org.apache.kylin.metadata.table.InternalTableManager;
 import org.apache.kylin.metadata.table.InternalTablePartition;
 import org.apache.kylin.metadata.table.InternalTablePartitionDetail;
 import org.apache.kylin.rest.constant.Constant;
+import org.apache.kylin.rest.request.InternalTableRequest;
 import org.apache.kylin.rest.response.InternalTableDescResponse;
 import org.apache.kylin.rest.response.InternalTableLoadingJobResponse;
 import org.apache.kylin.rest.util.AclEvaluate;
@@ -1157,4 +1162,54 @@ public class InternalTableServiceTest extends 
AbstractTestCase {
         Assertions.assertEquals(kylinException.getErrorCode(), 
ServerErrorCode.INTERNAL_TABLE_NOT_EXIST.toErrorCode());
         Assertions.assertFalse(internalTableFolder.exists());
     }
+
+    @Test
+    void testValidate() {
+        // 1. Unsupported StorageType
+        InternalTableRequest request = new InternalTableRequest();
+        request.setStorageType("clickhouse");
+        request.setTblProperties(Collections.emptyMap());
+        Assertions.assertThrows(KylinException.class, () -> 
InternalTableService.validate(request));
+
+        // 2. Only check when storageType is GLUTEN
+        request.setStorageType(InternalTableDesc.StorageType.PARQUET.name());
+        InternalTableService.validate(request);
+
+        // 3. Unsupported tbl_properties parameter
+        Map<String, String> tblProperties = new LinkedHashMap<>();
+        tblProperties.put("checkFalse", null);
+        request.setStorageType(InternalTableDesc.StorageType.GLUTEN.name());
+        request.setTblProperties(tblProperties);
+        Assertions.assertThrows(KylinException.class, () -> 
InternalTableService.validate(request));
+
+        // 4. Skip check for cases that do not contain PRIMARY_KEY
+        tblProperties.clear();
+        tblProperties.put(P_ORDER_BY_KEY, null);
+        InternalTableService.validate(request);
+
+        // 5. Set PRIMARY_KEY without ORDER_BY_KEY
+        tblProperties.clear();
+        tblProperties.put(P_PRIMARY_KEY, "primary1");
+        Assertions.assertThrows(KylinException.class, () -> 
InternalTableService.validate(request));
+
+        // 6. Skip check for cases that set ORDER_BY_KEY without PRIMARY_KEY
+        tblProperties.clear();
+        tblProperties.put(P_ORDER_BY_KEY, "order1");
+        InternalTableService.validate(request);
+
+        // 7. Test primaryKey is a prefix subset of orderByKey
+        tblProperties.clear();
+        tblProperties.put(P_PRIMARY_KEY, "a,b");
+        tblProperties.put(P_ORDER_BY_KEY, "a,b,c");
+        InternalTableService.validate(request);
+        request.setStorageType(null);
+        InternalTableService.validate(request);
+
+        // 8. Test primaryKey is not a prefix subset of orderByKey
+        tblProperties.clear();
+        request.setStorageType("");
+        tblProperties.put(P_PRIMARY_KEY, "a,c");
+        tblProperties.put(P_ORDER_BY_KEY, "a,b,c");
+        Assertions.assertThrows(KylinException.class, () -> 
InternalTableService.validate(request));
+    }
 }
diff --git 
a/src/datasource-service/src/main/java/org/apache/kylin/rest/service/InternalTableService.java
 
b/src/datasource-service/src/main/java/org/apache/kylin/rest/service/InternalTableService.java
index 5cd095a032..4496ac5a0f 100644
--- 
a/src/datasource-service/src/main/java/org/apache/kylin/rest/service/InternalTableService.java
+++ 
b/src/datasource-service/src/main/java/org/apache/kylin/rest/service/InternalTableService.java
@@ -24,6 +24,10 @@ import static 
org.apache.kylin.common.exception.ServerErrorCode.INTERNAL_TABLE_N
 import static 
org.apache.kylin.common.exception.ServerErrorCode.INTERNAL_TABLE_RELOAD_ERROR;
 import static 
org.apache.kylin.common.exception.ServerErrorCode.INVALID_INTERNAL_TABLE_PARAMETER;
 import static 
org.apache.kylin.common.exception.ServerErrorCode.TABLE_NOT_EXIST;
+import static org.apache.kylin.common.util.ArrayUtils.isPrefixSubset;
+import static org.apache.kylin.common.util.StringHelper.splitAndTrim;
+import static 
org.apache.kylin.metadata.cube.model.NBatchConstants.P_ORDER_BY_KEY;
+import static 
org.apache.kylin.metadata.cube.model.NBatchConstants.P_PRIMARY_KEY;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -41,6 +45,7 @@ import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.kylin.common.KylinConfig;
 import org.apache.kylin.common.exception.KylinException;
+import org.apache.kylin.common.msg.Message;
 import org.apache.kylin.common.msg.MsgPicker;
 import org.apache.kylin.common.util.HadoopUtil;
 import org.apache.kylin.engine.spark.builder.InternalTableLoader;
@@ -48,6 +53,7 @@ import org.apache.kylin.guava30.shaded.common.collect.Lists;
 import org.apache.kylin.guava30.shaded.common.collect.Maps;
 import org.apache.kylin.job.execution.ExecutableManager;
 import org.apache.kylin.job.execution.JobTypeEnum;
+import org.apache.kylin.metadata.cube.model.NBatchConstants;
 import org.apache.kylin.metadata.model.ColumnDesc;
 import org.apache.kylin.metadata.model.NTableMetadataManager;
 import org.apache.kylin.metadata.model.TableDesc;
@@ -56,6 +62,7 @@ import org.apache.kylin.metadata.table.InternalTableDesc;
 import org.apache.kylin.metadata.table.InternalTableManager;
 import org.apache.kylin.metadata.table.InternalTablePartition;
 import org.apache.kylin.metadata.table.InternalTablePartitionDetail;
+import org.apache.kylin.rest.request.InternalTableRequest;
 import org.apache.kylin.rest.response.InternalTableDescResponse;
 import org.apache.kylin.rest.response.InternalTableLoadingJobResponse;
 import org.apache.kylin.rest.util.AclEvaluate;
@@ -438,4 +445,65 @@ public class InternalTableService extends BasicService {
         return internalTableDesc.getTablePartition().getPartitionDetails();
     }
 
+    public static void validate(InternalTableRequest request) {
+        String storageType = request.getStorageType();
+        Message msg = MsgPicker.getMsg();
+        // 1. Validate the legality of storageType
+        if (storageType == null || storageType.isEmpty()) {
+            
request.setStorageType(InternalTableDesc.StorageType.GLUTEN.name());
+            storageType = request.getStorageType();
+        }
+        if (!InternalTableDesc.StorageType.contains(storageType)) {
+            throw new KylinException(INVALID_INTERNAL_TABLE_PARAMETER,
+                    String.format(Locale.ROOT, 
msg.getInternalTableStorageTypeFailed(), storageType));
+        }
+
+        // 2. Skip subsequent checks if the type is not GLUTEN
+        // TODO check other storageType
+        if 
(!storageType.equalsIgnoreCase(InternalTableDesc.StorageType.GLUTEN.name())) {
+            return;
+        }
+
+        // 3. Extract and validate key-value pairs
+        Map<String, String> tblProperties = request.getTblProperties();
+        String primaryKey = null;
+        String orderByKey = null;
+        // 3.1 Iterate to check the validity of keys and extract target key 
values
+        for (Map.Entry<String, String> entry : tblProperties.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            // Check if the key is valid
+            if (!NBatchConstants.TblPropertyKey.contains(key)) {
+                throw new KylinException(INVALID_INTERNAL_TABLE_PARAMETER,
+                        String.format(Locale.ROOT, 
msg.getInternalTableTblPropertiesFailed(), key));
+            }
+            // Extract target key values
+            if (key.equals(P_PRIMARY_KEY)) {
+                primaryKey = value;
+            }
+            if (key.equals(P_ORDER_BY_KEY)) {
+                orderByKey = value;
+            }
+        }
+        // 4. Validate the existence of the primaryKey
+        if (primaryKey == null) {
+            return;
+        }
+        // 5. Ensure that the orderByKey is set
+        if (orderByKey == null) {
+            throw new KylinException(INVALID_INTERNAL_TABLE_PARAMETER,
+                    "When " + P_PRIMARY_KEY + " is set, " + P_ORDER_BY_KEY + " 
must be set.");
+        }
+        // 6. Parse the field list
+        List<String> primaryColumns = Arrays.asList(splitAndTrim(primaryKey, 
","));
+        List<String> orderByColumns = Arrays.asList(splitAndTrim((orderByKey), 
","));
+
+        // 7. Verify that the primaryKey is a prefix of the orderByKey
+        if (!isPrefixSubset(primaryColumns, orderByColumns)) {
+            throw new KylinException(INVALID_INTERNAL_TABLE_PARAMETER,
+                    P_PRIMARY_KEY + " must be a prefix subset of " + 
P_ORDER_BY_KEY + ". Example: If " + P_ORDER_BY_KEY
+                            + " is 'a,b,c', " + P_PRIMARY_KEY + " can be 'a', 
'a,b' or 'a,b,c'");
+        }
+    }
+
 }
diff --git 
a/src/metadata-server/src/main/java/org/apache/kylin/rest/controller/InternalTableController.java
 
b/src/metadata-server/src/main/java/org/apache/kylin/rest/controller/InternalTableController.java
index 1f5d87fa8b..595f0723fd 100644
--- 
a/src/metadata-server/src/main/java/org/apache/kylin/rest/controller/InternalTableController.java
+++ 
b/src/metadata-server/src/main/java/org/apache/kylin/rest/controller/InternalTableController.java
@@ -71,6 +71,7 @@ public class InternalTableController extends NBasicController 
{
         if (StringUtils.isBlank(table) || StringUtils.isBlank(database)) {
             throw new KylinException(EMPTY_PARAMETER, "Table or database can 
not be null, please check again.");
         }
+        InternalTableService.validate(request);
         internalTableService.createInternalTable(project, table, database, 
request.getPartitionCols(),
                 request.getDatePartitionFormat(), request.getTblProperties(), 
request.getStorageType());
         return new EnvelopeResponse<>(KylinException.CODE_SUCCESS, "", "");
@@ -150,6 +151,7 @@ public class InternalTableController extends 
NBasicController {
         if (table.isEmpty()) {
             throw new KylinException(INVALID_TABLE_NAME, 
MsgPicker.getMsg().getTableNameCannotEmpty());
         }
+        InternalTableService.validate(request);
         internalTableService.updateInternalTable(project, table, database, 
request.getPartitionCols(),
                 request.getDatePartitionFormat(), request.getTblProperties(), 
request.getStorageType());
         return new EnvelopeResponse<>(KylinException.CODE_SUCCESS, null, "");

Reply via email to