From ffd8931cfe0e82664acda58020973edf4762fc42 Mon Sep 17 00:00:00 2001 From: zclllhhjj Date: Mon, 19 Aug 2024 19:07:47 +0800 Subject: [PATCH] [Enhancement](partition) Forbid create table with null partition item which relative column is not null (#39449) ## Proposed changes Issue Number: close #xxx before: ```sql CREATE TABLE `test_null` ( `k0` BIGINT NOT NULL, `k1` BIGINT NOT NULL ) partition by list (k0, k1) ( PARTITION `pX` values in ((NULL, 1)) ) PROPERTIES ( "replication_allocation" = "tag.location.default: 1" ); ``` may core in local exchange for inserting. now: ```sql mysql [test]>CREATE TABLE `test_null` ( -> `k0` BIGINT NOT NULL, -> `k1` BIGINT NOT NULL -> ) -> partition by list (k0, k1) ( -> PARTITION `pX` values in ((NULL, 1)) -> ) -> PROPERTIES ( -> "replication_allocation" = "tag.location.default: 1" -> ); ERROR 1105 (HY000): errCode = 2, detailMessage = errCode = 2, detailMessage = Can't have null partition is for NOT NULL partition column in partition expr's index 0 ``` --- .../doris/datasource/InternalCatalog.java | 130 ++++++++++++++---- .../plans/commands/CreateTableCommand.java | 7 +- .../apache/doris/catalog/CreateTableTest.java | 29 ++-- .../hive/HiveDDLAndDMLPlanTest.java | 2 +- .../trees/plans/CreateTableCommandTest.java | 49 +++---- .../partition_p0/test_null_partition.groovy | 83 +++++++++++ 6 files changed, 227 insertions(+), 73 deletions(-) create mode 100644 regression-test/suites/partition_p0/test_null_partition.groovy diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java index 61d783fa6a63ed..71c71fb8ec780d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java @@ -46,7 +46,9 @@ import org.apache.doris.analysis.MultiPartitionDesc; import org.apache.doris.analysis.PartitionDesc; import org.apache.doris.analysis.PartitionKeyDesc; +import org.apache.doris.analysis.PartitionKeyDesc.PartitionKeyValueType; import org.apache.doris.analysis.PartitionNames; +import org.apache.doris.analysis.PartitionValue; import org.apache.doris.analysis.QueryStmt; import org.apache.doris.analysis.RecoverDbStmt; import org.apache.doris.analysis.RecoverPartitionStmt; @@ -2252,6 +2254,8 @@ public void checkAvailableCapacity(Database db) throws DdlException { db.checkQuota(); } + // below are private functions used by createOlapTable + private Type getChildTypeByName(String name, CreateTableStmt stmt) throws AnalysisException { List columns = stmt.getColumns(); @@ -2263,6 +2267,98 @@ private Type getChildTypeByName(String name, CreateTableStmt stmt) throw new AnalysisException("Cannot find column `" + name + "` in table's columns"); } + private boolean findAllowNullforSlotRef(List baseSchema, SlotRef slot) throws AnalysisException { + for (Column col : baseSchema) { + if (col.nameEquals(slot.getColumnName(), true)) { + return col.isAllowNull(); + } + } + throw new AnalysisException("Unknown partition column name:" + slot.getColumnName()); + } + + private void checkNullityEqual(ArrayList partitionSlotNullables, List item) + throws AnalysisException { + // for MAX_VALUE or somethings + if (item == null) { + return; + } + for (int i = 0; i < item.size(); i++) { + try { + if (!partitionSlotNullables.get(i) && item.get(i).isNullPartition()) { + throw new AnalysisException("Can't have null partition is for NOT NULL partition " + + "column in partition expr's index " + i); + } + } catch (IndexOutOfBoundsException e) { + throw new AnalysisException("partition item's size out of partition columns: " + e.getMessage()); + } + } + } + + private void checkPartitionNullity(List baseSchema, PartitionDesc partitionDesc, + SinglePartitionDesc partition) + throws AnalysisException { + // in creating OlapTable, expr.desc is null. so we should find the column ourself. + ArrayList partitionExprs = partitionDesc.getPartitionExprs(); + ArrayList partitionSlotNullables = new ArrayList(); + for (Expr expr : partitionExprs) { + if (expr instanceof SlotRef) { + partitionSlotNullables.add(findAllowNullforSlotRef(baseSchema, (SlotRef) expr)); + } else if (expr instanceof FunctionCallExpr) { + partitionSlotNullables.add(Expr.isNullable(((FunctionCallExpr) expr).getFn(), expr.getChildren())); + } else { + throw new AnalysisException("Unknown partition expr type:" + expr.getExprName()); + } + } + + if (partition.getPartitionKeyDesc().getPartitionType() == PartitionKeyValueType.IN) { + List> inValues = partition.getPartitionKeyDesc().getInValues(); + for (List item : inValues) { + checkNullityEqual(partitionSlotNullables, item); + } + } else if (partition.getPartitionKeyDesc().getPartitionType() == PartitionKeyValueType.LESS_THAN) { + // only upper + List upperValues = partition.getPartitionKeyDesc().getUpperValues(); + checkNullityEqual(partitionSlotNullables, upperValues); + } else { + // fixed. upper and lower + List lowerValues = partition.getPartitionKeyDesc().getLowerValues(); + List upperValues = partition.getPartitionKeyDesc().getUpperValues(); + checkNullityEqual(partitionSlotNullables, lowerValues); + checkNullityEqual(partitionSlotNullables, upperValues); + } + } + + private void checkLegalityofPartitionExprs(CreateTableStmt stmt, PartitionDesc partitionDesc) + throws AnalysisException { + for (Expr expr : partitionDesc.getPartitionExprs()) { + if (expr != null && expr instanceof FunctionCallExpr) { // test them + FunctionCallExpr func = (FunctionCallExpr) expr; + ArrayList children = func.getChildren(); + Type[] childTypes = new Type[children.size()]; + for (int i = 0; i < children.size(); i++) { + if (children.get(i) instanceof LiteralExpr) { + childTypes[i] = children.get(i).getType(); + } else if (children.get(i) instanceof SlotRef) { + childTypes[i] = getChildTypeByName(children.get(i).getExprName(), stmt); + } else { + throw new AnalysisException(String.format( + "partition expr %s has unrecognized parameter in slot %d", func.getExprName(), i)); + } + } + Function fn = null; + try { + fn = func.getBuiltinFunction(func.getFnName().getFunction(), childTypes, + Function.CompareMode.IS_INDISTINGUISHABLE); // only for test + } catch (Exception e) { + throw new AnalysisException("partition expr " + func.getExprName() + " is illegal!"); + } + if (fn == null) { + throw new AnalysisException("partition expr " + func.getExprName() + " is illegal!"); + } + } + } + } + // Create olap table and related base index synchronously. private boolean createOlapTable(Database db, CreateTableStmt stmt) throws UserException { String tableName = stmt.getTableName(); @@ -2308,43 +2404,21 @@ private boolean createOlapTable(Database db, CreateTableStmt stmt) throws UserEx // create partition info PartitionDesc partitionDesc = stmt.getPartitionDesc(); - // check legality of partiton exprs ConnectContext ctx = ConnectContext.get(); Env env = Env.getCurrentEnv(); + + // check legality of partiton exprs. if (ctx != null && env != null && partitionDesc != null && partitionDesc.getPartitionExprs() != null) { - for (Expr expr : partitionDesc.getPartitionExprs()) { - if (expr != null && expr instanceof FunctionCallExpr) { // test them - FunctionCallExpr func = (FunctionCallExpr) expr; - ArrayList children = func.getChildren(); - Type[] childTypes = new Type[children.size()]; - for (int i = 0; i < children.size(); i++) { - if (children.get(i) instanceof LiteralExpr) { - childTypes[i] = children.get(i).getType(); - } else if (children.get(i) instanceof SlotRef) { - childTypes[i] = getChildTypeByName(children.get(i).getExprName(), stmt); - } else { - throw new AnalysisException(String.format( - "partition expr %s has unrecognized parameter in slot %d", func.getExprName(), i)); - } - } - Function fn = null; - try { - fn = func.getBuiltinFunction(func.getFnName().getFunction(), childTypes, - Function.CompareMode.IS_INDISTINGUISHABLE); // only for test - } catch (Exception e) { - throw new AnalysisException("partition expr " + func.getExprName() + " is illegal!"); - } - if (fn == null) { - throw new AnalysisException("partition expr " + func.getExprName() + " is illegal!"); - } - } - } + checkLegalityofPartitionExprs(stmt, partitionDesc); } PartitionInfo partitionInfo = null; Map partitionNameToId = Maps.newHashMap(); if (partitionDesc != null) { for (SinglePartitionDesc desc : partitionDesc.getSinglePartitionDescs()) { + // check legality of nullity of partition items. + checkPartitionNullity(baseSchema, partitionDesc, desc); + long partitionId = idGeneratorBuffer.getNextId(); partitionNameToId.put(desc.getPartitionName(), partitionId); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java index ad39513c83d80a..891585d8e06724 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java @@ -98,11 +98,8 @@ public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { LOG.debug("Nereids start to execute the create table command, query id: {}, tableName: {}", ctx.queryId(), createTableInfo.getTableName()); } - try { - Env.getCurrentEnv().createTable(createTableStmt); - } catch (Exception e) { - throw new AnalysisException(e.getMessage(), e.getCause()); - } + + Env.getCurrentEnv().createTable(createTableStmt); return; } LogicalPlan query = ctasQuery.get(); diff --git a/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableTest.java b/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableTest.java index bdd5a66902c6ae..5f66e903e3abfd 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableTest.java @@ -372,17 +372,16 @@ public void testAbnormal() throws DdlException, ConfigException { // single partition column with multi keys ExceptionChecker - .expectThrowsWithMsg(IllegalArgumentException.class, "partition key desc list size[2] is not equal to partition column size[1]", - () -> createTable("create table test.tbl10\n" - + "(k1 int not null, k2 varchar(128), k3 int, v1 int, v2 int)\n" - + "partition by list(k1)\n" - + "(\n" - + "partition p1 values in (\"1\", \"3\", \"5\"),\n" - + "partition p2 values in (\"2\", \"4\", \"6\"),\n" - + "partition p3 values in ((\"7\", \"8\"))\n" - + ")\n" - + "distributed by hash(k2) buckets 1\n" - + "properties('replication_num' = '1');")); + .expectThrowsWithMsg(AnalysisException.class, + "partition item's size out of partition columns: Index 1 out of bounds for length 1", + () -> createTable("create table test.tbl10\n" + + "(k1 int not null, k2 varchar(128), k3 int, v1 int, v2 int)\n" + + "partition by list(k1)\n" + "(\n" + + "partition p1 values in (\"1\", \"3\", \"5\"),\n" + + "partition p2 values in (\"2\", \"4\", \"6\"),\n" + + "partition p3 values in ((\"7\", \"8\"))\n" + ")\n" + + "distributed by hash(k2) buckets 1\n" + + "properties('replication_num' = '1');")); // multi partition columns with single key ExceptionChecker @@ -399,7 +398,7 @@ public void testAbnormal() throws DdlException, ConfigException { // multi partition columns with multi keys ExceptionChecker - .expectThrowsWithMsg(IllegalArgumentException.class, "partition key desc list size[3] is not equal to partition column size[2]", + .expectThrowsWithMsg(AnalysisException.class, "partition item's size out of partition columns: Index 2 out of bounds for length 2", () -> createTable("create table test.tbl12\n" + "(k1 int not null, k2 varchar(128) not null, k3 int, v1 int, v2 int)\n" + "partition by list(k1, k2)\n" @@ -922,8 +921,8 @@ public void testCreateTableWithMinLoadReplicaNum() throws Exception { @Test public void testCreateTableWithNerieds() throws Exception { - ExceptionChecker.expectThrowsWithMsg(org.apache.doris.nereids.exceptions.AnalysisException.class, - "Failed to check min load replica num", + ExceptionChecker.expectThrowsWithMsg(org.apache.doris.common.DdlException.class, + "Failed to check min load replica num", () -> createTable("create table test.tbl_min_load_replica_num_2_nereids\n" + "(k1 int, k2 int)\n" + "duplicate key(k1)\n" @@ -964,7 +963,7 @@ public void testCreateTableWithNerieds() throws Exception { + "distributed by hash(k1) buckets 10", true)); createDatabaseWithSql("create database db2 properties('replication_num' = '4')"); - ExceptionChecker.expectThrowsWithMsg(org.apache.doris.nereids.exceptions.AnalysisException.class, + ExceptionChecker.expectThrowsWithMsg(DdlException.class, "replication num should be less than the number of available backends. " + "replication num is 4, available backend num is 3", () -> createTable("create table db2.tbl_4_replica\n" diff --git a/fe/fe-core/src/test/java/org/apache/doris/datasource/hive/HiveDDLAndDMLPlanTest.java b/fe/fe-core/src/test/java/org/apache/doris/datasource/hive/HiveDDLAndDMLPlanTest.java index a40a4fed385520..ab4e5fd0fc557c 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/datasource/hive/HiveDDLAndDMLPlanTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/datasource/hive/HiveDDLAndDMLPlanTest.java @@ -339,7 +339,7 @@ public void testCreateAndDropWithSql() throws Exception { + "PROPERTIES (\n" + " 'location'='hdfs://loc/db/tbl',\n" + " 'file_format'='orc')"; - ExceptionChecker.expectThrowsWithMsg(org.apache.doris.nereids.exceptions.AnalysisException.class, + ExceptionChecker.expectThrowsWithMsg(org.apache.doris.common.UserException.class, "errCode = 2, detailMessage = errCode = 2," + " detailMessage = Create hive bucket table need set enable_create_hive_bucket_table to true", () -> createTable(createBucketedTableErr, true)); diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/CreateTableCommandTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/CreateTableCommandTest.java index dc45e3de0fe8c1..741faea4a1342c 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/CreateTableCommandTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/CreateTableCommandTest.java @@ -241,12 +241,12 @@ public void testNormal() throws DdlException, ConfigException { @Test public void testAbnormal() throws ConfigException { - checkThrow(AnalysisException.class, + checkThrow(org.apache.doris.common.DdlException.class, "Unknown properties: {aa=bb}", () -> createTable("create table test.atbl1\n" + "(k1 int, k2 float)\n" + "duplicate key(k1)\n" + "distributed by hash(k1) buckets 1\n" + "properties('replication_num' = '1','aa'='bb'); ")); - checkThrow(AnalysisException.class, + checkThrow(org.apache.doris.common.DdlException.class, "Floating point type should not be used in distribution column", () -> createTable("create table test.atbl1\n" + "(k1 int, k2 float)\n" + "duplicate key(k1)\n" + "distributed by hash(k2) buckets 1\n" + "properties('replication_num' = '1'); ")); @@ -257,18 +257,18 @@ public void testAbnormal() throws ConfigException { + "partition by range(k3)\n" + "(partition p1 values less than(\"10\"))\n" + "distributed by hash(k2) buckets 1\n" + "properties('replication_num' = '1'); ")); - checkThrow(AnalysisException.class, + checkThrow(org.apache.doris.common.DdlException.class, "Varchar should not in the middle of short keys", () -> createTable("create table test.atbl3\n" + "(k1 varchar(40), k2 int, k3 int)\n" + "duplicate key(k1, k2, k3)\n" + "distributed by hash(k1) buckets 1\n" + "properties('replication_num' = '1', 'short_key' = '3');")); - checkThrow(AnalysisException.class, "Short key is too large. should less than: 3", + checkThrow(org.apache.doris.common.DdlException.class, "Short key is too large. should less than: 3", () -> createTable("create table test.atbl4\n" + "(k1 int, k2 int, k3 int)\n" + "duplicate key(k1, k2, k3)\n" + "distributed by hash(k1) buckets 1\n" + "properties('replication_num' = '1', 'short_key' = '4');")); - checkThrow(AnalysisException.class, + checkThrow(org.apache.doris.common.DdlException.class, "replication num should be less than the number of available backends. replication num is 3, available backend num is 1", () -> createTable("create table test.atbl5\n" + "(k1 int, k2 int, k3 int)\n" + "duplicate key(k1, k2, k3)\n" + "distributed by hash(k1) buckets 1\n" @@ -278,48 +278,49 @@ public void testAbnormal() throws ConfigException { () -> createTable("create table test.atbl6\n" + "(k1 int, k2 int)\n" + "duplicate key(k1)\n" + "distributed by hash(k2) buckets 1\n" + "properties('replication_num' = '1'); ")); - checkThrow(AnalysisException.class, "Table 'atbl6' already exists", + checkThrow(org.apache.doris.common.DdlException.class, "Table 'atbl6' already exists", () -> createTable("create table test.atbl6\n" + "(k1 int, k2 int, k3 int)\n" + "duplicate key(k1, k2, k3)\n" + "distributed by hash(k1) buckets 1\n" + "properties('replication_num' = '1');")); ConfigBase.setMutableConfig("disable_storage_medium_check", "false"); - checkThrow(AnalysisException.class, + checkThrow(org.apache.doris.common.DdlException.class, "Failed to find enough backend, please check the replication num,replication tag and storage medium.\n" + "Create failed replications:\n" + "replication tag: {\"location\" : \"default\"}, replication num: 1, storage medium: SSD", () -> createTable("create table test.tb7(key1 int, key2 varchar(10)) distributed by hash(key1) \n" + "buckets 1 properties('replication_num' = '1', 'storage_medium' = 'ssd');")); - checkThrow(AnalysisException.class, "sequence column only support UNIQUE_KEYS", + checkThrow(org.apache.doris.common.DdlException.class, "sequence column only support UNIQUE_KEYS", () -> createTable("create table test.atbl8\n" + "(k1 varchar(40), k2 int, v1 int sum)\n" + "aggregate key(k1, k2)\n" + "partition by range(k2)\n" + "(partition p1 values less than(\"10\"))\n" + "distributed by hash(k2) buckets 1\n" + "properties('replication_num' = '1',\n" + "'function_column.sequence_type' = 'int');")); - checkThrow(AnalysisException.class, "sequence type only support integer types and date types", + checkThrow(org.apache.doris.common.DdlException.class, + "sequence type only support integer types and date types", () -> createTable("create table test.atbl8\n" + "(k1 varchar(40), k2 int, v1 int)\n" + "unique key(k1, k2)\n" + "partition by range(k2)\n" + "(partition p1 values less than(\"10\"))\n" + "distributed by hash(k2) buckets 1\n" + "properties('replication_num' = '1',\n" + "'function_column.sequence_type' = 'double');")); - checkThrow(AnalysisException.class, "The sequence_col and sequence_type cannot be set at the same time", + checkThrow(org.apache.doris.common.DdlException.class, "The sequence_col and sequence_type cannot be set at the same time", () -> createTable("create table test.atbl8\n" + "(k1 varchar(40), k2 int, v1 int)\n" + "unique key(k1, k2)\n" + "partition by range(k2)\n" + "(partition p1 values less than(\"10\"))\n" + "distributed by hash(k2) buckets 1\n" + "properties('replication_num' = '1',\n" + "'function_column.sequence_type' = 'int', 'function_column.sequence_col' = 'v1');")); - checkThrow(AnalysisException.class, "The specified sequence column[v3] not exists", + checkThrow(org.apache.doris.common.DdlException.class, "The specified sequence column[v3] not exists", () -> createTable("create table test.atbl8\n" + "(k1 varchar(40), k2 int, v1 int)\n" + "unique key(k1, k2)\n" + "partition by range(k2)\n" + "(partition p1 values less than(\"10\"))\n" + "distributed by hash(k2) buckets 1\n" + "properties('replication_num' = '1',\n" + "'function_column.sequence_col' = 'v3');")); - checkThrow(AnalysisException.class, "Sequence type only support integer types and date types", + checkThrow(org.apache.doris.common.DdlException.class, "Sequence type only support integer types and date types", () -> createTable("create table test.atbl8\n" + "(k1 varchar(40), k2 int, v1 int)\n" + "unique key(k1, k2)\n" + "partition by range(k2)\n" + "(partition p1 values less than(\"10\"))\n" @@ -341,7 +342,7 @@ public void testAbnormal() throws ConfigException { + "properties('replication_num' = '1');")); // single partition column with multi keys - checkThrow(AnalysisException.class, + checkThrow(org.apache.doris.common.AnalysisException.class, "partition key desc list size[2] is not equal to partition column size[1]", () -> createTable("create table test.tbl10\n" + "(k1 int not null, k2 varchar(128), k3 int, v1 int, v2 int)\n" @@ -355,7 +356,7 @@ public void testAbnormal() throws ConfigException { + "properties('replication_num' = '1');")); // multi partition columns with single key - checkThrow(AnalysisException.class, + checkThrow(IllegalArgumentException.class, "partition key desc list size[1] is not equal to partition column size[2]", () -> createTable("create table test.tbl11\n" + "(k1 int not null, k2 varchar(128) not null, k3 int, v1 int, v2 int)\n" @@ -368,7 +369,7 @@ public void testAbnormal() throws ConfigException { + "properties('replication_num' = '1');")); // multi partition columns with multi keys - checkThrow(AnalysisException.class, + checkThrow(org.apache.doris.common.AnalysisException.class, "partition key desc list size[3] is not equal to partition column size[2]", () -> createTable("create table test.tbl12\n" + "(k1 int not null, k2 varchar(128) not null, k3 int, v1 int, v2 int)\n" @@ -453,7 +454,7 @@ public void testAbnormal() throws ConfigException { + "PROPERTIES(\"replication_num\" = \"1\");")); // range: partition content != partition key type - checkThrow(AnalysisException.class, "Invalid number format: beijing", + checkThrow(org.apache.doris.common.DdlException.class, "Invalid number format: beijing", () -> createTable("CREATE TABLE test.tbl17 (\n" + " k1 int, k2 varchar(128), k3 int, v1 int, v2 int\n" + ")\n" @@ -466,7 +467,7 @@ public void testAbnormal() throws ConfigException { + "PROPERTIES(\"replication_num\" = \"1\");")); // list: partition content != partition key type - checkThrow(AnalysisException.class, "Invalid number format: beijing", + checkThrow(org.apache.doris.common.DdlException.class, "Invalid number format: beijing", () -> createTable("CREATE TABLE test.tbl18 (\n" + " k1 int not null, k2 varchar(128), k3 int, v1 int, v2 int\n" + ")\n" @@ -482,7 +483,7 @@ public void testAbnormal() throws ConfigException { * dynamic partition table */ // list partition with dynamic properties - checkThrow(AnalysisException.class, "Only support dynamic partition properties on range partition table", + checkThrow(org.apache.doris.common.DdlException.class, "Only support dynamic partition properties on range partition table", () -> createTable("CREATE TABLE test.tbl19\n" + "(\n" + " k1 DATE not null\n" @@ -500,7 +501,7 @@ public void testAbnormal() throws ConfigException { + ");\n")); // no partition table with dynamic properties - checkThrow(AnalysisException.class, "Only support dynamic partition properties on range partition table", + checkThrow(org.apache.doris.common.DdlException.class, "Only support dynamic partition properties on range partition table", () -> createTable("CREATE TABLE test.tbl20\n" + "(\n" + " k1 DATE\n" @@ -558,7 +559,7 @@ public void testZOrderTable() { + " 'data_sort.sort_type' = 'lexical');")); // create z-order sort table, default col_num - checkThrow(AnalysisException.class, "only support lexical method now!", + checkThrow(org.apache.doris.common.AnalysisException.class, "only support lexical method now!", () -> createTable( "create table test.zorder_tbl2\n" + "(k1 varchar(40), k2 int, k3 int)\n" + "duplicate key(k1, k2, k3)\n" @@ -567,7 +568,7 @@ public void testZOrderTable() { + " 'data_sort.sort_type' = 'zorder');")); // create z-order sort table, define sort_col_num - checkThrow(AnalysisException.class, "only support lexical method now!", + checkThrow(org.apache.doris.common.AnalysisException.class, "only support lexical method now!", () -> createTable( "create table test.zorder_tbl3\n" + "(k1 varchar(40), k2 int, k3 int)\n" + "duplicate key(k1, k2, k3)\n" @@ -576,7 +577,7 @@ public void testZOrderTable() { + " 'data_sort.sort_type' = 'zorder'," + " 'data_sort.col_num' = '2');")); // create z-order sort table, only 1 sort column - checkThrow(AnalysisException.class, "only support lexical method now!", + checkThrow(org.apache.doris.common.AnalysisException.class, "only support lexical method now!", () -> createTable("create table test.zorder_tbl4\n" + "(k1 varchar(40), k2 int, k3 int)\n" + "duplicate key(k1, k2, k3)\n" + "partition by range(k2)\n" + "(partition p1 values less than(\"10\"))\n" @@ -584,7 +585,7 @@ public void testZOrderTable() { + " 'data_sort.sort_type' = 'zorder'," + " 'data_sort.col_num' = '1');")); // create z-order sort table, sort column is empty - checkThrow(AnalysisException.class, "only support lexical method now!", + checkThrow(org.apache.doris.common.AnalysisException.class, "only support lexical method now!", () -> createTable("create table test.zorder_tbl4\n" + "(k1 varchar(40), k2 int, k3 int)\n" + "duplicate key(k1, k2, k3)\n" + "partition by range(k2)\n" + "(partition p1 values less than(\"10\"))\n" @@ -691,7 +692,7 @@ public void testCreateTableWithStructType() { @Test public void testCreateTableWithInMemory() { - checkThrow(AnalysisException.class, "Not support set 'in_memory'='true' now!", + checkThrow(org.apache.doris.common.AnalysisException.class, "Not support set 'in_memory'='true' now!", () -> createTable("create table test.test_inmemory(k1 INT, k2 INT) duplicate key (k1) " + "distributed by hash(k1) buckets 1 properties('replication_num' = '1','in_memory'='true');")); } diff --git a/regression-test/suites/partition_p0/test_null_partition.groovy b/regression-test/suites/partition_p0/test_null_partition.groovy new file mode 100644 index 00000000000000..2563b374d381df --- /dev/null +++ b/regression-test/suites/partition_p0/test_null_partition.groovy @@ -0,0 +1,83 @@ +// 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. + +suite("test_null_partition") { + sql "set allow_partition_column_nullable = true;" + + sql " drop table if exists test_null " + test { + sql """ + CREATE TABLE `test_null` ( + `k0` BIGINT NOT NULL, + `k1` BIGINT NOT NULL + ) + partition by list (k0, k1) ( + PARTITION `pX` values in ((NULL, 1)) + ) + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + ); + """ + exception "Can't have null partition is for NOT NULL partition column in partition expr's index 0" + } + + test { + sql """ + CREATE TABLE `test_null` ( + `k0` BIGINT NOT NULL, + `k1` BIGINT NOT NULL + ) + partition by list (k0, k1) ( + PARTITION `pX` values in ((1, 2), (1, NULL)) + ) + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + ); + """ + exception "Can't have null partition is for NOT NULL partition column in partition expr's index 1" + } + + sql " drop table if exists OK " + sql """ + CREATE TABLE `OK` ( + `k0` BIGINT NULL, + `k1` BIGINT NOT NULL + ) + partition by list (k0, k1) ( + PARTITION `pX` values in ((NULL, 1), (NULL, 2), (NULL, 3)) + ) + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + ); + """ + + test { + sql """ + CREATE TABLE `test_null` ( + `k0` BIGINT NULL, + `k1` BIGINT NOT NULL + ) + partition by list (k0, k1) ( + PARTITION `pX` values in ((NULL, 1), (NULL, 2), (NULL, 3), (4, NULL)) + ) + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + ); + """ + exception "Can't have null partition is for NOT NULL partition column in partition expr's index 1" + } +} \ No newline at end of file