Skip to content

Commit a849396

Browse files
committed
Add new option for rename response column names
1 parent b41af34 commit a849396

File tree

6 files changed

+199
-3
lines changed

6 files changed

+199
-3
lines changed

clickhouse-client/src/main/java/com/clickhouse/client/config/ClickHouseClientOption.java

+5
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ public enum ClickHouseClientOption implements ClickHouseOption {
123123
*/
124124
MAX_THREADS_PER_CLIENT("max_threads_per_client", 0,
125125
"Size of thread pool for each client instance, 0 or negative number means the client will use shared thread pool."),
126+
/**
127+
* Method to rename response columns.
128+
*/
129+
RENAME_RESPONSE_COLUMN("rename_response_column", ClickHouseRenameMethod.NONE,
130+
"Method to rename response columns."),
126131
/**
127132
* Whether to enable retry.
128133
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package com.clickhouse.client.config;
2+
3+
import java.util.function.UnaryOperator;
4+
5+
/**
6+
* Methods for renaming.
7+
*/
8+
public enum ClickHouseRenameMethod {
9+
/**
10+
* No OP.
11+
*/
12+
NONE(null),
13+
/**
14+
* Removes prefix including the dot. So "d.t.col1" becomes "col1" and "col2"
15+
* remains the same.
16+
*/
17+
REMOVE_PREFIX(s -> {
18+
int index = s.lastIndexOf('.');
19+
return index >= 0 ? s.substring(index + 1) : s;
20+
}),
21+
22+
/**
23+
* Replaces whitespace and underscore to camel case. So "a simple_column"
24+
* becomes "aSimpleColumn" and "col_1 2" becomes "col12".
25+
*/
26+
TO_CAMELCASE(s -> {
27+
StringBuilder builder = new StringBuilder(s.length());
28+
boolean toUpperCase = false;
29+
for (char ch : s.toCharArray()) {
30+
if (Character.isWhitespace(ch) || ch == '_') {
31+
toUpperCase = true;
32+
} else if (toUpperCase) {
33+
builder.append(Character.toUpperCase(ch));
34+
toUpperCase = false;
35+
} else {
36+
builder.append(ch);
37+
}
38+
}
39+
return builder.toString();
40+
}),
41+
/**
42+
* Removes prefix and replace whitespace and underscore to camel case.
43+
*/
44+
TO_CAMELCASE_WITHOUT_PREFIX(s -> TO_CAMELCASE.rename(REMOVE_PREFIX.rename(s))),
45+
/**
46+
* Replaces whitespace and camel case to underscore. So "aSimpleColumn" becomes
47+
* "a_simple_column" and "col12" becomes "col_12".
48+
*/
49+
TO_UNDERSCORE(s -> {
50+
StringBuilder builder = new StringBuilder(s.length() + 5);
51+
int prev = -1; // 0 - normal, 1 - whitespace, 2 - upper case
52+
for (char ch : s.toCharArray()) {
53+
if (Character.isWhitespace(ch)) {
54+
if (prev == 0) {
55+
builder.append('_');
56+
}
57+
prev = 1;
58+
} else if (Character.isUpperCase(ch)) {
59+
if (prev == 0) {
60+
builder.append('_').append(Character.toLowerCase(ch));
61+
} else if (prev == 1) {
62+
builder.append(Character.toLowerCase(ch));
63+
} else {
64+
builder.append(ch);
65+
}
66+
prev = 2;
67+
} else {
68+
builder.append(ch);
69+
prev = 0;
70+
}
71+
}
72+
return builder.toString();
73+
}),
74+
/**
75+
* Removes prefix and replace whitespace and camel case to underscore.
76+
*/
77+
TO_UNDERSCORE_WITHOUT_PREFIX(s -> TO_UNDERSCORE.rename(REMOVE_PREFIX.rename(s)));
78+
79+
private final UnaryOperator<String> renameFunc;
80+
81+
ClickHouseRenameMethod(UnaryOperator<String> renameFunc) {
82+
this.renameFunc = renameFunc;
83+
}
84+
85+
/**
86+
* Rename the given name.
87+
*
88+
* @param name name to change
89+
* @return non-null new name
90+
*/
91+
public String rename(String name) {
92+
if (name == null) {
93+
name = "";
94+
}
95+
return renameFunc != null ? renameFunc.apply(name) : name;
96+
}
97+
}

clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseRowBinaryProcessor.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import com.clickhouse.client.ClickHouseSerializer;
2626
import com.clickhouse.client.ClickHouseValue;
2727
import com.clickhouse.client.ClickHouseValues;
28+
import com.clickhouse.client.config.ClickHouseClientOption;
29+
import com.clickhouse.client.config.ClickHouseRenameMethod;
2830

2931
/**
3032
* Data processor for handling {@link ClickHouseFormat#RowBinary} and
@@ -521,10 +523,12 @@ protected List<ClickHouseColumn> readColumns() throws IOException {
521523
names[i] = input.readUnicodeString();
522524
}
523525

526+
ClickHouseRenameMethod m = (ClickHouseRenameMethod) config
527+
.getOption(ClickHouseClientOption.RENAME_RESPONSE_COLUMN);
524528
List<ClickHouseColumn> columns = new ArrayList<>(size);
525529
for (int i = 0; i < size; i++) {
526530
// a bit risky here - what if ClickHouse support user type?
527-
columns.add(ClickHouseColumn.of(names[i], input.readAsciiString()));
531+
columns.add(ClickHouseColumn.of(m.rename(names[i]), input.readAsciiString()));
528532
}
529533

530534
return columns;

clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseTabSeparatedProcessor.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import com.clickhouse.client.ClickHouseRecord;
2121
import com.clickhouse.client.ClickHouseSerializer;
2222
import com.clickhouse.client.ClickHouseValue;
23+
import com.clickhouse.client.config.ClickHouseClientOption;
24+
import com.clickhouse.client.config.ClickHouseRenameMethod;
2325
import com.clickhouse.client.data.tsv.ByteFragment;
2426

2527
public class ClickHouseTabSeparatedProcessor extends ClickHouseDataProcessor {
@@ -288,10 +290,12 @@ protected List<ClickHouseColumn> readColumns() throws IOException {
288290
buf.lastByte() == getTextHandler().rowDelimiter ? buf.length() - 1 : buf.length());
289291
types = toStringArray(typesFragment, getTextHandler().colDelimiter);
290292
}
291-
List<ClickHouseColumn> list = new ArrayList<>(cols.length);
292293

294+
ClickHouseRenameMethod m = (ClickHouseRenameMethod) config
295+
.getOption(ClickHouseClientOption.RENAME_RESPONSE_COLUMN);
296+
List<ClickHouseColumn> list = new ArrayList<>(cols.length);
293297
for (int i = 0; i < cols.length; i++) {
294-
list.add(ClickHouseColumn.of(cols[i], types == null ? "Nullable(String)" : types[i]));
298+
list.add(ClickHouseColumn.of(m.rename(cols[i]), types == null ? "Nullable(String)" : types[i]));
295299
}
296300

297301
return list;

clickhouse-client/src/test/java/com/clickhouse/client/ClientIntegrationTest.java

+28
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.stream.Collectors;
2424

2525
import com.clickhouse.client.config.ClickHouseClientOption;
26+
import com.clickhouse.client.config.ClickHouseRenameMethod;
2627
import com.clickhouse.client.config.ClickHouseSslMode;
2728
import com.clickhouse.client.data.BinaryStreamUtils;
2829
import com.clickhouse.client.data.ClickHouseBigDecimalValue;
@@ -93,6 +94,18 @@ protected Object[][] getCompressionMatrix() {
9394
new Object[] { false, true } };
9495
}
9596

97+
@DataProvider(name = "renameMethods")
98+
protected Object[][] getRenameMethods() {
99+
return new Object[][] {
100+
new Object[] { null, "a b c", " ", "d.E_f" },
101+
new Object[] { ClickHouseRenameMethod.NONE, "a b c", " ", "d.E_f" },
102+
new Object[] { ClickHouseRenameMethod.REMOVE_PREFIX, "a b c", " ", "E_f" },
103+
new Object[] { ClickHouseRenameMethod.TO_CAMELCASE, "aBC", "", "d.EF" },
104+
new Object[] { ClickHouseRenameMethod.TO_CAMELCASE_WITHOUT_PREFIX, "aBC", "", "EF" },
105+
new Object[] { ClickHouseRenameMethod.TO_UNDERSCORE, "a_b_c", "", "d._e_f" },
106+
new Object[] { ClickHouseRenameMethod.TO_UNDERSCORE_WITHOUT_PREFIX, "a_b_c", "", "E_f" }, };
107+
}
108+
96109
@DataProvider(name = "simpleTypeProvider")
97110
protected Object[][] getSimpleTypes() {
98111
return new Object[][] {
@@ -1073,6 +1086,21 @@ public void testInsertWithInputFunction() throws Exception {
10731086
}
10741087
}
10751088

1089+
@Test(dataProvider = "renameMethods", groups = "integration")
1090+
public void testRenameResponseColumns(ClickHouseRenameMethod m, String col1, String col2, String col3)
1091+
throws Exception {
1092+
ClickHouseNode server = getServer();
1093+
try (ClickHouseClient client = getClient();
1094+
ClickHouseResponse resp = client.connect(server)
1095+
.format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
1096+
.option(ClickHouseClientOption.RENAME_RESPONSE_COLUMN, m)
1097+
.query("select 1 `a b c`, 2 ` `, 3 `d.E_f`").execute().get()) {
1098+
Assert.assertEquals(resp.getColumns().get(0).getColumnName(), col1);
1099+
Assert.assertEquals(resp.getColumns().get(1).getColumnName(), col2);
1100+
Assert.assertEquals(resp.getColumns().get(2).getColumnName(), col3);
1101+
}
1102+
}
1103+
10761104
@Test(groups = "integration")
10771105
public void testTempTable() throws Exception {
10781106
ClickHouseNode server = getServer();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.clickhouse.client.config;
2+
3+
import org.testng.Assert;
4+
import org.testng.annotations.Test;
5+
6+
public class ClickHouseRenameMethodTest {
7+
@Test(groups = { "unit" })
8+
public void testRenameNullOrEmptyString() {
9+
for (ClickHouseRenameMethod m : ClickHouseRenameMethod.values()) {
10+
Assert.assertEquals(m.rename(null), "");
11+
Assert.assertEquals(m.rename(""), "");
12+
}
13+
}
14+
15+
@Test(groups = { "unit" })
16+
public void testNone() {
17+
Assert.assertEquals(ClickHouseRenameMethod.NONE.rename("\t \n \r"), "\t \n \r");
18+
Assert.assertEquals(ClickHouseRenameMethod.NONE.rename("test 1 2 3"), "test 1 2 3");
19+
}
20+
21+
@Test(groups = { "unit" })
22+
public void testRemovePrefix() {
23+
Assert.assertEquals(ClickHouseRenameMethod.REMOVE_PREFIX.rename("\t \n \r"), "\t \n \r");
24+
Assert.assertEquals(ClickHouseRenameMethod.REMOVE_PREFIX.rename("test 1 2 3"), "test 1 2 3");
25+
Assert.assertEquals(ClickHouseRenameMethod.REMOVE_PREFIX.rename("test.1 2 3"), "1 2 3");
26+
Assert.assertEquals(ClickHouseRenameMethod.REMOVE_PREFIX.rename("test.1.2.3"), "3");
27+
Assert.assertEquals(ClickHouseRenameMethod.REMOVE_PREFIX.rename(".test"), "test");
28+
Assert.assertEquals(ClickHouseRenameMethod.REMOVE_PREFIX.rename("test."), "");
29+
Assert.assertEquals(ClickHouseRenameMethod.REMOVE_PREFIX.rename("."), "");
30+
}
31+
32+
@Test(groups = { "unit" })
33+
public void testCamelCase() {
34+
Assert.assertEquals(ClickHouseRenameMethod.TO_CAMELCASE.rename("\t \n \r"), "");
35+
Assert.assertEquals(ClickHouseRenameMethod.TO_CAMELCASE.rename("test 1 2 3"), "test123");
36+
Assert.assertEquals(ClickHouseRenameMethod.TO_CAMELCASE.rename("test oNE Two_three"), "testONETwoThree");
37+
Assert.assertEquals(ClickHouseRenameMethod.TO_CAMELCASE.rename("test"), "test");
38+
Assert.assertEquals(ClickHouseRenameMethod.TO_CAMELCASE.rename(" test"), "Test");
39+
Assert.assertEquals(ClickHouseRenameMethod.TO_CAMELCASE.rename("test "), "test");
40+
41+
Assert.assertEquals(ClickHouseRenameMethod.TO_CAMELCASE_WITHOUT_PREFIX.rename("a.test_col"), "testCol");
42+
}
43+
44+
@Test(groups = { "unit" })
45+
public void testUnderscore() {
46+
Assert.assertEquals(ClickHouseRenameMethod.TO_UNDERSCORE.rename("\t \n \r"), "");
47+
Assert.assertEquals(ClickHouseRenameMethod.TO_UNDERSCORE.rename("TEST"), "TEST");
48+
Assert.assertEquals(ClickHouseRenameMethod.TO_UNDERSCORE.rename("Test"), "Test");
49+
Assert.assertEquals(ClickHouseRenameMethod.TO_UNDERSCORE.rename("TestONE"), "Test_oNE");
50+
Assert.assertEquals(ClickHouseRenameMethod.TO_UNDERSCORE.rename("Test ONE"), "Test_oNE");
51+
Assert.assertEquals(ClickHouseRenameMethod.TO_UNDERSCORE.rename("Test oneTwo"), "Test_one_two");
52+
Assert.assertEquals(ClickHouseRenameMethod.TO_UNDERSCORE.rename("testOnetWo"), "test_onet_wo");
53+
Assert.assertEquals(ClickHouseRenameMethod.TO_UNDERSCORE.rename("test12Three"), "test12_three");
54+
55+
Assert.assertEquals(ClickHouseRenameMethod.TO_UNDERSCORE_WITHOUT_PREFIX.rename("a.t.est1\t 2Three"),
56+
"est1_2_three");
57+
}
58+
}

0 commit comments

Comments
 (0)