Skip to content

Commit 20d2d4b

Browse files
authored
[core] Add validation for default value (#6164)
1 parent 03aa965 commit 20d2d4b

File tree

7 files changed

+143
-23
lines changed

7 files changed

+143
-23
lines changed

paimon-common/src/main/java/org/apache/paimon/casting/DefaultValueRow.java

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@
2929
import org.apache.paimon.types.DataField;
3030
import org.apache.paimon.types.RowKind;
3131
import org.apache.paimon.types.RowType;
32-
import org.apache.paimon.types.VarCharType;
3332

3433
import javax.annotation.Nullable;
3534

3635
import java.util.List;
3736

37+
import static org.apache.paimon.utils.DefaultValueUtils.convertDefaultValue;
38+
3839
/**
3940
* An implementation of {@link InternalRow} which provides a default value for the underlying {@link
4041
* InternalRow}.
@@ -215,21 +216,7 @@ public static DefaultValueRow create(RowType rowType) {
215216
}
216217

217218
containsDefaultValue = true;
218-
@SuppressWarnings("unchecked")
219-
CastExecutor<Object, Object> resolve =
220-
(CastExecutor<Object, Object>)
221-
CastExecutors.resolve(VarCharType.STRING_TYPE, dataField.type());
222-
223-
if (resolve == null) {
224-
throw new RuntimeException(
225-
"Default value do not support the type of " + dataField.type());
226-
}
227-
228-
if (defaultValueStr.startsWith("'") && defaultValueStr.endsWith("'")) {
229-
defaultValueStr = defaultValueStr.substring(1, defaultValueStr.length() - 1);
230-
}
231-
232-
Object defaultValue = resolve.cast(BinaryString.fromString(defaultValueStr));
219+
Object defaultValue = convertDefaultValue(dataField.type(), defaultValueStr);
233220
row.setField(i, defaultValue);
234221
}
235222

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.paimon.utils;
20+
21+
import org.apache.paimon.casting.CastExecutor;
22+
import org.apache.paimon.casting.CastExecutors;
23+
import org.apache.paimon.data.BinaryString;
24+
import org.apache.paimon.types.DataType;
25+
import org.apache.paimon.types.VarCharType;
26+
27+
import javax.annotation.Nullable;
28+
29+
/** Utils for default value. */
30+
public class DefaultValueUtils {
31+
32+
public static Object convertDefaultValue(DataType dataType, String defaultValueStr) {
33+
@SuppressWarnings("unchecked")
34+
CastExecutor<Object, Object> resolve =
35+
(CastExecutor<Object, Object>)
36+
CastExecutors.resolve(VarCharType.STRING_TYPE, dataType);
37+
38+
if (resolve == null) {
39+
throw new RuntimeException("Default value do not support the type of " + dataType);
40+
}
41+
42+
if (defaultValueStr.startsWith("'") && defaultValueStr.endsWith("'")) {
43+
defaultValueStr = defaultValueStr.substring(1, defaultValueStr.length() - 1);
44+
}
45+
46+
return resolve.cast(BinaryString.fromString(defaultValueStr));
47+
}
48+
49+
public static void validateDefaultValue(DataType dataType, @Nullable String defaultValueStr) {
50+
if (defaultValueStr == null) {
51+
return;
52+
}
53+
54+
try {
55+
convertDefaultValue(dataType, defaultValueStr);
56+
} catch (Exception e) {
57+
throw new RuntimeException(
58+
"Unsupported default value `" + defaultValueStr + "` for type " + dataType, e);
59+
}
60+
}
61+
}

paimon-core/src/main/java/org/apache/paimon/catalog/CatalogUtils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.apache.paimon.table.system.AllTableOptionsTable;
4040
import org.apache.paimon.table.system.CatalogOptionsTable;
4141
import org.apache.paimon.table.system.SystemTableLoader;
42+
import org.apache.paimon.types.DataField;
4243
import org.apache.paimon.utils.InternalRowPartitionComputer;
4344
import org.apache.paimon.utils.Preconditions;
4445

@@ -61,6 +62,7 @@
6162
import static org.apache.paimon.options.OptionsUtils.convertToPropertiesPrefixKey;
6263
import static org.apache.paimon.table.system.AllTableOptionsTable.ALL_TABLE_OPTIONS;
6364
import static org.apache.paimon.table.system.CatalogOptionsTable.CATALOG_OPTIONS;
65+
import static org.apache.paimon.utils.DefaultValueUtils.validateDefaultValue;
6466
import static org.apache.paimon.utils.Preconditions.checkArgument;
6567

6668
/** Utils for {@link Catalog}. */
@@ -147,6 +149,9 @@ public static void validateCreateTable(Schema schema) {
147149
"Cannot define %s for format table.",
148150
PRIMARY_KEY.key());
149151
}
152+
for (DataField field : schema.fields()) {
153+
validateDefaultValue(field.type(), field.defaultValue());
154+
}
150155
}
151156

