|
1 | 1 | package com.clickhouse.client.api.internal; |
2 | 2 |
|
| 3 | +import com.clickhouse.client.api.ClickHouseException; |
3 | 4 | import com.clickhouse.client.api.DataTypeUtils; |
4 | | -import com.clickhouse.client.api.data_formats.internal.AbstractBinaryFormatReader; |
5 | 5 | import com.clickhouse.client.api.data_formats.internal.BinaryStreamReader; |
6 | 6 | import com.clickhouse.data.ClickHouseColumn; |
7 | 7 | import com.clickhouse.data.ClickHouseDataType; |
|
13 | 13 | import java.time.ZonedDateTime; |
14 | 14 | import java.time.format.DateTimeFormatter; |
15 | 15 | import java.time.temporal.TemporalAccessor; |
16 | | -import java.util.Arrays; |
17 | 16 | import java.util.Date; |
| 17 | +import java.util.List; |
18 | 18 |
|
| 19 | +/** |
| 20 | + * Class designed to convert different data types to Java objects. |
| 21 | + * First use-case is to convert ClickHouse data types to String representation. |
| 22 | + * Note: class is not thread-safe to avoid extra object creation. |
| 23 | + */ |
19 | 24 | public class DataTypeConverter { |
20 | 25 |
|
| 26 | + private static final char QUOTE = '\''; |
| 27 | + |
| 28 | + private static final String NULL = "NULL"; |
| 29 | + |
21 | 30 | public static final DataTypeConverter INSTANCE = new DataTypeConverter(); |
22 | 31 |
|
| 32 | + private final ListAsStringWriter listAsStringWriter = new ListAsStringWriter(); |
| 33 | + |
| 34 | + private final ArrayAsStringWriter arrayAsStringWriter = new ArrayAsStringWriter(); |
| 35 | + |
23 | 36 | public String convertToString(Object value, ClickHouseColumn column) { |
24 | 37 | if (value == null) { |
25 | 38 | return null; |
@@ -56,7 +69,21 @@ public String convertToString(Object value, ClickHouseColumn column) { |
56 | 69 | } |
57 | 70 |
|
58 | 71 | public String stringToString(Object bytesOrString, ClickHouseColumn column) { |
59 | | - return bytesOrString instanceof byte[] ? new String((byte[]) bytesOrString) : (String) bytesOrString; |
| 72 | + StringBuilder sb = new StringBuilder(); |
| 73 | + if (column.isArray()) { |
| 74 | + sb.append(QUOTE); |
| 75 | + } |
| 76 | + if (bytesOrString instanceof CharSequence) { |
| 77 | + sb.append(((CharSequence) bytesOrString)); |
| 78 | + } else if (bytesOrString instanceof byte[]) { |
| 79 | + sb.append(bytesOrString); |
| 80 | + } else { |
| 81 | + sb.append(bytesOrString); |
| 82 | + } |
| 83 | + if (column.isArray()) { |
| 84 | + sb.append(QUOTE); |
| 85 | + } |
| 86 | + return sb.toString(); |
60 | 87 | } |
61 | 88 |
|
62 | 89 | public String dateToString(Object value, ClickHouseColumn column) { |
@@ -149,33 +176,94 @@ public String ipvToString(Object value, ClickHouseColumn column) { |
149 | 176 |
|
150 | 177 | public String arrayToString(Object value, ClickHouseColumn column) { |
151 | 178 | if (value instanceof BinaryStreamReader.ArrayValue) { |
152 | | - return ((BinaryStreamReader.ArrayValue) value).asList().toString(); |
153 | | - } else if (value instanceof byte[]) { |
154 | | - return Arrays.toString((byte[]) value); |
155 | | - } else if (value instanceof short[]) { |
156 | | - return Arrays.toString((short[]) value); |
157 | | - } else if (value instanceof int[]) { |
158 | | - return Arrays.toString((int[]) value); |
159 | | - } else if (value instanceof long[]) { |
160 | | - return Arrays.toString((long[]) value); |
161 | | - } else if (value instanceof float[]) { |
162 | | - return Arrays.toString((float[]) value); |
163 | | - } else if (value instanceof double[]) { |
164 | | - return Arrays.toString((double[]) value); |
165 | | - } else if (value instanceof boolean[]) { |
166 | | - return Arrays.toString((boolean[]) value); |
167 | | - } else if (value instanceof char[]) { |
168 | | - return Arrays.toString((char[]) value); |
169 | | - } else if (value instanceof Object[]) { |
170 | | - return Arrays.deepToString((Object[]) value); |
| 179 | + return listAsStringWriter.convertAndReset(((BinaryStreamReader.ArrayValue) value).asList(), new StringBuilder(), column); |
| 180 | + } else if (value.getClass().isArray()) { |
| 181 | + return arrayAsStringWriter.convertAndReset(value, new StringBuilder(), column); |
| 182 | + } else if (value instanceof List<?>) { |
| 183 | + return listAsStringWriter.convertAndReset((List<?>) value, new StringBuilder(), column); |
171 | 184 | } |
172 | 185 | return value.toString(); |
173 | 186 | } |
174 | 187 |
|
| 188 | + /** |
| 189 | + * |
| 190 | + * @param value not null object value to convert |
| 191 | + * @param column column describing the DB value |
| 192 | + * @return String representing the value |
| 193 | + */ |
175 | 194 | public String variantOrDynamicToString(Object value, ClickHouseColumn column) { |
176 | 195 | if (value instanceof BinaryStreamReader.ArrayValue) { |
177 | 196 | return arrayToString(value, column); |
178 | 197 | } |
179 | 198 | return value.toString(); |
180 | 199 | } |
| 200 | + |
| 201 | + private final class ArrayAsStringWriter extends BaseCollectionConverter.BaseArrayWriter { |
| 202 | + private ClickHouseColumn column; |
| 203 | + |
| 204 | + ArrayAsStringWriter() { |
| 205 | + super(); |
| 206 | + } |
| 207 | + |
| 208 | + public void setColumn(ClickHouseColumn column) { |
| 209 | + this.column = column; |
| 210 | + } |
| 211 | + |
| 212 | + |
| 213 | + @Override |
| 214 | + protected void onItem(Object item, ListConversionState<Object> state) { |
| 215 | + if (item == null) { |
| 216 | + append(NULL); |
| 217 | + return; |
| 218 | + } |
| 219 | + String str = DataTypeConverter.this.convertToString(item, column.getArrayBaseColumn() == null ? column : column.getArrayBaseColumn()); |
| 220 | + try { |
| 221 | + if (column.getArrayBaseColumn().getDataType() == ClickHouseDataType.String) { |
| 222 | + appendable.append(QUOTE).append(str).append(QUOTE); |
| 223 | + } else { |
| 224 | + appendable.append(str); |
| 225 | + } |
| 226 | + } catch (Exception ex) { |
| 227 | + throw new ClickHouseException(ex.getMessage(), ex); |
| 228 | + } |
| 229 | + } |
| 230 | + |
| 231 | + public String convertAndReset(Object list, Appendable acc, ClickHouseColumn column) { |
| 232 | + try { |
| 233 | + setColumn(column); |
| 234 | + return super.convert(list, acc); |
| 235 | + } finally { |
| 236 | + this.column = null; |
| 237 | + setAccumulator(null); |
| 238 | + } |
| 239 | + } |
| 240 | + } |
| 241 | + |
| 242 | + private final class ListAsStringWriter extends BaseCollectionConverter.BaseListWriter { |
| 243 | + |
| 244 | + private ClickHouseColumn column; |
| 245 | + |
| 246 | + public void setColumn(ClickHouseColumn column) { |
| 247 | + this.column = column; |
| 248 | + } |
| 249 | + |
| 250 | + @Override |
| 251 | + protected void onItem(Object item, ListConversionState<List<?>> state) { |
| 252 | + if (item == null) { |
| 253 | + append(NULL); |
| 254 | + return; |
| 255 | + } |
| 256 | + append(DataTypeConverter.this.convertToString(item, column.getArrayBaseColumn() == null ? column : column.getArrayBaseColumn())); |
| 257 | + } |
| 258 | + |
| 259 | + public String convertAndReset(List<?> list, Appendable acc, ClickHouseColumn column) { |
| 260 | + try { |
| 261 | + setColumn(column); |
| 262 | + return super.convert(list, acc); |
| 263 | + } finally { |
| 264 | + this.column = null; |
| 265 | + setAccumulator(null); |
| 266 | + } |
| 267 | + } |
| 268 | + } |
181 | 269 | } |
0 commit comments