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 <[email protected]>
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, "");