152157
public static void validateNamePattern(Catalog catalog, String namePattern) {

paimon-core/src/main/java/org/apache/paimon/schema/SchemaManager.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
import static org.apache.paimon.catalog.Identifier.DEFAULT_MAIN_BRANCH;
9292
import static org.apache.paimon.catalog.Identifier.UNKNOWN_DATABASE;
9393
import static org.apache.paimon.mergetree.compact.PartialUpdateMergeFunction.SEQUENCE_GROUP;
94+
import static org.apache.paimon.utils.DefaultValueUtils.validateDefaultValue;
9495
import static org.apache.paimon.utils.FileUtils.listVersionedFiles;
9596
import static org.apache.paimon.utils.Preconditions.checkArgument;
9697
import static org.apache.paimon.utils.Preconditions.checkState;
@@ -559,13 +560,15 @@ protected void updateLastColumn(
559560
updateNestedColumn(
560561
newFields,
561562
update.fieldNames(),
562-
(field, depth) ->
563-
new DataField(
564-
field.id(),
565-
field.name(),
566-
field.type(),
567-
field.description(),
568-
update.newDefaultValue()),
563+
(field, depth) -> {
564+
validateDefaultValue(field.type(), update.newDefaultValue());
565+
return new DataField(
566+
field.id(),
567+
field.name(),
568+
field.type(),
569+
field.description(),
570+
update.newDefaultValue());
571+
},
569572
lazyIdentifier);
570573
} else {
571574
throw new UnsupportedOperationException("Unsupported change: " + change.getClass());

paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/BranchSqlITCase.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ public void testDefaultValue() throws Exception {
5252
.containsExactlyInAnyOrder("+I[1, 5]", "+I[2, 5]");
5353
}
5454

55+
@Test
56+
public void testUnsupportedDefaultValue() {
57+
sql("CREATE TABLE T (a INT, b INT)");
58+
assertThatThrownBy(
59+
() -> sql("CALL sys.alter_column_default_value('default.T', 'b', 'ddd')"))
60+
.hasMessageContaining("Unsupported default value `ddd` for type INT");
61+
}
62+
5563
@Test
5664
public void testArrayDefaultValue() throws Exception {
5765
sql("CREATE TABLE T_ARRAY (id INT, tags ARRAY<STRING>, numbers ARRAY<INT>)");
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.paimon.spark.sql
20+
21+
import org.apache.paimon.spark.PaimonSparkTestBase
22+
23+
class DefaultValueTest extends PaimonSparkTestBase {
24+
25+
test("Default Value: unsupported default value") {
26+
withTimeZone("Asia/Shanghai") {
27+
withTable("t") {
28+
assert(
29+
intercept[Throwable] {
30+
sql("""
31+
|CREATE TABLE t (
32+
| a INT,
33+
| b TIMESTAMP DEFAULT current_timestamp(),
34+
| c TIMESTAMP_NTZ DEFAULT current_timestamp,
35+
| d DATE DEFAULT current_date()
36+
|)
37+
|""".stripMargin)
38+
}.getMessage
39+
.contains("Unsupported default value `current_timestamp()`"))
40+
41+
sql("CREATE TABLE t (a INT DEFAULT 1)")
42+
assert(
43+
intercept[Throwable] {
44+
sql("""
45+
|ALTER TABLE t ALTER COLUMN a SET DEFAULT current_timestamp()
46+
|""".stripMargin)
47+
}.getMessage
48+
.contains("Unsupported default value `current_timestamp()`"))
49+
}
50+
}
51+
}
52+
}

paimon-spark/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ under the License.
5858
<groupId>org.apache.hadoop</groupId>
5959
<artifactId>hadoop-common</artifactId>
6060
</exclusion>
61+
<exclusion>
62+
<groupId>org.apache.commons</groupId>
63+
<artifactId>commons-lang3</artifactId>
64+
</exclusion>
6165
</exclusions>
6266
</dependency>
6367

0 commit comments

Comments
 (0)