Skip to content

Commit 2b3b3db

Browse files
committed
address jark's comments
1 parent fd3cbee commit 2b3b3db

File tree

18 files changed

+544
-239
lines changed

18 files changed

+544
-239
lines changed

fluss-client/src/main/java/com/alibaba/fluss/client/table/getter/PartitionGetter.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,22 @@
1616

1717
package com.alibaba.fluss.client.table.getter;
1818

19+
import com.alibaba.fluss.metadata.ResolvedPartitionSpec;
1920
import com.alibaba.fluss.row.InternalRow;
2021
import com.alibaba.fluss.types.DataType;
2122
import com.alibaba.fluss.types.RowType;
2223

23-
import java.util.Collections;
2424
import java.util.List;
2525

26-
import static com.alibaba.fluss.utils.PartitionUtils.getPartitionName;
26+
import static com.alibaba.fluss.metadata.ResolvedPartitionSpec.fromPartitionValue;
2727
import static com.alibaba.fluss.utils.Preconditions.checkArgument;
2828
import static com.alibaba.fluss.utils.Preconditions.checkNotNull;
2929

3030
/** A getter to get partition name from a row. */
3131
public class PartitionGetter {
3232

33+
// TODO currently, only support one partition key.
34+
private final String partitionKey;
3335
private final InternalRow.FieldGetter partitionFieldGetter;
3436

3537
public PartitionGetter(RowType rowType, List<String> partitionKeys) {
@@ -42,8 +44,7 @@ public PartitionGetter(RowType rowType, List<String> partitionKeys) {
4244

4345
// check the partition column
4446
List<String> fieldNames = rowType.getFieldNames();
45-
// TODO currently, only support one partition key.
46-
String partitionKey = partitionKeys.get(0);
47+
this.partitionKey = partitionKeys.get(0);
4748
int partitionColumnIndex = fieldNames.indexOf(partitionKey);
4849
checkArgument(
4950
partitionColumnIndex >= 0,
@@ -60,6 +61,8 @@ public PartitionGetter(RowType rowType, List<String> partitionKeys) {
6061
public String getPartition(InternalRow row) {
6162
Object partitionValue = partitionFieldGetter.getFieldOrNull(row);
6263
checkNotNull(partitionValue, "Partition value shouldn't be null.");
63-
return getPartitionName(Collections.singletonList(partitionValue.toString()));
64+
ResolvedPartitionSpec resolvedPartitionSpec =
65+
fromPartitionValue(partitionKey, partitionValue.toString());
66+
return resolvedPartitionSpec.getPartitionName();
6467
}
6568
}

fluss-client/src/test/java/com/alibaba/fluss/client/admin/FlussAdminITCase.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,9 @@ tablePath, newPartitionSpec("age", "$10"), false)
669669
.get())
670670
.cause()
671671
.isInstanceOf(InvalidPartitionException.class)
672-
.hasMessageContaining("The partition value should not contain separator: '$'");
672+
.hasMessageContaining(
673+
"The partition value $10 is invalid: '$10' contains one or more "
674+
+ "characters other than ASCII alphanumerics, '_' and '-'");
673675

674676
// test create partition with wrong partition key.
675677
assertThatThrownBy(

fluss-common/src/main/java/com/alibaba/fluss/metadata/PhysicalTablePath.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,20 @@ public String getPartitionName() {
8484
* Returns true if the database name, table name and the optional partition name are all valid.
8585
*/
8686
public boolean isValid() {
87-
return getTablePath().isValid()
88-
&& (partitionName == null || detectInvalidName(partitionName) == null);
87+
if (!getTablePath().isValid()) {
88+
return false;
89+
}
90+
91+
if (partitionName != null) {
92+
String[] partitionValues = partitionName.split("\\$");
93+
for (String partitionValue : partitionValues) {
94+
if (detectInvalidName(partitionValue) != null) {
95+
return false;
96+
}
97+
}
98+
}
99+
100+
return true;
89101
}
90102

91103
@Override
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright (c) 2025 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.alibaba.fluss.metadata;
18+
19+
import com.alibaba.fluss.annotation.PublicEvolving;
20+
21+
import java.util.ArrayList;
22+
import java.util.Arrays;
23+
import java.util.Collections;
24+
import java.util.List;
25+
import java.util.Map;
26+
27+
import static com.alibaba.fluss.utils.Preconditions.checkArgument;
28+
29+
/**
30+
* Represents the resolved {@link PartitionSpec}, which the partition spec is re-arranged to the
31+
* correct order by comparing with a list of strictly ordered partition keys.
32+
*
33+
* @since 0.6
34+
*/
35+
@PublicEvolving
36+
public class ResolvedPartitionSpec {
37+
public static final String PARTITION_SPEC_SEPARATOR = "$";
38+
39+
private final List<String> partitionKeys;
40+
private final List<String> partitionValues;
41+
private final String partitionName;
42+
43+
private ResolvedPartitionSpec(List<String> partitionKeys, List<String> partitionValues) {
44+
checkArgument(
45+
partitionKeys.size() == partitionValues.size(),
46+
"The number of partition keys and partition values should be the same.");
47+
this.partitionKeys = partitionKeys;
48+
this.partitionValues = partitionValues;
49+
this.partitionName = generatePartitionName();
50+
}
51+
52+
public static ResolvedPartitionSpec fromPartitionSpec(
53+
List<String> partitionKeys, PartitionSpec partitionSpec) {
54+
return new ResolvedPartitionSpec(
55+
partitionKeys, getReorderedPartitionValues(partitionKeys, partitionSpec));
56+
}
57+
58+
public static ResolvedPartitionSpec fromPartitionValue(
59+
String partitionKey, String partitionValue) {
60+
return new ResolvedPartitionSpec(
61+
Collections.singletonList(partitionKey), Collections.singletonList(partitionValue));
62+
}
63+
64+
public static ResolvedPartitionSpec fromPartitionName(
65+
List<String> partitionKeys, String partitionName) {
66+
return new ResolvedPartitionSpec(partitionKeys, Arrays.asList(partitionName.split("\\$")));
67+
}
68+
69+
public List<String> getPartitionKeys() {
70+
return partitionKeys;
71+
}
72+
73+
public List<String> getPartitionValues() {
74+
return partitionValues;
75+
}
76+
77+
public String getPartitionName() {
78+
return partitionName;
79+
}
80+
81+
/**
82+
* Returns the partition name for a partition table of specify partition values.
83+
*
84+
* <p>The partition name is in the following format:
85+
*
86+
* <pre>
87+
* value1$value2$...$valueN
88+
* </pre>
89+
*
90+
* <p>For example, if the partition keys are [a, b, c], and the partition values are [1, 2, 3],
91+
* the partition name is "1$2$3".
92+
*
93+
* <p>Currently, we only support one partition key. So the partition name is in the following
94+
* format:
95+
*
96+
* <pre>
97+
* value
98+
* </pre>
99+
*
100+
* <p>For example, if the partition keys are [a], and the partition value is [1], the partition
101+
* name will be "1".
102+
*/
103+
public String generatePartitionName() {
104+
return String.join(PARTITION_SPEC_SEPARATOR, partitionValues);
105+
}
106+
107+
/**
108+
* Returns the qualified partition name for a partition spec (partition keys and partition
109+
* values). The qualified partition name is not used as the partition directory path, but is
110+
* used as a pretty display name of a partition. The qualified partition name is in the
111+
* following format:
112+
*
113+
* <pre>
114+
* key1=value1/key2=value2/.../keyN=valueN
115+
* </pre>
116+
*/
117+
public String getPartitionQualifiedName() {
118+
StringBuilder sb = new StringBuilder();
119+
for (int i = 0; i < partitionKeys.size(); i++) {
120+
sb.append(partitionKeys.get(i)).append("=").append(partitionValues.get(i));
121+
if (i != partitionKeys.size() - 1) {
122+
sb.append("/");
123+
}
124+
}
125+
return sb.toString();
126+
}
127+
128+
@Override
129+
public String toString() {
130+
return "ResolvedPartitionSpec{"
131+
+ "partitionKeys="
132+
+ partitionKeys
133+
+ ", partitionValues="
134+
+ partitionValues
135+
+ '}';
136+
}
137+
138+
private static List<String> getReorderedPartitionValues(
139+
List<String> partitionKeys, PartitionSpec partitionSpec) {
140+
Map<String, String> partitionSpecMap = partitionSpec.getPartitionSpec();
141+
List<String> reOrderedPartitionValues = new ArrayList<>(partitionKeys.size());
142+
partitionKeys.forEach(
143+
partitionKey -> reOrderedPartitionValues.add(partitionSpecMap.get(partitionKey)));
144+
return reOrderedPartitionValues;
145+
}
146+
}

fluss-common/src/main/java/com/alibaba/fluss/metadata/TablePath.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,12 @@ public static String detectInvalidName(String identifier) {
163163
return "'"
164164
+ identifier
165165
+ "' contains one or more characters other than "
166-
+ "ASCII alphanumerics, '_', '$' and '-'";
166+
+ "ASCII alphanumerics, '_' and '-'";
167167
}
168168
return null;
169169
}
170170

171-
/** Valid characters for Fluss table names are the ASCII alphanumerics, '_', '$' and '-'. */
171+
/** Valid characters for Fluss table names are the ASCII alphanumerics, '_' and '-'. */
172172
private static boolean containsInvalidPattern(String identifier) {
173173
for (int i = 0; i < identifier.length(); ++i) {
174174
char c = identifier.charAt(i);
@@ -179,8 +179,7 @@ private static boolean containsInvalidPattern(String identifier) {
179179
|| (c >= '0' && c <= '9')
180180
|| (c >= 'A' && c <= 'Z')
181181
|| c == '_'
182-
|| c == '-'
183-
|| c == '$';
182+
|| c == '-';
184183

185184
if (!validChar) {
186185
return true;

0 commit comments

Comments
 (0)