queue, int timeout) {
- return new BlockingInputStream(ClickHouseChecker.nonNull(queue, "queue"), timeout);
+ return new BlockingInputStream(queue, timeout);
}
/**
* Wraps the given input stream.
*
* @param input non-null input stream
- * @return wrapped input
+ * @return wrapped input, or the same input if it's instance of
+ * {@link ClickHouseInputStream}
*/
public static ClickHouseInputStream of(InputStream input) {
- return input instanceof ClickHouseInputStream ? (ClickHouseInputStream) input : new WrappedInputStream(input);
+ return of(input, (int) ClickHouseClientOption.MAX_BUFFER_SIZE.getDefaultValue());
+ }
+
+ /**
+ * Wraps the given input stream.
+ *
+ * @param input non-null input stream
+ * @param bufferSize buffer size which is always greater than zero(usually 4096
+ * or larger)
+ * @return wrapped input, or the same input if it's instance of
+ * {@link ClickHouseInputStream}
+ */
+ public static ClickHouseInputStream of(InputStream input, int bufferSize) {
+ return input instanceof ClickHouseInputStream ? (ClickHouseInputStream) input
+ : new WrappedInputStream(input, bufferSize);
}
/**
- * Reads an unsigned byte from the input stream.
+ * Reads an unsigned byte from the input stream. Unlike {@link #read()}, it will
+ * throw {@link IOException} if the input stream has been closed.
*
* @return unsigned byte
* @throws IOException when failed to read value from input stream or reached
@@ -263,8 +419,10 @@ public int readUnsignedByte() throws IOException {
}
/**
- * Reads one single byte from the input stream. It's supposed to be faster than
- * {@link #read()}.
+ * Reads one single byte from the input stream. Unlike {@link #read()}, it will
+ * throw {@link IOException} if the input stream has been closed. In general,
+ * this method should be faster than {@link #read()}, especially when it's an
+ * input stream backed by byte[] or {@link java.nio.ByteBuffer}.
*
* @return byte value if present
* @throws IOException when failed to read value from input stream or reached
@@ -274,7 +432,8 @@ public int readUnsignedByte() throws IOException {
/**
* Reads {@code length} bytes from the input stream. It behaves in the same
- * way as {@link java.io.DataInput#readFully(byte[])}.
+ * way as {@link java.io.DataInput#readFully(byte[])}, and it will throw
+ * {@link IOException} when the input stream has been closed.
*
* @param length number of bytes to read
* @return byte array and its length should be {@code length}
@@ -282,6 +441,10 @@ public int readUnsignedByte() throws IOException {
* retrieve all bytes, or reached end of the stream
*/
public byte[] readBytes(int length) throws IOException {
+ if (length <= 0) {
+ return EMPTY_BYTES;
+ }
+
byte[] bytes = new byte[length];
for (int l = length, c = 0, n = 0; l > 0; l -= n) {
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseParameterizedQuery.java b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseParameterizedQuery.java
index fd29077a0..c690ba322 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseParameterizedQuery.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseParameterizedQuery.java
@@ -4,22 +4,24 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Map.Entry;
/**
* A parameterized query is a parsed query with parameters being extracted for
* substitution.
*
* Here parameter is define in the format of {@code :[()]}. It
- * starts with colon, followed by name, and then optionally type within
- * brackets. For example: in query "select :no as no, :name(String) as name",
- * both {@code no} and {@code name} are parameters. Moreover, type of the last
- * parameter is {@code String}.
+ * starts with colon, immediately followed by name, and then optionally type
+ * within brackets. For example: in query "select :no as no, :name(String) as
+ * name", we have two parameters: {@code no} and {@code name}. Moreover, type of
+ * the last parameter is {@code String}.
*/
public class ClickHouseParameterizedQuery implements Serializable {
private static final long serialVersionUID = 8108887349618342152L;
@@ -271,6 +273,19 @@ protected String parse() {
return partIndex < len ? originalQuery.substring(partIndex, len) : null;
}
+ /**
+ * Appends last part of the query if it exists.
+ *
+ * @param builder non-null string builder
+ * @return the builder
+ */
+ protected StringBuilder appendLastPartIfExists(StringBuilder builder) {
+ if (lastPart != null) {
+ builder.append(lastPart);
+ }
+ return builder;
+ }
+
/**
* Converts given raw value to SQL expression.
*
@@ -305,10 +320,7 @@ public String apply(Map params) {
builder.append(params.getOrDefault(p.paramName, ClickHouseValues.NULL_EXPR));
}
- if (lastPart != null) {
- builder.append(lastPart);
- }
- return builder.toString();
+ return appendLastPartIfExists(builder).toString();
}
/**
@@ -318,23 +330,25 @@ public String apply(Map params) {
* @return substituted query
*/
public String apply(Collection params) {
- if (!hasParameter()) {
- return originalQuery;
- }
-
- StringBuilder builder = new StringBuilder();
- Iterator it = params == null ? null : params.iterator();
- boolean hasMore = it != null && it.hasNext();
- for (QueryPart p : parts) {
- builder.append(p.part);
- builder.append(hasMore ? it.next() : ClickHouseValues.NULL_EXPR);
- hasMore = hasMore && it.hasNext();
- }
-
- if (lastPart != null) {
- builder.append(lastPart);
+ if (params == null || params.isEmpty()) {
+ return apply(Collections.emptyMap());
+ }
+
+ Map map = null;
+ Iterator it = params.iterator();
+ if (it.hasNext()) {
+ map = new HashMap<>();
+ for (String n : names.keySet()) {
+ String v = it.next();
+ if (v != null) {
+ map.put(n, v);
+ }
+ if (!it.hasNext()) {
+ break;
+ }
+ }
}
- return builder.toString();
+ return apply(map);
}
/**
@@ -351,22 +365,24 @@ public String apply(Object param, Object... more) {
return originalQuery;
}
- int len = more == null ? 0 : more.length + 1;
- StringBuilder builder = new StringBuilder();
- int index = 0;
- for (QueryPart p : parts) {
- builder.append(p.part);
- if (index > 0) {
- param = index < len ? more[index - 1] : null;
+ int len = more == null ? 0 : more.length;
+ Map map = new HashMap<>();
+ int index = -1;
+ for (Entry e : names.entrySet()) {
+ ClickHouseValue v = e.getValue();
+ if (index < 0) {
+ map.put(e.getKey(),
+ v != null ? v.update(param).toSqlExpression() : ClickHouseValues.convertToSqlExpression(param));
+ } else if (index < len) {
+ map.put(e.getKey(), v != null ? v.update(more[index]).toSqlExpression()
+ : ClickHouseValues.convertToSqlExpression(more[index]));
+ } else {
+ break;
}
- builder.append(toSqlExpression(p.paramName, param));
index++;
}
- if (lastPart != null) {
- builder.append(lastPart);
- }
- return builder.toString();
+ return apply(map);
}
/**
@@ -378,24 +394,25 @@ public String apply(Object param, Object... more) {
* @return substituted query
*/
public String apply(Object[] values) {
- if (!hasParameter()) {
- return originalQuery;
+ int len = values == null ? 0 : values.length;
+ if (len == 0) {
+ return apply(Collections.emptyMap());
}
- int len = values == null ? 0 : values.length;
- StringBuilder builder = new StringBuilder();
+ Map map = new HashMap<>();
int index = 0;
- for (QueryPart p : parts) {
- builder.append(p.part);
- builder.append(
- index < len ? toSqlExpression(p.paramName, values[index]) : ClickHouseValues.NULL_EXPR);
+ for (Entry e : names.entrySet()) {
+ ClickHouseValue v = e.getValue();
+ if (index < len) {
+ map.put(e.getKey(), v != null ? v.update(values[index]).toSqlExpression()
+ : ClickHouseValues.convertToSqlExpression(values[index]));
+ } else {
+ break;
+ }
index++;
}
- if (lastPart != null) {
- builder.append(lastPart);
- }
- return builder.toString();
+ return apply(map);
}
/**
@@ -410,22 +427,21 @@ public String apply(String param, String... more) {
return originalQuery;
}
- int len = more == null ? 0 : more.length + 1;
- StringBuilder builder = new StringBuilder();
- int index = 0;
- for (QueryPart p : parts) {
- builder.append(p.part);
- if (index > 0) {
- param = index < len ? more[index - 1] : ClickHouseValues.NULL_EXPR;
+ int len = more == null ? 0 : more.length;
+ Map map = new HashMap<>();
+ int index = -1;
+ for (String n : names.keySet()) {
+ if (index < 0) {
+ map.put(n, param);
+ } else if (index < len) {
+ map.put(n, more[index]);
+ } else {
+ break;
}
- builder.append(param);
index++;
}
- if (lastPart != null) {
- builder.append(lastPart);
- }
- return builder.toString();
+ return apply(map);
}
/**
@@ -435,23 +451,23 @@ public String apply(String param, String... more) {
* @return substituted query
*/
public String apply(String[] values) {
- if (!hasParameter()) {
- return originalQuery;
+ int len = values == null ? 0 : values.length;
+ if (len == 0) {
+ return apply(Collections.emptyMap());
}
- int len = values == null ? 0 : values.length;
- StringBuilder builder = new StringBuilder();
+ Map map = new HashMap<>();
int index = 0;
- for (QueryPart p : parts) {
- builder.append(p.part);
- builder.append(index < len ? values[index] : ClickHouseValues.NULL_EXPR);
+ for (String n : names.keySet()) {
+ if (index < len) {
+ map.put(n, values[index]);
+ } else {
+ break;
+ }
index++;
}
- if (lastPart != null) {
- builder.append(lastPart);
- }
- return builder.toString();
+ return apply(map);
}
/**
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseThreadFactory.java b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseThreadFactory.java
new file mode 100644
index 000000000..a4cd2ffe7
--- /dev/null
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseThreadFactory.java
@@ -0,0 +1,49 @@
+package com.clickhouse.client;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ClickHouseThreadFactory implements ThreadFactory {
+ private final boolean daemon;
+ private final int priority;
+
+ private final ThreadGroup group;
+ private final String namePrefix;
+ private final AtomicInteger threadNumber;
+
+ public ClickHouseThreadFactory(Object owner) {
+ this(owner, true, Thread.NORM_PRIORITY);
+ }
+
+ public ClickHouseThreadFactory(Object owner, boolean daemon, int priority) {
+ String prefix = null;
+ if (owner instanceof String) {
+ prefix = ((String) owner).trim();
+ } else if (owner != null) {
+ prefix = new StringBuilder().append(owner.getClass().getSimpleName()).append('@').append(owner.hashCode())
+ .toString();
+ }
+ this.daemon = daemon;
+ this.priority = ClickHouseChecker.between(priority, "Priority", Thread.MIN_PRIORITY, Thread.MAX_PRIORITY);
+
+ SecurityManager s = System.getSecurityManager();
+ group = s != null ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
+ namePrefix = !ClickHouseChecker.isNullOrBlank(prefix) ? prefix
+ : new StringBuilder().append(getClass().getSimpleName()).append('@').append(hashCode())
+ .append('-').toString();
+ threadNumber = new AtomicInteger(1);
+ }
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
+ if (daemon != t.isDaemon()) {
+ t.setDaemon(daemon);
+ }
+ if (priority != t.getPriority()) {
+ t.setPriority(priority);
+ }
+ // t.setUncaughtExceptionHandler(null);
+ return t;
+ }
+}
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseUtils.java b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseUtils.java
index 7e21e9dbc..2748f2ff7 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseUtils.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseUtils.java
@@ -14,11 +14,9 @@
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
-import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
@@ -30,7 +28,6 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Supplier;
@@ -109,19 +106,30 @@ private static T findFirstService(Class extends T> serviceInterface) {
return service;
}
- public static ExecutorService newThreadPool(String owner, int maxThreads, int maxRequests) {
+ public static ExecutorService newThreadPool(Object owner, int maxThreads, int maxRequests) {
+ return newThreadPool(owner, maxThreads, 0, maxRequests, 0L, true);
+ }
+
+ public static ExecutorService newThreadPool(Object owner, int coreThreads, int maxThreads, int maxRequests,
+ long keepAliveTimeoutMs, boolean allowCoreThreadTimeout) {
BlockingQueue queue = maxRequests > 0 ? new ArrayBlockingQueue<>(maxRequests)
: new LinkedBlockingQueue<>();
+ if (coreThreads < 2) {
+ coreThreads = 2;
+ }
+ if (maxThreads < coreThreads) {
+ maxThreads = coreThreads;
+ }
+ if (keepAliveTimeoutMs <= 0L) {
+ keepAliveTimeoutMs = allowCoreThreadTimeout ? 1000L : 0L;
+ }
- return new ThreadPoolExecutor(1, maxThreads < 1 ? 1 : maxThreads, 0L, TimeUnit.MILLISECONDS, queue,
- new ThreadFactory() {
- @Override
- public Thread newThread(Runnable r) {
- Thread thread = new Thread(r, owner);
- thread.setUncaughtExceptionHandler(null);
- return thread;
- }
- }, new ThreadPoolExecutor.AbortPolicy());
+ ThreadPoolExecutor pool = new ThreadPoolExecutor(coreThreads, maxThreads, keepAliveTimeoutMs,
+ TimeUnit.MILLISECONDS, queue, new ClickHouseThreadFactory(owner), new ThreadPoolExecutor.AbortPolicy());
+ if (allowCoreThreadTimeout) {
+ pool.allowCoreThreadTimeOut(true);
+ }
+ return pool;
}
public static boolean isCloseBracket(char ch) {
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java
index c2a6a1fd3..7c39aea47 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java
@@ -454,11 +454,60 @@ default Map asMap(Class keyClass, Class valueClass) {
* Gets value as a typed object.
*
* @param type of the object
+ * @param type of the enum
* @param clazz class of the object
* @return a typed object representing the value, could be null
*/
- default T asObject(Class clazz) {
- return isNullOrEmpty() ? null : ClickHouseChecker.nonNull(clazz, ClickHouseValues.TYPE_CLASS).cast(asObject());
+ default > T asObject(Class clazz) {
+ if (clazz == null) {
+ return null;
+ } else if (clazz == boolean.class || clazz == Boolean.class) {
+ return clazz.cast(asBoolean());
+ } else if (clazz == byte.class || clazz == Byte.class) {
+ return clazz.cast(asByte());
+ } else if (clazz == char.class || clazz == Character.class) {
+ return clazz.cast(asCharacter());
+ } else if (clazz == short.class || clazz == Short.class) {
+ return clazz.cast(asShort());
+ } else if (clazz == int.class || clazz == Integer.class) {
+ return clazz.cast(asInteger());
+ } else if (clazz == long.class || clazz == Long.class) {
+ return clazz.cast(asLong());
+ } else if (clazz == float.class || clazz == Float.class) {
+ return clazz.cast(asFloat());
+ } else if (clazz == double.class || clazz == Double.class) {
+ return clazz.cast(asDouble());
+ } else if (clazz == String.class) {
+ return clazz.cast(asString());
+ } else if (clazz == LocalDate.class) {
+ return clazz.cast(asDate());
+ } else if (clazz == LocalDateTime.class) {
+ return clazz.cast(asDateTime());
+ } else if (clazz == OffsetDateTime.class) {
+ return clazz.cast(asOffsetDateTime());
+ } else if (clazz == ZonedDateTime.class) {
+ return clazz.cast(asZonedDateTime());
+ } else if (clazz == LocalTime.class) {
+ return clazz.cast(asTime());
+ } else if (clazz == BigInteger.class) {
+ return clazz.cast(asBigInteger());
+ } else if (clazz == BigDecimal.class) {
+ return clazz.cast(asBigDecimal());
+ } else if (clazz == Inet4Address.class) {
+ return clazz.cast(asInet4Address());
+ } else if (clazz == Inet6Address.class) {
+ return clazz.cast(asInet6Address());
+ } else if (clazz == UUID.class) {
+ return clazz.cast(asUuid());
+ } else if (Array.class.isAssignableFrom(clazz)) {
+ return clazz.cast(asArray());
+ } else if (List.class.isAssignableFrom(clazz)) {
+ return clazz.cast(asTuple());
+ } else if (Enum.class.isAssignableFrom(clazz)) {
+ return clazz.cast(asEnum((Class) clazz));
+ } else {
+ return clazz.cast(asObject());
+ }
}
/**
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/config/ClickHouseClientOption.java b/clickhouse-client/src/main/java/com/clickhouse/client/config/ClickHouseClientOption.java
index 6cc43f8cb..fa9d8c361 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/config/ClickHouseClientOption.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/config/ClickHouseClientOption.java
@@ -24,7 +24,6 @@ public enum ClickHouseClientOption implements ClickHouseOption {
*/
CLIENT_NAME("client_name", "ClickHouse Java Client",
"Client name, which is either 'client_name' or 'http_user_agent' shows up in system.query_log table."),
-
/**
* Whether server will compress response to client or not.
*/
@@ -42,7 +41,8 @@ public enum ClickHouseClientOption implements ClickHouseOption {
* Compression algorithm server will use to decompress request, when
* {@link #DECOMPRESS} is {@code true}.
*/
- DECOMPRESS_ALGORITHM("decompress_alogrithm", ClickHouseCompression.GZIP, "Algorithm for decompressing request."),
+ DECOMPRESS_ALGORITHM("decompress_alogrithm", ClickHouseCompression.GZIP,
+ "Algorithm for decompressing request."),
/**
* Compression level for compressing server response.
*/
@@ -143,7 +143,8 @@ public enum ClickHouseClientOption implements ClickHouseOption {
/**
* SSL mode.
*/
- SSL_MODE("sslmode", ClickHouseSslMode.STRICT, "verify or not certificate: none (don't verify), strict (verify)"),
+ SSL_MODE("sslmode", ClickHouseSslMode.STRICT,
+ "verify or not certificate: none (don't verify), strict (verify)"),
/**
* SSL root certificiate.
*/
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/config/ClickHouseDefaults.java b/clickhouse-client/src/main/java/com/clickhouse/client/config/ClickHouseDefaults.java
index feb36664e..f8172a3d4 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/config/ClickHouseDefaults.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/config/ClickHouseDefaults.java
@@ -69,6 +69,11 @@ public enum ClickHouseDefaults implements ClickHouseOption {
* Max requests.
*/
MAX_REQUESTS("max_requests", 0, "Maximum size of shared thread pool, 0 means no limit."),
+ /**
+ * Thread keep alive timeout in milliseconds.
+ */
+ THREAD_KEEPALIVE_TIMEOUT("thread_keepalive_timeout", 0L,
+ "Thread keep alive timeout in milliseconds. 0 or negative number means additional thread will be closed immediately after execution completed."),
/**
* Server time zone, defaults to {@code UTC}.
*/
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/BinaryStreamUtils.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/BinaryStreamUtils.java
index e1725e3f7..f26dece4d 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/BinaryStreamUtils.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/BinaryStreamUtils.java
@@ -76,6 +76,9 @@ public final class BinaryStreamUtils {
public static final BigDecimal NANOS = new BigDecimal(BigInteger.TEN.pow(9));
+ private static final int[] BASES = new int[] { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
+ 1000000000 };
+
private static > T toEnum(int value, Class enumType) {
for (T t : ClickHouseChecker.nonNull(enumType, "enumType").getEnumConstants()) {
if (t.ordinal() == value) {
@@ -88,15 +91,33 @@ private static > T toEnum(int value, Class enumType) {
}
public static int toInt32(byte[] bytes, int offset) {
- return (0xFF & bytes[offset]) | ((0xFF & bytes[offset + 1]) << 8) | ((0xFF & bytes[offset + 2]) << 16)
- | ((0xFF & bytes[offset + 3]) << 24);
+ return (0xFF & bytes[offset++]) | ((0xFF & bytes[offset++]) << 8) | ((0xFF & bytes[offset++]) << 16)
+ | ((0xFF & bytes[offset]) << 24);
}
public static long toInt64(byte[] bytes, int offset) {
- return (0xFFL & bytes[offset]) | ((0xFFL & bytes[offset + 1]) << 8) | ((0xFFL & bytes[offset + 2]) << 16)
- | ((0xFFL & bytes[offset + 3]) << 24) | ((0xFFL & bytes[offset + 4]) << 32)
- | ((0xFFL & bytes[offset + 5]) << 40) | ((0xFFL & bytes[offset + 6]) << 48)
- | ((0xFFL & bytes[offset + 7]) << 56);
+ return (0xFFL & bytes[offset++]) | ((0xFFL & bytes[offset++]) << 8) | ((0xFFL & bytes[offset++]) << 16)
+ | ((0xFFL & bytes[offset++]) << 24) | ((0xFFL & bytes[offset++]) << 32)
+ | ((0xFFL & bytes[offset++]) << 40) | ((0xFFL & bytes[offset++]) << 48)
+ | ((0xFFL & bytes[offset]) << 56);
+ }
+
+ public static void setInt32(byte[] bytes, int offset, int value) {
+ bytes[offset++] = (byte) (0xFF & value);
+ bytes[offset++] = (byte) (0xFF & (value >> 8));
+ bytes[offset++] = (byte) (0xFF & (value >> 16));
+ bytes[offset] = (byte) (0xFF & (value >> 24));
+ }
+
+ public static void setInt64(byte[] bytes, int offset, long value) {
+ bytes[offset++] = (byte) (0xFF & value);
+ bytes[offset++] = (byte) (0xFF & (value >> 8));
+ bytes[offset++] = (byte) (0xFF & (value >> 16));
+ bytes[offset++] = (byte) (0xFF & (value >> 24));
+ bytes[offset++] = (byte) (0xFF & (value >> 32));
+ bytes[offset++] = (byte) (0xFF & (value >> 40));
+ bytes[offset++] = (byte) (0xFF & (value >> 48));
+ bytes[offset] = (byte) (0xFF & (value >> 56));
}
/**
@@ -659,7 +680,7 @@ public static short readUnsignedInt8(ClickHouseInputStream input) throws IOExcep
* end of the stream
*/
public static void writeUnsignedInt8(OutputStream output, int value) throws IOException {
- output.write((byte) (ClickHouseChecker.between(value, ClickHouseValues.TYPE_INT, 0, U_INT8_MAX) & 0xFFL));
+ output.write((byte) (0xFF & ClickHouseChecker.between(value, ClickHouseValues.TYPE_INT, 0, U_INT8_MAX)));
}
/**
@@ -683,7 +704,7 @@ public static short readInt16(ClickHouseInputStream input) throws IOException {
* end of the stream
*/
public static void writeInt16(OutputStream output, short value) throws IOException {
- output.write(new byte[] { (byte) (0xFFL & value), (byte) (0xFFL & (value >> 8)) });
+ output.write(new byte[] { (byte) (0xFF & value), (byte) (0xFF & (value >> 8)) });
}
/**
@@ -746,8 +767,8 @@ public static int readInt32(ClickHouseInputStream input) throws IOException {
* end of the stream
*/
public static void writeInt32(OutputStream output, int value) throws IOException {
- output.write(new byte[] { (byte) (0xFFL & value), (byte) (0xFFL & (value >> 8)), (byte) (0xFFL & (value >> 16)),
- (byte) (0xFFL & (value >> 24)) });
+ output.write(new byte[] { (byte) (0xFF & value), (byte) (0xFF & (value >> 8)), (byte) (0xFF & (value >> 16)),
+ (byte) (0xFF & (value >> 24)) });
}
/**
@@ -796,14 +817,8 @@ public static long readInt64(ClickHouseInputStream input) throws IOException {
* end of the stream
*/
public static void writeInt64(OutputStream output, long value) throws IOException {
- value = Long.reverseBytes(value);
-
byte[] bytes = new byte[8];
- for (int i = 7; i >= 0; i--) {
- bytes[i] = (byte) (value & 0xFFL);
- value >>= 8;
- }
-
+ setInt64(bytes, 0, value);
output.write(bytes);
}
@@ -1403,11 +1418,7 @@ public static LocalDateTime readDateTime64(ClickHouseInputStream input, int scal
long value = readInt64(input);
int nanoSeconds = 0;
if (ClickHouseChecker.between(scale, ClickHouseValues.PARAM_SCALE, 0, 9) > 0) {
- int factor = 1;
- for (int i = 0; i < scale; i++) {
- factor *= 10;
- }
-
+ int factor = BASES[scale];
nanoSeconds = (int) (value % factor);
value /= factor;
if (nanoSeconds < 0) {
@@ -1415,9 +1426,7 @@ public static LocalDateTime readDateTime64(ClickHouseInputStream input, int scal
value--;
}
if (nanoSeconds > 0L) {
- for (int i = 9 - scale; i > 0; i--) {
- nanoSeconds *= 10;
- }
+ nanoSeconds *= BASES[9 - scale];
}
}
@@ -1456,15 +1465,10 @@ public static void writeDateTime64(OutputStream output, LocalDateTime value, int
: value.atZone(tz.toZoneId()).toEpochSecond(),
ClickHouseValues.TYPE_DATE_TIME, DATETIME64_MIN, DATETIME64_MAX);
if (ClickHouseChecker.between(scale, ClickHouseValues.PARAM_SCALE, 0, 9) > 0) {
- for (int i = 0; i < scale; i++) {
- v *= 10;
- }
+ v *= BASES[scale];
int nanoSeconds = value.getNano();
if (nanoSeconds > 0L) {
- for (int i = 9 - scale; i > 0; i--) {
- nanoSeconds /= 10;
- }
- v += nanoSeconds;
+ v += nanoSeconds / BASES[9 - scale];
}
}
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBlockChecksum.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBlockChecksum.java
deleted file mode 100644
index 00f31bdec..000000000
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBlockChecksum.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package com.clickhouse.client.data;
-
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-public class ClickHouseBlockChecksum {
- private final long first;
- private final long second;
-
- public ClickHouseBlockChecksum(long first, long second) {
- this.first = first;
- this.second = second;
- }
-
- public static ClickHouseBlockChecksum fromBytes(byte[] checksum) {
- ByteBuffer buffer = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN).put(checksum);
- ((Buffer) buffer).flip();
- return new ClickHouseBlockChecksum(buffer.getLong(), buffer.getLong());
- }
-
- public static ClickHouseBlockChecksum calculateForBlock(byte magic, int compressedSizeWithHeader,
- int uncompressedSize, byte[] data, int length) {
- ByteBuffer buffer = ByteBuffer.allocate(compressedSizeWithHeader).order(ByteOrder.LITTLE_ENDIAN)
- .put((byte) magic).putInt(compressedSizeWithHeader).putInt(uncompressedSize).put(data, 0, length);
- ((Buffer) buffer).flip();
- return calculate(buffer.array());
- }
-
- public byte[] asBytes() {
- ByteBuffer buffer = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN).putLong(first).putLong(second);
- ((Buffer) buffer).flip();
- return buffer.array();
- }
-
- private static ClickHouseBlockChecksum calculate(byte[] data) {
- long[] sum = ClickHouseCityHash.cityHash128(data, 0, data.length);
- return new ClickHouseBlockChecksum(sum[0], sum[1]);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- ClickHouseBlockChecksum that = (ClickHouseBlockChecksum) o;
-
- if (first != that.first)
- return false;
- return second == that.second;
- }
-
- @Override
- public int hashCode() {
- int result = (int) (first ^ (first >>> 32));
- result = 31 * result + (int) (second ^ (second >>> 32));
- return result;
- }
-
- @Override
- public String toString() {
- return "{" + first + ", " + second + '}';
- }
-}
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBoolValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBoolValue.java
new file mode 100644
index 000000000..5034670f2
--- /dev/null
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBoolValue.java
@@ -0,0 +1,267 @@
+package com.clickhouse.client.data;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import com.clickhouse.client.ClickHouseChecker;
+import com.clickhouse.client.ClickHouseValue;
+import com.clickhouse.client.ClickHouseValues;
+
+/**
+ * Wraper class of bool.
+ */
+public class ClickHouseBoolValue implements ClickHouseValue {
+ /**
+ * Create a new instance representing null value.
+ *
+ * @return new instance representing null value
+ */
+ public static ClickHouseBoolValue ofNull() {
+ return ofNull(null);
+ }
+
+ /**
+ * Update given value to null or create a new instance if {@code ref} is null.
+ *
+ * @param ref object to update, could be null
+ * @return same object as {@code ref} or a new instance if it's null
+ */
+ public static ClickHouseBoolValue ofNull(ClickHouseValue ref) {
+ return ref instanceof ClickHouseBoolValue ? ((ClickHouseBoolValue) ref).set(true, false)
+ : new ClickHouseBoolValue(true, false);
+ }
+
+ /**
+ * Wrap the given value.
+ *
+ * @param value value
+ * @return object representing the value
+ */
+ public static ClickHouseBoolValue of(boolean value) {
+ return of(null, value);
+ }
+
+ /**
+ * Wrap the given value.
+ *
+ * @param value value
+ * @return object representing the value
+ */
+ public static ClickHouseBoolValue of(int value) {
+ return of(null, value == 1);
+ }
+
+ /**
+ * Update value of the given object or create a new instance if {@code ref} is
+ * null.
+ *
+ * @param ref object to update, could be null
+ * @param value value
+ * @return same object as {@code ref} or a new instance if it's null
+ */
+ public static ClickHouseBoolValue of(ClickHouseValue ref, boolean value) {
+ return ref instanceof ClickHouseBoolValue ? ((ClickHouseBoolValue) ref).set(false, value)
+ : new ClickHouseBoolValue(false, value);
+ }
+
+ private boolean isNull;
+ private boolean value;
+
+ protected ClickHouseBoolValue(boolean isNull, boolean value) {
+ set(isNull, value);
+ }
+
+ protected ClickHouseBoolValue set(boolean isNull, boolean value) {
+ this.isNull = isNull;
+ this.value = !isNull && value;
+
+ return this;
+ }
+
+ /**
+ * Gets value.
+ *
+ * @return value
+ */
+ public boolean getValue() {
+ return value;
+ }
+
+ @Override
+ public ClickHouseBoolValue copy(boolean deep) {
+ return new ClickHouseBoolValue(isNull, value);
+ }
+
+ @Override
+ public boolean isNullOrEmpty() {
+ return isNull;
+ }
+
+ @Override
+ public byte asByte() {
+ return value ? (byte) 1 : (byte) 0;
+ }
+
+ @Override
+ public short asShort() {
+ return value ? (short) 1 : (short) 0;
+ }
+
+ @Override
+ public int asInteger() {
+ return value ? 1 : 0;
+ }
+
+ @Override
+ public long asLong() {
+ return value ? 1L : 0L;
+ }
+
+ @Override
+ public BigInteger asBigInteger() {
+ return isNull ? null : (value ? BigInteger.ONE : BigInteger.ZERO);
+ }
+
+ @Override
+ public float asFloat() {
+ return value ? 1F : 0F;
+ }
+
+ @Override
+ public double asDouble() {
+ return value ? 1D : 0D;
+ }
+
+ @Override
+ public BigDecimal asBigDecimal(int scale) {
+ return isNull ? null : (value ? BigDecimal.ONE : BigDecimal.ZERO);
+ }
+
+ @Override
+ public Object asObject() {
+ return isNull ? null : Boolean.valueOf(value);
+ }
+
+ @Override
+ public String asString(int length, Charset charset) {
+ if (isNull) {
+ return null;
+ }
+
+ String str = String.valueOf(value);
+ if (length > 0) {
+ ClickHouseChecker.notWithDifferentLength(str.getBytes(charset == null ? StandardCharsets.UTF_8 : charset),
+ length);
+ }
+
+ return str;
+ }
+
+ @Override
+ public ClickHouseBoolValue resetToNullOrEmpty() {
+ return set(true, false);
+ }
+
+ @Override
+ public String toSqlExpression() {
+ return isNull ? ClickHouseValues.NULL_EXPR : String.valueOf(value ? 1 : 0);
+ }
+
+ @Override
+ public ClickHouseBoolValue update(char value) {
+ return set(false, value == 1);
+ }
+
+ @Override
+ public ClickHouseBoolValue update(byte value) {
+ return set(false, value == (byte) 1);
+ }
+
+ @Override
+ public ClickHouseBoolValue update(short value) {
+ return set(false, value == (short) 1);
+ }
+
+ @Override
+ public ClickHouseBoolValue update(int value) {
+ return set(false, value == 1);
+ }
+
+ @Override
+ public ClickHouseBoolValue update(long value) {
+ return set(false, value == 1L);
+ }
+
+ @Override
+ public ClickHouseBoolValue update(float value) {
+ return set(false, value == 1F);
+ }
+
+ @Override
+ public ClickHouseBoolValue update(double value) {
+ return set(false, value == 1D);
+ }
+
+ @Override
+ public ClickHouseBoolValue update(BigInteger value) {
+ return value == null ? resetToNullOrEmpty() : set(false, BigInteger.ONE.equals(value));
+ }
+
+ @Override
+ public ClickHouseBoolValue update(BigDecimal value) {
+ return value == null ? resetToNullOrEmpty() : set(false, BigDecimal.ONE.equals(value));
+ }
+
+ @Override
+ public ClickHouseBoolValue update(Enum> value) {
+ return value == null ? resetToNullOrEmpty() : set(false, value.ordinal() == 1);
+ }
+
+ @Override
+ public ClickHouseBoolValue update(String value) {
+ return value == null ? resetToNullOrEmpty() : set(false, Boolean.parseBoolean(value) || "1".equals(value));
+ }
+
+ @Override
+ public ClickHouseBoolValue update(ClickHouseValue value) {
+ return value == null ? resetToNullOrEmpty() : set(false, value.asBoolean());
+ }
+
+ @Override
+ public ClickHouseBoolValue update(Object value) {
+ if (value instanceof Boolean) {
+ return set(false, (boolean) value);
+ } else if (value instanceof Number) {
+ return set(false, ((Number) value).byteValue() == (byte) 0);
+ } else if (value instanceof ClickHouseValue) {
+ return set(false, ((ClickHouseValue) value).asBoolean());
+ }
+
+ ClickHouseValue.super.update(value);
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) { // too bad this is a mutable class :<
+ return true;
+ } else if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ ClickHouseBoolValue v = (ClickHouseBoolValue) obj;
+ return isNull == v.isNull && value == v.value;
+ }
+
+ @Override
+ public int hashCode() {
+ // not going to use Objects.hash(isNull, value) due to autoboxing
+ return (31 + (isNull ? 1231 : 1237)) * 31 + (value ? 1231 : 1237);
+ }
+
+ @Override
+ public String toString() {
+ return ClickHouseValues.convertToString(this);
+ }
+}
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseEnumValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseEnumValue.java
index 0b32b93b9..93386febf 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseEnumValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseEnumValue.java
@@ -5,132 +5,220 @@
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import com.clickhouse.client.ClickHouseChecker;
+import com.clickhouse.client.ClickHouseEnum;
+import com.clickhouse.client.ClickHouseUtils;
import com.clickhouse.client.ClickHouseValue;
import com.clickhouse.client.ClickHouseValues;
/**
* Wraper class of enum.
*/
-public class ClickHouseEnumValue> extends ClickHouseObjectValue {
- static final String ERROR_NO_ENUM_TYPE = "Failed to convert value due to lack of enum type: ";
+public class ClickHouseEnumValue implements ClickHouseValue {
+ /**
+ * Create a new instance representing null value.
+ *
+ * @param clazz enum class
+ * @return new instance representing null value
+ */
+ public static ClickHouseEnumValue ofNull(Class extends Enum> clazz) {
+ return ofNull(null, ClickHouseEnum.of(clazz));
+ }
/**
* Create a new instance representing null value.
*
- * @param enum type
+ * @param type enum type, null is same as {@link ClickHouseEnum#EMPTY}
* @return new instance representing null value
*/
- public static > ClickHouseEnumValue ofNull() {
- return ofNull(null);
+ public static ClickHouseEnumValue ofNull(ClickHouseEnum type) {
+ return ofNull(null, type);
}
/**
* Update given value to null or create a new instance if {@code ref} is null.
*
- * @param enum type
- * @param ref object to update, could be null
+ * @param ref object to update, could be null
+ * @param clazz enum class
* @return same object as {@code ref} or a new instance if it's null
*/
- @SuppressWarnings("unchecked")
- public static > ClickHouseEnumValue ofNull(ClickHouseValue ref) {
- return ref instanceof ClickHouseEnumValue ? (ClickHouseEnumValue) ((ClickHouseEnumValue) ref).set(null)
- : new ClickHouseEnumValue<>(null);
+ public static ClickHouseEnumValue ofNull(ClickHouseValue ref, Class extends Enum> clazz) {
+ return ref instanceof ClickHouseEnumValue ? ((ClickHouseEnumValue) ref).set(true, 0)
+ : new ClickHouseEnumValue(ClickHouseEnum.of(clazz), true, 0);
+ }
+
+ /**
+ * Update given value to null or create a new instance if {@code ref} is null.
+ *
+ * @param ref object to update, could be null
+ * @param type enum type, null is same as {@link ClickHouseEnum#EMPTY}
+ * @return same object as {@code ref} or a new instance if it's null
+ */
+ public static ClickHouseEnumValue ofNull(ClickHouseValue ref, ClickHouseEnum type) {
+ return ref instanceof ClickHouseEnumValue ? ((ClickHouseEnumValue) ref).set(true, 0)
+ : new ClickHouseEnumValue(type, true, 0);
}
/**
* Wrap the given value.
*
- * @param enum type
* @param value value
* @return object representing the value
*/
- public static > ClickHouseEnumValue of(T value) {
+ public static ClickHouseEnumValue of(Enum> value) {
return of(null, value);
}
+ /**
+ * Wrap the given value.
+ *
+ * @param value value
+ * @param type enum type
+ * @return object representing the value
+ */
+ public static ClickHouseEnumValue of(ClickHouseEnum type, int value) {
+ return of(null, type, value);
+ }
+
+ /**
+ * Wrap the given value.
+ *
+ * @param value value
+ * @param type enum type
+ * @return object representing the value
+ */
+ public static ClickHouseEnumValue of(ClickHouseEnum type, Number value) {
+ return value == null ? ofNull(null, type) : of(null, type, value.intValue());
+ }
+
/**
* Update value of the given object or create a new instance if {@code ref} is
* null.
*
- * @param enum type
* @param ref object to update, could be null
* @param value value
* @return same object as {@code ref} or a new instance if it's null
*/
- @SuppressWarnings("unchecked")
- public static > ClickHouseEnumValue of(ClickHouseValue ref, T value) {
- return ref instanceof ClickHouseEnumValue
- ? (ClickHouseEnumValue) ((ClickHouseEnumValue) ref).update(value)
- : new ClickHouseEnumValue<>(value);
+ public static ClickHouseEnumValue of(ClickHouseValue ref, Enum> value) {
+ ClickHouseEnumValue v;
+ if (ref instanceof ClickHouseEnumValue) {
+ v = (ClickHouseEnumValue) ref;
+ if (value != null) {
+ v.set(false, value.ordinal());
+ } else {
+ v.resetToNullOrEmpty();
+ }
+ } else {
+ if (value != null) {
+ v = new ClickHouseEnumValue(ClickHouseEnum.of(value.getClass()), false, value.ordinal());
+ } else {
+ v = new ClickHouseEnumValue(ClickHouseEnum.EMPTY, true, 0);
+ }
+ }
+ return v;
}
- protected ClickHouseEnumValue(T value) {
- super(value);
+ /**
+ * Update value of the given object or create a new instance if {@code ref} is
+ * null.
+ *
+ * @param ref object to update, could be null
+ * @param type enum type, null is same as {@link ClickHouseEnum#EMPTY}
+ * @param value value
+ * @return same object as {@code ref} or a new instance if it's null
+ */
+ public static ClickHouseEnumValue of(ClickHouseValue ref, ClickHouseEnum type, int value) {
+ return ref instanceof ClickHouseEnumValue ? ((ClickHouseEnumValue) ref).set(false, value)
+ : new ClickHouseEnumValue(type, false, value);
+ }
+
+ private final ClickHouseEnum type;
+
+ private boolean isNull;
+ private int value;
+
+ protected ClickHouseEnumValue(ClickHouseEnum type, boolean isNull, int value) {
+ this.type = type != null ? type : ClickHouseEnum.EMPTY;
+
+ set(isNull, value);
+ }
+
+ protected ClickHouseEnumValue set(boolean isNull, int value) {
+ this.isNull = isNull;
+ this.value = isNull ? 0 : type.validate(value);
+ return this;
+ }
+
+ /**
+ * Gets value.
+ *
+ * @return value
+ */
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public ClickHouseEnumValue copy(boolean deep) {
+ return new ClickHouseEnumValue(type, isNull, value);
}
@Override
- public ClickHouseEnumValue copy(boolean deep) {
- return new ClickHouseEnumValue<>(getValue());
+ public boolean isNullOrEmpty() {
+ return isNull;
}
@Override
public byte asByte() {
- return isNullOrEmpty() ? (byte) 0 : (byte) getValue().ordinal();
+ return (byte) value;
}
@Override
public short asShort() {
- return isNullOrEmpty() ? (short) 0 : (short) getValue().ordinal();
+ return (short) value;
}
@Override
public int asInteger() {
- return isNullOrEmpty() ? 0 : getValue().ordinal();
+ return value;
}
@Override
public long asLong() {
- return isNullOrEmpty() ? 0L : getValue().ordinal();
+ return value;
}
@Override
public BigInteger asBigInteger() {
- return isNullOrEmpty() ? null : BigInteger.valueOf(getValue().ordinal());
+ return isNull ? null : BigInteger.valueOf(value);
}
@Override
public float asFloat() {
- return isNullOrEmpty() ? 0F : getValue().ordinal();
+ return value;
}
@Override
public double asDouble() {
- return isNullOrEmpty() ? 0D : getValue().ordinal();
+ return value;
}
@Override
public BigDecimal asBigDecimal(int scale) {
- return isNullOrEmpty() ? null : BigDecimal.valueOf(getValue().ordinal(), scale);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public > E asEnum(Class enumType) {
- return (E) getValue();
+ return isNull ? null : BigDecimal.valueOf(value, scale);
}
@Override
public Object asObject() {
- return getValue();
+ return isNull ? null : type.name(value);
}
@Override
public String asString(int length, Charset charset) {
- if (isNullOrEmpty()) {
+ if (isNull) {
return null;
}
- String str = String.valueOf(getValue().name());
+ String str = type.name(value);
if (length > 0) {
ClickHouseChecker.notWithDifferentLength(str.getBytes(charset == null ? StandardCharsets.UTF_8 : charset),
length);
@@ -140,130 +228,115 @@ public String asString(int length, Charset charset) {
}
@Override
- public String toSqlExpression() {
- return ClickHouseValues.convertToQuotedString(asString(0, null));
+ public ClickHouseEnumValue resetToNullOrEmpty() {
+ return set(true, (byte) 0);
}
@Override
- public ClickHouseEnumValue update(boolean value) {
- return update(value ? 1 : 0);
+ public String toSqlExpression() {
+ return isNull ? ClickHouseValues.NULL_EXPR
+ : new StringBuilder().append('\'').append(ClickHouseUtils.escape(type.name(value), '\'')).append('\'')
+ .toString();
}
@Override
- public ClickHouseEnumValue update(char value) {
- return update((int) value);
+ public ClickHouseEnumValue update(char value) {
+ return set(false, value);
}
@Override
- public ClickHouseEnumValue update(byte value) {
- return update((int) value);
+ public ClickHouseEnumValue update(byte value) {
+ return set(false, value);
}
@Override
- public ClickHouseEnumValue update(short value) {
- return update((int) value);
+ public ClickHouseEnumValue update(short value) {
+ return set(false, value);
}
@Override
- @SuppressWarnings("unchecked")
- public ClickHouseEnumValue update(int value) {
- if (isNullOrEmpty()) {
- throw new IllegalArgumentException(ERROR_NO_ENUM_TYPE + value);
- }
-
- Class clazz = (Class) getValue().getClass();
- for (T t : clazz.getEnumConstants()) {
- if (t.ordinal() == value) {
- return update(t);
- }
- }
-
- throw new IllegalArgumentException();
+ public ClickHouseEnumValue update(int value) {
+ return set(false, value);
}
@Override
- public ClickHouseEnumValue update(long value) {
- return update((int) value);
+ public ClickHouseEnumValue update(long value) {
+ return set(false, (int) value);
}
@Override
- public ClickHouseEnumValue update(float value) {
- return update((int) value);
+ public ClickHouseEnumValue update(float value) {
+ return set(false, (int) value);
}
@Override
- public ClickHouseEnumValue update(double value) {
- return update((int) value);
+ public ClickHouseEnumValue update(double value) {
+ return set(false, (int) value);
}
@Override
- public ClickHouseEnumValue update(BigInteger value) {
- if (value == null) {
- resetToNullOrEmpty();
- return this;
- }
+ public ClickHouseEnumValue update(BigInteger value) {
+ return value == null ? resetToNullOrEmpty() : set(false, value.intValueExact());
+ }
- return update(value.intValueExact());
+ @Override
+ public ClickHouseEnumValue update(BigDecimal value) {
+ return value == null ? resetToNullOrEmpty() : set(false, value.intValueExact());
}
@Override
- public ClickHouseEnumValue update(BigDecimal value) {
- if (value == null) {
- resetToNullOrEmpty();
- return this;
- }
+ public ClickHouseEnumValue update(Enum> value) {
+ return value == null ? resetToNullOrEmpty() : set(false, value.ordinal());
+ }
- return update(value.intValueExact());
+ @Override
+ public ClickHouseEnumValue update(String value) {
+ return value == null ? resetToNullOrEmpty() : set(false, type.value(value));
}
@Override
- @SuppressWarnings("unchecked")
- public ClickHouseEnumValue update(Enum> value) {
- set((T) value);
- return this;
+ public ClickHouseEnumValue update(ClickHouseValue value) {
+ return value == null ? resetToNullOrEmpty() : set(false, value.asInteger());
}
@Override
- @SuppressWarnings("unchecked")
- public ClickHouseEnumValue update(ClickHouseValue value) {
- if (value == null || value.isNullOrEmpty()) {
- resetToNullOrEmpty();
- } else if (value instanceof ClickHouseEnumValue) {
- set(((ClickHouseEnumValue) value).getValue());
- } else if (isNullOrEmpty()) {
- throw new IllegalArgumentException(ERROR_NO_ENUM_TYPE + value);
- } else {
- set(value.asEnum(isNullOrEmpty() ? null : (Class) getValue().getClass()));
+ public ClickHouseEnumValue update(Object value) {
+ if (value instanceof Number) {
+ return set(false, ((Number) value).intValue());
+ } else if (value instanceof String) {
+ return set(false, type.value((String) value));
+ } else if (value instanceof ClickHouseValue) {
+ return set(false, ((ClickHouseValue) value).asInteger());
}
+
+ ClickHouseValue.super.update(value);
return this;
}
@Override
- @SuppressWarnings("unchecked")
- public ClickHouseEnumValue update(String value) {
- if (value == null) {
- resetToNullOrEmpty();
- } else if (isNullOrEmpty()) {
- throw new IllegalArgumentException(ERROR_NO_ENUM_TYPE + value);
- } else {
- set((T) Enum.valueOf(getValue().getClass(), value));
+ public boolean equals(Object obj) {
+ if (this == obj) { // too bad this is a mutable class :<
+ return true;
+ } else if (obj == null || getClass() != obj.getClass()) {
+ return false;
}
- return this;
+ ClickHouseEnumValue v = (ClickHouseEnumValue) obj;
+ return isNull == v.isNull && value == v.value && type.equals(v.type);
}
@Override
- @SuppressWarnings("unchecked")
- public ClickHouseEnumValue update(Object value) {
- if (value instanceof Enum) {
- set((T) value);
- return this;
- } else if (value instanceof ClickHouseEnumValue) {
- set(((ClickHouseEnumValue) value).getValue());
- return this;
- }
+ public int hashCode() {
+ // not going to use Objects.hash(isNull, value) due to autoboxing
+ final int prime = 31;
+ int result = prime + (isNull ? 1231 : 1237);
+ result = prime * result + value;
+ result = prime * result + type.hashCode();
+ return result;
+ }
- super.update(value);
- return this;
+ @Override
+ public String toString() {
+ return ClickHouseValues.convertToString(this);
}
}
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseLZ4InputStream.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseLZ4InputStream.java
index d563f8096..0cee8bfed 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseLZ4InputStream.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseLZ4InputStream.java
@@ -3,13 +3,12 @@
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import com.clickhouse.client.ClickHouseChecker;
import com.clickhouse.client.ClickHouseInputStream;
+import com.clickhouse.client.ClickHouseUtils;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4FastDecompressor;
@@ -20,95 +19,84 @@
public class ClickHouseLZ4InputStream extends ClickHouseInputStream {
private static final LZ4Factory factory = LZ4Factory.fastestInstance();
- static final int MAGIC = 0x82;
+ static final byte MAGIC = (byte) 0x82;
+ static final int HEADER_LENGTH = 25;
+ private final LZ4FastDecompressor decompressor;
private final InputStream stream;
+ private final byte[] header;
- private ByteBuffer currentBlock;
+ private byte[] currentBlock;
+ private int position;
private boolean closed;
private boolean checkNext() throws IOException {
- if (currentBlock == null || !currentBlock.hasRemaining()) {
+ if (!closed && position >= currentBlock.length) {
currentBlock = readNextBlock();
}
- return currentBlock != null;
+ return currentBlock.length > 0;
}
// every block is:
- private ByteBuffer readNextBlock() throws IOException {
- int read = stream.read();
- if (read < 0) {
- return null;
- }
+ private byte[] readNextBlock() throws IOException {
+ position = 0;
- byte[] bytes = new byte[16];
- bytes[0] = (byte) read;
- // checksum - 16 bytes.
- readFully(bytes, 1, 15);
- ClickHouseBlockChecksum expected = ClickHouseBlockChecksum.fromBytes(bytes);
- // header:
- // 1 byte - 0x82 (shows this is LZ4)
- int magic = readUnsignedByteFromInput();
- if (magic != MAGIC) {
- throw new IOException("Magic is not correct: " + magic);
+ // checksum(16 bytes) + 1 magic byte + header(8 bytes)
+ if (!readFully(header, 0, HEADER_LENGTH)) {
+ return EMPTY_BYTES;
+ } else if (header[16] != MAGIC) {
+ // 1 byte - 0x82 (shows this is LZ4)
+ throw new IOException(
+ ClickHouseUtils.format("Magic is not correct - expect [%d] but got [%d]", MAGIC, header[16]));
}
- readFully(bytes, 0, 8);
// 4 bytes - size of the compressed data including 9 bytes of the header
- int compressedSizeWithHeader = BinaryStreamUtils.toInt32(bytes, 0);
+ int compressedSizeWithHeader = BinaryStreamUtils.toInt32(header, 17);
// 4 bytes - size of uncompressed data
- int uncompressedSize = BinaryStreamUtils.toInt32(bytes, 4);
- int compressedSize = compressedSizeWithHeader - 9; // header
- byte[] block = new byte[compressedSize];
- // compressed data: compressed_size - 9 байт.
- readFully(block, 0, block.length);
-
- ClickHouseBlockChecksum real = ClickHouseBlockChecksum.calculateForBlock((byte) magic, compressedSizeWithHeader,
- uncompressedSize, block, compressedSize);
- if (!real.equals(expected)) {
+ int uncompressedSize = BinaryStreamUtils.toInt32(header, 21);
+ int offset = 9;
+ byte[] block = new byte[compressedSizeWithHeader];
+ block[0] = header[16];
+ BinaryStreamUtils.setInt32(block, 1, compressedSizeWithHeader);
+ BinaryStreamUtils.setInt32(block, 5, uncompressedSize);
+ // compressed data: compressed_size - 9 bytes
+ if (!readFully(block, offset, compressedSizeWithHeader - offset)) {
+ throw new EOFException();
+ }
+
+ long[] real = ClickHouseCityHash.cityHash128(block, 0, block.length);
+ if (real[0] != BinaryStreamUtils.toInt64(header, 0) || real[1] != BinaryStreamUtils.toInt64(header, 8)) {
throw new IllegalArgumentException("Checksum doesn't match: corrupted data.");
}
byte[] decompressed = new byte[uncompressedSize];
- LZ4FastDecompressor decompressor = factory.fastDecompressor();
- decompressor.decompress(block, 0, decompressed, 0, uncompressedSize);
- return ByteBuffer.wrap(decompressed);
+ decompressor.decompress(block, offset, decompressed, 0, uncompressedSize);
+ return decompressed;
}
- private void readFully(byte b[], int off, int len) throws IOException {
- if (len < 0) {
- throw new IndexOutOfBoundsException();
- }
+ private boolean readFully(byte[] b, int off, int len) throws IOException {
int n = 0;
while (n < len) {
int count = stream.read(b, off + n, len - n);
if (count < 0) {
- try {
- close();
- } catch (IOException e) {
- // ignore
+ if (n == 0) {
+ return false;
}
throw new EOFException();
}
n += count;
}
- }
- private int readUnsignedByteFromInput() throws IOException {
- int ch = stream.read();
- if (ch < 0) {
- try {
- close();
- } catch (IOException e) {
- // ignore
- }
- throw new EOFException();
- }
- return ch;
+ return true;
}
public ClickHouseLZ4InputStream(InputStream stream) {
+ this.decompressor = factory.fastDecompressor();
this.stream = ClickHouseChecker.nonNull(stream, "InputStream");
+ this.header = new byte[HEADER_LENGTH];
+
+ this.currentBlock = EMPTY_BYTES;
+ this.position = 0;
this.closed = false;
}
@@ -123,7 +111,7 @@ public byte readByte() throws IOException {
throw new EOFException();
}
- return currentBlock.get();
+ return currentBlock[position++];
}
@Override
@@ -132,16 +120,16 @@ public int available() throws IOException {
return 0;
}
- int estimated = stream.available();
+ int estimated = currentBlock.length - position;
if (estimated == 0 && checkNext()) {
- estimated = currentBlock.remaining();
+ estimated = currentBlock.length - position;
}
return estimated;
}
@Override
public int read() throws IOException {
- return checkNext() ? 0xFF & currentBlock.get() : -1;
+ return checkNext() ? 0xFF & currentBlock[position++] : -1;
}
@Override
@@ -160,15 +148,15 @@ public int read(byte[] b, int off, int len) throws IOException {
int copied = 0;
while (copied != len) {
- int toCopy = Math.min(currentBlock.remaining(), len - copied);
- currentBlock.get(b, off, toCopy);
+ int toCopy = Math.min(currentBlock.length - position, len - copied);
+ System.arraycopy(currentBlock, position, b, off, toCopy);
+ position += toCopy;
off += toCopy;
copied += toCopy;
if (!checkNext()) {
break;
}
-
}
return copied;
@@ -205,10 +193,10 @@ public String readString(int byteLength, Charset charset) throws IOException {
charset = StandardCharsets.UTF_8;
}
- if (byteLength > 8 && currentBlock.remaining() > byteLength) {
- int pos = currentBlock.position();
- ((Buffer) currentBlock).position(pos + byteLength);
- return charset.decode(ByteBuffer.wrap(currentBlock.array(), pos, byteLength)).toString();
+ if (currentBlock.length - position > byteLength) {
+ int offset = position;
+ position += byteLength;
+ return new String(currentBlock, offset, byteLength, charset);
}
return new String(readBytes(byteLength), charset);
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseLZ4OutputStream.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseLZ4OutputStream.java
index c7da08d7a..4779edaea 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseLZ4OutputStream.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseLZ4OutputStream.java
@@ -1,14 +1,17 @@
package com.clickhouse.client.data;
-import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+
+import com.clickhouse.client.ClickHouseChecker;
+
import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory;
public class ClickHouseLZ4OutputStream extends OutputStream {
private static final LZ4Factory factory = LZ4Factory.fastestInstance();
- private final DataOutputStream dataWrapper;
+
+ private final OutputStream output;
private final LZ4Compressor compressor;
private final byte[] currentBlock;
@@ -17,23 +20,20 @@ public class ClickHouseLZ4OutputStream extends OutputStream {
private int pointer;
public ClickHouseLZ4OutputStream(OutputStream stream, int maxCompressBlockSize) {
- dataWrapper = new DataOutputStream(stream);
+ output = ClickHouseChecker.nonNull(stream, "output");
+
compressor = factory.fastCompressor();
currentBlock = new byte[maxCompressBlockSize];
- compressedBlock = new byte[compressor.maxCompressedLength(maxCompressBlockSize)];
- }
+ // reserve the first 9 bytes for calculating checksum
+ compressedBlock = new byte[compressor.maxCompressedLength(maxCompressBlockSize) + 15];
+ compressedBlock[16] = ClickHouseLZ4InputStream.MAGIC;
- /**
- * @return Location of pointer in the byte buffer (bytes not yet flushed)
- */
- public int position() {
- return pointer;
+ pointer = 0;
}
@Override
public void write(int b) throws IOException {
- currentBlock[pointer] = (byte) b;
- pointer++;
+ currentBlock[pointer++] = (byte) b;
if (pointer == currentBlock.length) {
writeBlock();
@@ -72,25 +72,18 @@ public void flush() throws IOException {
if (pointer != 0) {
writeBlock();
}
- dataWrapper.flush();
- }
-
- private void writeInt(int value) throws IOException {
- dataWrapper.write(0xFF & value);
- dataWrapper.write(0xFF & (value >> 8));
- dataWrapper.write(0xFF & (value >> 16));
- dataWrapper.write(0xFF & (value >> 24));
+ output.flush();
}
private void writeBlock() throws IOException {
- int compressed = compressor.compress(currentBlock, 0, pointer, compressedBlock, 0);
- ClickHouseBlockChecksum checksum = ClickHouseBlockChecksum.calculateForBlock(
- (byte) ClickHouseLZ4InputStream.MAGIC, compressed + 9, pointer, compressedBlock, compressed);
- dataWrapper.write(checksum.asBytes());
- dataWrapper.writeByte(ClickHouseLZ4InputStream.MAGIC);
- writeInt(compressed + 9); // compressed size with header
- writeInt(pointer); // uncompressed size
- dataWrapper.write(compressedBlock, 0, compressed);
+ int compressed = compressor.compress(currentBlock, 0, pointer, compressedBlock, 25);
+ int compressedSizeWithHeader = compressed + 9;
+ BinaryStreamUtils.setInt32(compressedBlock, 17, compressedSizeWithHeader); // compressed size with header
+ BinaryStreamUtils.setInt32(compressedBlock, 21, pointer); // uncompressed size
+ long[] hash = ClickHouseCityHash.cityHash128(compressedBlock, 16, compressedSizeWithHeader);
+ BinaryStreamUtils.setInt64(compressedBlock, 0, hash[0]);
+ BinaryStreamUtils.setInt64(compressedBlock, 8, hash[1]);
+ output.write(compressedBlock, 0, compressed + 25);
pointer = 0;
}
}
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseObjectValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseObjectValue.java
index 8fd94c8ff..45fe395c6 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseObjectValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseObjectValue.java
@@ -114,11 +114,6 @@ public Object asObject() {
return getValue();
}
- @Override
- public E asObject(Class clazz) {
- return ClickHouseChecker.nonNull(clazz, ClickHouseValues.TYPE_CLASS).cast(getValue());
- }
-
@Override
public String asString(int length, Charset charset) {
if (isNullOrEmpty()) {
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHousePipedStream.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHousePipedStream.java
index de4ca2f02..86f468812 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHousePipedStream.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHousePipedStream.java
@@ -81,7 +81,7 @@ public void close() throws IOException {
flush();
- buffer = ClickHouseInputStream.EMPTY;
+ buffer = ClickHouseInputStream.EMPTY_BUFFER;
try {
if (timeout > 0) {
if (!queue.offer(buffer, timeout, TimeUnit.MILLISECONDS)) {
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseRowBinaryProcessor.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseRowBinaryProcessor.java
index 79c2c8294..336653c42 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseRowBinaryProcessor.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseRowBinaryProcessor.java
@@ -163,18 +163,27 @@ private MappedFunctions() {
deserializers = new EnumMap<>(ClickHouseDataType.class);
serializers = new EnumMap<>(ClickHouseDataType.class);
- // enum and numbers
+ // enums
buildMappings(deserializers, serializers,
- (r, f, c, i) -> ClickHouseByteValue.of(r, BinaryStreamUtils.readInt8(i)),
+ (r, f, c, i) -> ClickHouseEnumValue.of(r, c.getEnumConstants(), BinaryStreamUtils.readInt8(i)),
(v, f, c, o) -> BinaryStreamUtils.writeInt8(o, v.asByte()), ClickHouseDataType.Enum,
- ClickHouseDataType.Enum8, ClickHouseDataType.Int8);
+ ClickHouseDataType.Enum8);
+ buildMappings(deserializers, serializers,
+ (r, f, c, i) -> ClickHouseEnumValue.of(r, c.getEnumConstants(), BinaryStreamUtils.readInt16(i)),
+ (v, f, c, o) -> BinaryStreamUtils.writeInt16(o, v.asShort()), ClickHouseDataType.Enum16);
+ // bool and numbers
+ buildMappings(deserializers, serializers,
+ (r, f, c, i) -> ClickHouseBoolValue.of(r, BinaryStreamUtils.readBoolean(i)),
+ (v, f, c, o) -> BinaryStreamUtils.writeBoolean(o, v.asBoolean()), ClickHouseDataType.Bool);
+ buildMappings(deserializers, serializers,
+ (r, f, c, i) -> ClickHouseByteValue.of(r, BinaryStreamUtils.readInt8(i)),
+ (v, f, c, o) -> BinaryStreamUtils.writeInt8(o, v.asByte()), ClickHouseDataType.Int8);
buildMappings(deserializers, serializers,
(r, f, c, i) -> ClickHouseShortValue.of(r, BinaryStreamUtils.readUnsignedInt8(i)),
(v, f, c, o) -> BinaryStreamUtils.writeUnsignedInt8(o, v.asInteger()), ClickHouseDataType.UInt8);
buildMappings(deserializers, serializers,
(r, f, c, i) -> ClickHouseShortValue.of(r, BinaryStreamUtils.readInt16(i)),
- (v, f, c, o) -> BinaryStreamUtils.writeInt16(o, v.asShort()), ClickHouseDataType.Enum16,
- ClickHouseDataType.Int16);
+ (v, f, c, o) -> BinaryStreamUtils.writeInt16(o, v.asShort()), ClickHouseDataType.Int16);
buildMappings(deserializers, serializers,
(r, f, c, i) -> ClickHouseIntegerValue.of(r, BinaryStreamUtils.readUnsignedInt16(i)),
(v, f, c, o) -> BinaryStreamUtils.writeUnsignedInt16(o, v.asInteger()), ClickHouseDataType.UInt16);
@@ -505,11 +514,11 @@ public boolean hasNext() {
@Override
public ClickHouseRecord next() {
- if (!hasNext()) {
+ ClickHouseRecord r = readNextRow();
+ if (r == null) {
throw new NoSuchElementException("No more record");
}
-
- return readNextRow();
+ return r;
}
}
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/AbstractClientTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/AbstractClientTest.java
index 8a4f34f28..da8616ada 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/AbstractClientTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/AbstractClientTest.java
@@ -19,7 +19,7 @@ protected Object[] newConnection(Object[] connection, ClickHouseNode server, Cli
if (connection != null) {
closeConnection(connection, false);
}
-
+
return new Object[] { request.getConfig(), server };
}
@@ -124,7 +124,11 @@ public void testInit() {
ClickHouseConfig config = new ClickHouseConfig();
sc.init(config);
- Assert.assertEquals(sc.getExecutor(), ClickHouseClient.getExecutorService());
+ if (config.getMaxThreadsPerClient() > 0) {
+ Assert.assertNotEquals(sc.getExecutor(), ClickHouseClient.getExecutorService());
+ } else {
+ Assert.assertEquals(sc.getExecutor(), ClickHouseClient.getExecutorService());
+ }
Assert.assertTrue(sc.isInitialized());
Assert.assertTrue(sc.getConfig() == config);
Assert.assertNull(sc.getServer());
@@ -133,7 +137,11 @@ public void testInit() {
ClickHouseConfig newConfig = new ClickHouseConfig();
sc.init(newConfig);
- Assert.assertEquals(sc.getExecutor(), ClickHouseClient.getExecutorService());
+ if (config.getMaxThreadsPerClient() > 0) {
+ Assert.assertNotEquals(sc.getExecutor(), ClickHouseClient.getExecutorService());
+ } else {
+ Assert.assertEquals(sc.getExecutor(), ClickHouseClient.getExecutorService());
+ }
Assert.assertTrue(sc.isInitialized());
Assert.assertTrue(sc.getConfig() != config);
Assert.assertEquals(sc.getConnection(req), new Object[] { req.getConfig(), req.getServer() });
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseColumnTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseColumnTest.java
index 105b4806e..b5a78c599 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseColumnTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseColumnTest.java
@@ -4,9 +4,14 @@
import java.util.LinkedList;
import java.util.List;
import org.testng.Assert;
+import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class ClickHouseColumnTest {
+ @DataProvider(name = "enumTypesProvider")
+ private Object[][] getEnumTypes() {
+ return new Object[][] { { "Enum" }, { "Enum8" }, { "Enum16" } };
+ }
@Test(groups = { "unit" })
public void testReadColumn() {
@@ -148,4 +153,20 @@ public void testArray() throws Exception {
Assert.assertEquals(c.getArrayBaseColumn().getOriginalTypeName(), "LowCardinality(Nullable(String))");
Assert.assertFalse(c.getArrayBaseColumn().isArray());
}
+
+ @Test(dataProvider = "enumTypesProvider", groups = { "unit" })
+ public void testEnum(String typeName) throws Exception {
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> ClickHouseColumn.of("e", typeName + "('Query''Start' = a)"));
+ Assert.assertThrows(IllegalArgumentException.class, () -> ClickHouseColumn.of("e", typeName + "(aa,1)"));
+ ClickHouseColumn column = ClickHouseColumn.of("e", typeName + "('Query''Start' = 1, 'Query\\'Finish' = 10)");
+ Assert.assertTrue(column.isEnum());
+ Assert.assertEquals(column.getDataType(), ClickHouseDataType.of(typeName));
+ Assert.assertThrows(IllegalArgumentException.class, () -> column.getEnumConstants().name(2));
+ Assert.assertThrows(IllegalArgumentException.class, () -> column.getEnumConstants().value(""));
+ Assert.assertEquals(column.getEnumConstants().name(1), "Query'Start");
+ Assert.assertEquals(column.getEnumConstants().name(10), "Query'Finish");
+ Assert.assertEquals(column.getEnumConstants().value("Query'Start"), 1);
+ Assert.assertEquals(column.getEnumConstants().value("Query'Finish"), 10);
+ }
}
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseDataTypeTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseDataTypeTest.java
index 1cdf1d5b4..7ccfb60b0 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseDataTypeTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseDataTypeTest.java
@@ -12,7 +12,7 @@ public void testAlias() {
}
for (ClickHouseDataType t : ClickHouseDataType.values()) {
- Assert.assertFalse(ClickHouseDataType.isAlias(t.name()));
+ Assert.assertFalse(ClickHouseDataType.isAlias(t.name()), t.name() + " should not be an alias");
}
}
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseExceptionTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseExceptionTest.java
index ad41f1bec..ca31c37b9 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseExceptionTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseExceptionTest.java
@@ -17,19 +17,19 @@ public void testConstructorWithCause() {
e = new ClickHouseException(233, (Throwable) null, server);
Assert.assertEquals(e.getErrorCode(), 233);
Assert.assertNull(e.getCause());
- Assert.assertEquals(e.getMessage(), "Unknown error 233 on server " + server);
+ Assert.assertEquals(e.getMessage(), "Unknown error 233, server " + server);
Throwable cause = new IllegalArgumentException();
e = new ClickHouseException(123, cause, server);
Assert.assertEquals(e.getErrorCode(), 123);
Assert.assertEquals(e.getCause(), cause);
- Assert.assertEquals(e.getMessage(), "Unknown error 123 on server " + server);
+ Assert.assertEquals(e.getMessage(), "Unknown error 123, server " + server);
cause = new IllegalArgumentException("Some error");
e = new ClickHouseException(111, cause, server);
Assert.assertEquals(e.getErrorCode(), 111);
Assert.assertEquals(e.getCause(), cause);
- Assert.assertEquals(e.getMessage(), "Some error on server " + server);
+ Assert.assertEquals(e.getMessage(), "Some error, server " + server);
}
@Test(groups = { "unit" })
@@ -43,17 +43,17 @@ public void testConstructorWithoutCause() {
e = new ClickHouseException(233, (String) null, server);
Assert.assertEquals(e.getErrorCode(), 233);
Assert.assertNull(e.getCause());
- Assert.assertEquals(e.getMessage(), "Unknown error 233 on server " + server);
+ Assert.assertEquals(e.getMessage(), "Unknown error 233, server " + server);
e = new ClickHouseException(123, "", server);
Assert.assertEquals(e.getErrorCode(), 123);
Assert.assertNull(e.getCause());
- Assert.assertEquals(e.getMessage(), "Unknown error 123 on server " + server);
+ Assert.assertEquals(e.getMessage(), "Unknown error 123, server " + server);
e = new ClickHouseException(111, "Some error", server);
Assert.assertEquals(e.getErrorCode(), 111);
Assert.assertNull(e.getCause());
- Assert.assertEquals(e.getMessage(), "Some error on server " + server);
+ Assert.assertEquals(e.getMessage(), "Some error, server " + server);
}
@Test(groups = { "unit" })
@@ -64,12 +64,12 @@ public void testHandleException() {
Assert.assertEquals(e.getErrorCode(), ClickHouseException.ERROR_UNKNOWN);
Assert.assertEquals(e.getCause(), cause);
Assert.assertEquals(e.getMessage(),
- "Unknown error " + ClickHouseException.ERROR_UNKNOWN + " on server " + server);
+ "Unknown error " + ClickHouseException.ERROR_UNKNOWN + ", server " + server);
e = ClickHouseException.of("Some error", server);
Assert.assertEquals(e.getErrorCode(), ClickHouseException.ERROR_UNKNOWN);
Assert.assertNull(e.getCause());
- Assert.assertEquals(e.getMessage(), "Some error on server " + server);
+ Assert.assertEquals(e.getMessage(), "Some error, server " + server);
Assert.assertEquals(e, ClickHouseException.of(e, server));
@@ -78,32 +78,32 @@ public void testHandleException() {
Assert.assertEquals(e.getErrorCode(), ClickHouseException.ERROR_UNKNOWN);
Assert.assertEquals(e.getCause(), cause);
Assert.assertEquals(e.getMessage(),
- "Unknown error " + ClickHouseException.ERROR_UNKNOWN + " on server " + server);
+ "Unknown error " + ClickHouseException.ERROR_UNKNOWN + ", server " + server);
e = ClickHouseException.of((ExecutionException) null, server);
Assert.assertEquals(e.getErrorCode(), ClickHouseException.ERROR_UNKNOWN);
Assert.assertNull(e.getCause());
Assert.assertEquals(e.getMessage(),
- "Unknown error " + ClickHouseException.ERROR_UNKNOWN + " on server " + server);
+ "Unknown error " + ClickHouseException.ERROR_UNKNOWN + ", server " + server);
cause = new ExecutionException(new ClickHouseException(-100, (Throwable) null, server));
e = ClickHouseException.of(cause, server);
Assert.assertEquals(e, cause.getCause());
Assert.assertEquals(e.getErrorCode(), -100);
Assert.assertNull(e.getCause());
- Assert.assertEquals(e.getMessage(), "Unknown error -100 on server " + server);
+ Assert.assertEquals(e.getMessage(), "Unknown error -100, server " + server);
cause = new ExecutionException(new IllegalArgumentException());
e = ClickHouseException.of(cause, server);
Assert.assertEquals(e.getErrorCode(), ClickHouseException.ERROR_UNKNOWN);
Assert.assertEquals(e.getCause(), cause.getCause());
Assert.assertEquals(e.getMessage(),
- "Unknown error " + ClickHouseException.ERROR_UNKNOWN + " on server " + server);
+ "Unknown error " + ClickHouseException.ERROR_UNKNOWN + ", server " + server);
cause = new ExecutionException(new IllegalArgumentException("Code: 12345. Something goes wrong..."));
e = ClickHouseException.of(cause, server);
Assert.assertEquals(e.getErrorCode(), 12345);
Assert.assertEquals(e.getCause(), cause.getCause());
- Assert.assertEquals(e.getMessage(), cause.getCause().getMessage() + " on server " + server);
+ Assert.assertEquals(e.getMessage(), cause.getCause().getMessage() + ", server " + server);
}
}
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseInputStreamTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseInputStreamTest.java
new file mode 100644
index 000000000..bdc91f32f
--- /dev/null
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseInputStreamTest.java
@@ -0,0 +1,211 @@
+package com.clickhouse.client;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class ClickHouseInputStreamTest {
+ private InputStream generateInputStream(byte[] bytes) {
+ if (bytes.length > 0) {
+ new Random().nextBytes(bytes);
+ }
+ return new BufferedInputStream(new ByteArrayInputStream(bytes));
+ }
+
+ @Test(groups = { "unit" })
+ public void testNullEmptyOrClosedInput() throws IOException {
+ Assert.assertThrows(IllegalArgumentException.class, () -> ClickHouseInputStream.of(null));
+ ClickHouseInputStream empty = ClickHouseInputStream
+ .of(generateInputStream(new byte[0]));
+ Assert.assertEquals(empty.isClosed(), false);
+ Assert.assertEquals(empty.available(), 0);
+ Assert.assertEquals(empty.read(), -1);
+ Assert.assertEquals(empty.read(), -1);
+ Assert.assertEquals(empty.read(new byte[1]), -1);
+ Assert.assertEquals(empty.read(new byte[1]), -1);
+ Assert.assertEquals(empty.readBytes(0), new byte[0]);
+ Assert.assertThrows(EOFException.class, () -> empty.readByte());
+ Assert.assertEquals(empty.isClosed(), true);
+ Assert.assertThrows(IOException.class, () -> empty.read());
+
+ ClickHouseInputStream empty1 = ClickHouseInputStream
+ .of(generateInputStream(new byte[0]));
+ Assert.assertEquals(empty1.isClosed(), false);
+ Assert.assertThrows(EOFException.class, () -> empty1.readBytes(1));
+ Assert.assertEquals(empty1.isClosed(), true);
+ Assert.assertThrows(IOException.class, () -> empty1.read());
+
+ InputStream in = generateInputStream(new byte[] { (byte) 123 });
+ in.close();
+ ClickHouseInputStream chIn = ClickHouseInputStream.of(in);
+ Assert.assertEquals(chIn.isClosed(), false);
+ Assert.assertThrows(IOException.class, () -> chIn.available());
+ Assert.assertEquals(chIn.isClosed(), false);
+ Assert.assertEquals(ClickHouseInputStream.of(chIn), chIn);
+ Assert.assertEquals(chIn.readBytes(0), new byte[0]);
+ Assert.assertThrows(IOException.class, () -> chIn.readBytes(1));
+ Assert.assertThrows(IOException.class, () -> chIn.read());
+ Assert.assertThrows(IOException.class, () -> chIn.readByte());
+ Assert.assertThrows(IOException.class, () -> chIn.read(new byte[0]));
+ chIn.close();
+ Assert.assertEquals(chIn.isClosed(), true);
+ }
+
+ @Test(groups = { "unit" })
+ public void testWrappedInput() throws IOException {
+ int sample = 10000;
+ byte[] bytes = new byte[sample];
+ try (InputStream in = generateInputStream(bytes); ClickHouseInputStream chIn = ClickHouseInputStream.of(in)) {
+ for (int i = 0; i < sample; i++) {
+ Assert.assertTrue(chIn.available() > 0);
+ Assert.assertEquals(chIn.readByte(), bytes[i]);
+ }
+
+ Assert.assertEquals(chIn.available(), 0);
+ Assert.assertFalse(chIn.isClosed(), "Should not be closed");
+
+ Assert.assertThrows(EOFException.class, () -> chIn.readByte());
+ Assert.assertTrue(chIn.isClosed(), "Should have been closed automatically");
+ }
+
+ try (InputStream in = generateInputStream(bytes); ClickHouseInputStream chIn = ClickHouseInputStream.of(in)) {
+ Assert.assertEquals(chIn.readBytes(sample), bytes);
+ Assert.assertFalse(chIn.isClosed(), "Should not be closed");
+ Assert.assertThrows(EOFException.class, () -> chIn.readBytes(1));
+ Assert.assertTrue(chIn.isClosed(), "Should have been closed automatically");
+ }
+ }
+
+ @Test(groups = { "unit" })
+ public void testNullOrEmptyBlockingInput() throws IOException {
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> ClickHouseInputStream.of((BlockingQueue) null, 0));
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> ClickHouseInputStream.of(new ArrayBlockingQueue<>(0), -1));
+
+ BlockingQueue queue = new ArrayBlockingQueue<>(1);
+ ClickHouseInputStream empty = ClickHouseInputStream.of(queue, 10);
+ Assert.assertEquals(empty.isClosed(), false);
+ Assert.assertThrows(IOException.class, () -> empty.available());
+ Assert.assertThrows(IOException.class, () -> empty.read());
+ Assert.assertThrows(IOException.class, () -> empty.read(new byte[1]));
+ Assert.assertEquals(empty.readBytes(0), new byte[0]);
+ Assert.assertThrows(IOException.class, () -> empty.readByte());
+ Assert.assertThrows(IOException.class, () -> empty.readBytes(1));
+ Assert.assertEquals(empty.isClosed(), false);
+
+ queue.offer(ClickHouseInputStream.EMPTY_BUFFER);
+ Assert.assertEquals(empty.available(), 0);
+ Assert.assertEquals(empty.read(), -1);
+ Assert.assertEquals(empty.read(), -1);
+ Assert.assertEquals(empty.read(new byte[1]), -1);
+ Assert.assertEquals(empty.read(new byte[2]), -1);
+ Assert.assertThrows(EOFException.class, () -> empty.readByte());
+ Assert.assertEquals(empty.isClosed(), true);
+ Assert.assertThrows(IOException.class, () -> empty.read());
+ }
+
+ @Test(groups = { "unit" })
+ public void testBlockingInput() throws IOException {
+ BlockingQueue queue = new LinkedBlockingQueue<>();
+ Random r = new Random();
+ byte[] values = new byte[1234567];
+ r.nextBytes(values);
+ for (int i = 0; i < values.length; i++) {
+ int len = values.length - i - 1;
+ if (len > 1024) {
+ len = r.nextInt(1024);
+ }
+ byte[] bytes = new byte[len + 1];
+ System.arraycopy(values, i, bytes, 0, bytes.length);
+ queue.offer(ByteBuffer.wrap(bytes));
+ i += bytes.length - 1;
+ }
+ queue.offer(ClickHouseInputStream.EMPTY_BUFFER);
+
+ ClickHouseInputStream in = ClickHouseInputStream.of(queue, 100);
+ for (int i = 0; i < values.length; i++) {
+ int length = Math.min(2048, values.length - i - 1) + 1;
+ Assert.assertTrue(in.available() > 0, "Should have at least " + length + " byte(s) to read");
+ Assert.assertEquals(in.readBytes(length), Arrays.copyOfRange(values, i, i + length));
+ i += length - 1;
+ }
+ Assert.assertFalse(in.isClosed(), "Should not be closed");
+ Assert.assertTrue(in.available() == 0, "Should have all bytes read");
+ in.close();
+ Assert.assertTrue(in.available() == 0, "Should have all bytes read");
+ Assert.assertTrue(in.isClosed(), "Should have been closed");
+ }
+
+ @Test(groups = { "unit" })
+ public void testBlockingInputAsync() throws IOException {
+ BlockingQueue queue = new LinkedBlockingQueue<>();
+ Random r = new Random();
+ byte[] values = new byte[1234567];
+ r.nextBytes(values);
+
+ new Thread(() -> {
+ for (int i = 0; i < values.length; i++) {
+ int len = values.length - i - 1;
+ if (len > 1024) {
+ len = r.nextInt(1024);
+ }
+ byte[] bytes = new byte[len + 1];
+ System.arraycopy(values, i, bytes, 0, bytes.length);
+ queue.offer(ByteBuffer.wrap(bytes));
+ i += bytes.length - 1;
+ }
+ queue.offer(ClickHouseInputStream.EMPTY_BUFFER);
+ }).start();
+ ClickHouseInputStream in = ClickHouseInputStream.of(queue, 0);
+ for (int i = 0; i < values.length; i++) {
+ int length = Math.min(2048, values.length - i - 1) + 1;
+ Assert.assertTrue(in.available() > 0, "Should have at least " + length + " byte(s) to read");
+ Assert.assertEquals(in.readBytes(length), Arrays.copyOfRange(values, i, i + length));
+ i += length - 1;
+ }
+ Assert.assertFalse(in.isClosed(), "Should not be closed");
+ Assert.assertTrue(in.available() == 0, "Should have all bytes read");
+ in.close();
+ Assert.assertTrue(in.available() == 0, "Should have all bytes read");
+ Assert.assertTrue(in.isClosed(), "Should have been closed");
+ }
+
+ @Test(groups = { "unit" })
+ public void testSkipInput() throws IOException {
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[0])).skip(0L), 0L);
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[0])).skip(1L), 0L);
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[0])).skip(Long.MAX_VALUE), 0L);
+
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[1])).skip(0L), 0L);
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[1])).skip(1L), 1L);
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[1])).skip(Long.MAX_VALUE), 1L);
+
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[2])).skip(0L), 0L);
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[2])).skip(1L), 1L);
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[2])).skip(Long.MAX_VALUE), 2L);
+
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[3]), 2).skip(0L), 0L);
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[3]), 2).skip(1L), 1L);
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[3]), 2).skip(2L), 2L);
+ Assert.assertEquals(ClickHouseInputStream.of(generateInputStream(new byte[3]), 2).skip(Long.MAX_VALUE), 3L);
+
+ ClickHouseInputStream in = ClickHouseInputStream.of(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4, 5 }), 2);
+ Assert.assertEquals(in.read(), 1);
+ Assert.assertEquals(in.skip(1L), 1L);
+ Assert.assertEquals(in.read(), 3);
+ Assert.assertEquals(in.skip(2L), 2L);
+ Assert.assertEquals(in.read(), -1);
+ }
+}
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseParameterizedQueryTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseParameterizedQueryTest.java
index 3335d5516..2ef7ad7cd 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseParameterizedQueryTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseParameterizedQueryTest.java
@@ -44,6 +44,19 @@ public void testApplyCollection() {
"select first::String,last + 1 as result");
Assert.assertEquals(q.apply(Arrays.asList(new String[] { "first", "last", "more" })),
"select first::String,last + 1 as result");
+
+ query = "select :p1 p1, :p2 p2, :p1 p3";
+ q = ClickHouseParameterizedQuery.of(query);
+ Assert.assertTrue(q.getOriginalQuery() == query);
+ Assert.assertEquals(q.apply((Collection) null), "select NULL p1, NULL p2, NULL p3");
+ Assert.assertEquals(q.apply(Collections.emptyList()), "select NULL p1, NULL p2, NULL p3");
+ Assert.assertEquals(q.apply(Collections.emptySet()), "select NULL p1, NULL p2, NULL p3");
+ Assert.assertEquals(q.apply(Arrays.asList(new String[] { "first" })),
+ "select first p1, NULL p2, first p3");
+ Assert.assertEquals(q.apply(Arrays.asList(new String[] { "first", "last" })),
+ "select first p1, last p2, first p3");
+ Assert.assertEquals(q.apply(Arrays.asList(new String[] { "first", "last", "more" })),
+ "select first p1, last p2, first p3");
}
@Test(groups = { "unit" })
@@ -71,6 +84,22 @@ public void testApplyObjects() {
Assert.assertEquals(q.apply(Arrays.asList(1, null)), "select (1,NULL)::String,NULL + 1 as result");
Assert.assertEquals(q.apply(Arrays.asList(ClickHouseDateTimeValue.ofNull(3).update(1), null)),
"select ('1970-01-01 00:00:00.001',NULL)::String,NULL + 1 as result");
+
+ query = "select :p1 p1, :p2 p2, :p1 p3";
+ q = ClickHouseParameterizedQuery.of(query);
+ Assert.assertTrue(q.getOriginalQuery() == query);
+ Assert.assertEquals(q.apply((Object) null), "select NULL p1, NULL p2, NULL p3");
+ Assert.assertEquals(q.apply((Object) null, (Object) null), "select NULL p1, NULL p2, NULL p3");
+ Assert.assertEquals(q.apply('a'), "select 97 p1, NULL p2, 97 p3");
+ Assert.assertEquals(q.apply(1, (Object) null), "select 1 p1, NULL p2, 1 p3");
+ Assert.assertEquals(q.apply(ClickHouseDateTimeValue.ofNull(3).update(1), (Object) null),
+ "select '1970-01-01 00:00:00.001' p1, NULL p2, '1970-01-01 00:00:00.001' p3");
+ Assert.assertEquals(q.apply(Collections.singletonList('a')), "select (97) p1, NULL p2, (97) p3");
+ Assert.assertEquals(q.apply(Arrays.asList(1, null)), "select (1,NULL) p1, NULL p2, (1,NULL) p3");
+ Assert.assertEquals(q.apply(Arrays.asList(ClickHouseDateTimeValue.ofNull(3).update(1), null)),
+ "select ('1970-01-01 00:00:00.001',NULL) p1, NULL p2, ('1970-01-01 00:00:00.001',NULL) p3");
+ Assert.assertEquals(q.apply(new StringBuilder("321"), new StringBuilder("123"), new StringBuilder("456")),
+ "select 321 p1, 123 p2, 321 p3");
}
@Test(groups = { "unit" })
@@ -116,6 +145,15 @@ public void testApplyStrings() {
Assert.assertEquals(q.apply((String) null, (String) null), "select null::String,null + 1 as result");
Assert.assertEquals(q.apply("'a'"), "select 'a'::String,NULL + 1 as result");
Assert.assertEquals(q.apply("1", (String) null), "select 1::String,null + 1 as result");
+
+ query = "select :p1 p1, :p2 p2, :p1 p3";
+ q = ClickHouseParameterizedQuery.of(query);
+ Assert.assertTrue(q.getOriginalQuery() == query);
+ Assert.assertEquals(q.apply((String) null), "select null p1, NULL p2, null p3");
+ Assert.assertEquals(q.apply((String) null, (String) null), "select null p1, null p2, null p3");
+ Assert.assertEquals(q.apply("'a'"), "select 'a' p1, NULL p2, 'a' p3");
+ Assert.assertEquals(q.apply("1", (String) null), "select 1 p1, null p2, 1 p3");
+ Assert.assertEquals(q.apply("1", "2", "3"), "select 1 p1, 2 p2, 1 p3");
}
@Test(groups = { "unit" })
@@ -195,7 +233,7 @@ public void testApplyTypedParameters() {
Assert.assertNull(templates[0]);
Assert.assertTrue(templates[1] instanceof ClickHouseDateTimeValue);
Assert.assertEquals(((ClickHouseDateTimeValue) templates[1]).getScale(), 0);
- Assert.assertEquals(pq.apply(ts, ts, ts), // shoud support only two parameters
+ Assert.assertEquals(pq.apply(ts, ts),
"select '1970-01-01 02:46:40.123456789' ts1, '1970-01-01 02:46:40' ts2, '1970-01-01 02:46:40' ts3");
}
}
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseValuesTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseValuesTest.java
index 410a966e8..27db2f679 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseValuesTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/ClickHouseValuesTest.java
@@ -193,6 +193,6 @@ public void testConvertToSqlExpression() throws UnknownHostException {
LocalDate.of(2021, 11, 12), LocalTime.of(11, 12, 13, 123456789),
LocalDateTime.of(LocalDate.of(2021, 11, 12), LocalTime.of(11, 12, 13, 123456789)),
new boolean[] { false, true } }),
- "[1,97,1,2,3,4,5.555,6.666666,'\\'x\\'','00000000-0000-0000-0000-000000000002','127.0.0.1','0:0:0:0:0:0:0:1',29,123456789,1.23456789,NULL,'2021-11-12','11:12:13.123456789','2021-11-12 11:12:13.123456789',[0,1]]");
+ "[1,97,1,2,3,4,5.555,6.666666,'\\'x\\'','00000000-0000-0000-0000-000000000002','127.0.0.1','0:0:0:0:0:0:0:1',30,123456789,1.23456789,NULL,'2021-11-12','11:12:13.123456789','2021-11-12 11:12:13.123456789',[0,1]]");
}
}
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickHouseEnumValueTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickHouseEnumValueTest.java
index bdfc88d0d..fd4789f92 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickHouseEnumValueTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickHouseEnumValueTest.java
@@ -9,31 +9,38 @@
public class ClickHouseEnumValueTest extends BaseClickHouseValueTest {
@Test(groups = { "unit" })
public void testCopy() {
- sameValue(ClickHouseEnumValue.ofNull(), ClickHouseEnumValue.ofNull(), 3, 9, Object.class,
+ sameValue(ClickHouseEnumValue.ofNull(ClickHouseDataType.class),
+ ClickHouseEnumValue.ofNull(ClickHouseDataType.class), 3, 9, Object.class,
ClickHouseDataType.class, Object.class, Object.class);
- sameValue(ClickHouseEnumValue.of(ClickHouseDataType.String), ClickHouseEnumValue.of(ClickHouseDataType.String),
+ sameValue(ClickHouseEnumValue.of(ClickHouseDataType.String),
+ ClickHouseEnumValue.of(ClickHouseDataType.String),
3, 9, Object.class, ClickHouseDataType.class, Object.class, Object.class);
- ClickHouseEnumValue v = ClickHouseEnumValue.of(ClickHouseDataType.Array);
+ ClickHouseEnumValue v = ClickHouseEnumValue.of(ClickHouseDataType.Array);
sameValue(v, v.copy(), 3, 9, Object.class, ClickHouseDataType.class, Object.class, Object.class);
}
@Test(groups = { "unit" })
public void testUpdate() {
- sameValue(ClickHouseEnumValue.ofNull(),
- ClickHouseEnumValue.ofNull().update(ClickHouseDataType.Date32).set(null), 3, 9, Object.class,
+ sameValue(ClickHouseEnumValue.ofNull(ClickHouseDataType.class),
+ ClickHouseEnumValue.ofNull(ClickHouseDataType.class).update(ClickHouseDataType.Date32).set(true, 0), 3,
+ 9,
+ Object.class,
ClickHouseDataType.class, Object.class, Object.class);
sameValue(ClickHouseEnumValue.of(ClickHouseDataType.Date32),
- ClickHouseEnumValue.ofNull().update(ClickHouseDataType.Date32), 3, 9, Object.class,
+ ClickHouseEnumValue.ofNull(ClickHouseDataType.class).update(ClickHouseDataType.Date32), 3, 9,
+ Object.class,
ClickHouseDataType.class, Object.class, Object.class);
sameValue(ClickHouseEnumValue.of(ClickHouseDataType.Date32),
- ClickHouseEnumValue.of(ClickHouseFormat.Arrow).update(ClickHouseDataType.Date32), 3, 9, Object.class,
+ ClickHouseEnumValue.of(ClickHouseDataType.Array).update(ClickHouseDataType.Date32), 3, 9,
+ Object.class,
ClickHouseDataType.class, Object.class, Object.class);
sameValue(ClickHouseEnumValue.of(ClickHouseDataType.IntervalYear),
ClickHouseEnumValue.of(ClickHouseDataType.String).update(false), 3, 9, Object.class,
ClickHouseDataType.class, Object.class, Object.class);
sameValue(ClickHouseEnumValue.of(ClickHouseDataType.IntervalYear),
- ClickHouseEnumValue.of(ClickHouseDataType.String).update(new boolean[] { false }), 3, 9, Object.class,
+ ClickHouseEnumValue.of(ClickHouseDataType.String).update(new boolean[] { false }), 3, 9,
+ Object.class,
ClickHouseDataType.class, Object.class, Object.class);
sameValue(ClickHouseEnumValue.of(ClickHouseDataType.IntervalYear),
ClickHouseEnumValue.of(ClickHouseDataType.String).update('\0'), 3, 9, Object.class,
@@ -51,7 +58,8 @@ public void testUpdate() {
ClickHouseEnumValue.of(ClickHouseDataType.String).update(0L), 3, 9, Object.class,
ClickHouseDataType.class, Object.class, Object.class);
sameValue(ClickHouseEnumValue.of(ClickHouseDataType.IntervalYear),
- ClickHouseEnumValue.of(ClickHouseDataType.String).update("IntervalYear"), 3, 9, Object.class,
+ ClickHouseEnumValue.of(ClickHouseDataType.String).update("IntervalYear"), 3, 9,
+ Object.class,
ClickHouseDataType.class, Object.class, Object.class);
}
}
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickHousePipedStreamTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickHousePipedStreamTest.java
index 9b2328c0b..66cc6bf73 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickHousePipedStreamTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickHousePipedStreamTest.java
@@ -50,7 +50,7 @@ public void testRead() throws Exception {
}
stream.queue.clear();
- stream.queue.put(ClickHouseInputStream.EMPTY);
+ stream.queue.put(ClickHouseInputStream.EMPTY_BUFFER);
Assert.assertEquals(stream.queue.size(), 1);
try (InputStream in = stream.getInput()) {
Assert.assertEquals(in.read(), -1);
@@ -58,7 +58,7 @@ public void testRead() throws Exception {
stream.queue.put((ByteBuffer) ((Buffer) buf).rewind());
// stream.queue.put(buf);
- stream.queue.put(ClickHouseInputStream.EMPTY);
+ stream.queue.put(ClickHouseInputStream.EMPTY_BUFFER);
Assert.assertEquals(stream.queue.size(), 2);
try (InputStream in = stream.getInput()) {
Assert.assertEquals(in.read(), 3);
@@ -122,7 +122,7 @@ public void testReadBytes() throws Exception {
buf = ByteBuffer.allocate(2).put(new byte[] { (byte) 3, (byte) 4 });
stream.queue.put((ByteBuffer) ((Buffer) buf).rewind());
- stream.queue.put(ClickHouseInputStream.EMPTY);
+ stream.queue.put(ClickHouseInputStream.EMPTY_BUFFER);
Assert.assertEquals(stream.queue.size(), 2);
try (InputStream in = stream.getInput()) {
Assert.assertEquals(in.read(bytes, 0, 3), 2);
diff --git a/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickhouseLZ4InputStreamTest.java b/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickhouseLZ4InputStreamTest.java
index 7b934e101..b136880da 100644
--- a/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickhouseLZ4InputStreamTest.java
+++ b/clickhouse-client/src/test/java/com/clickhouse/client/data/ClickhouseLZ4InputStreamTest.java
@@ -30,17 +30,17 @@ private InputStream generateInputStream(String prefix, int samples, StringBuilde
return new ByteArrayInputStream(result);
}
- @DataProvider(name = "prefixes")
- private Object[][] getPrefixes() {
- return new Object[][] { { "test" }, { "萌萌哒" },
- { "1😂2萌🥘" } };
+ @DataProvider(name = "samples")
+ private Object[][] getSamples() {
+ return new Object[][] { { "", 0 }, { "test", 100000 }, { "萌萌哒", 1024 * 1024 },
+ { "1😂2萌🥘", 2500000 } };
};
- @Test(dataProvider = "prefixes", groups = { "unit" })
- public void testReadByte(String prefix) throws IOException {
+ @Test(dataProvider = "samples", groups = { "unit" })
+ public void testReadByte(String prefix, int samples) throws IOException {
StringBuilder builder = new StringBuilder();
boolean readAll = false;
- try (InputStream in = generateInputStream(prefix, 10000, builder);
+ try (InputStream in = generateInputStream(prefix, samples, builder);
ClickHouseLZ4InputStream lz4In = new ClickHouseLZ4InputStream(in);
ByteArrayOutputStream out = new ByteArrayOutputStream();) {
try {
@@ -58,11 +58,34 @@ public void testReadByte(String prefix) throws IOException {
Assert.assertTrue(readAll, "All bytes should have read without any issue");
}
- @Test(dataProvider = "prefixes", groups = { "unit" })
- public void testRead(String prefix) throws IOException {
+ @Test(dataProvider = "samples", groups = { "unit" })
+ public void testReadByteWithAvailable(String prefix, int samples) throws IOException {
StringBuilder builder = new StringBuilder();
boolean readAll = false;
- try (InputStream in = generateInputStream(prefix, 10000, builder);
+ try (InputStream in = generateInputStream(prefix, samples, builder);
+ ClickHouseLZ4InputStream lz4In = new ClickHouseLZ4InputStream(in);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();) {
+ while (true) {
+ if (lz4In.available() == 0) {
+ readAll = true;
+ break;
+ }
+
+ out.write(0xFF & lz4In.readByte());
+ }
+
+ out.flush();
+
+ Assert.assertEquals(new String(out.toByteArray(), StandardCharsets.UTF_8), builder.toString());
+ }
+ Assert.assertTrue(readAll, "All bytes should have read without any issue");
+ }
+
+ @Test(dataProvider = "samples", groups = { "unit" })
+ public void testRead(String prefix, int samples) throws IOException {
+ StringBuilder builder = new StringBuilder();
+ boolean readAll = false;
+ try (InputStream in = generateInputStream(prefix, samples, builder);
ClickHouseLZ4InputStream lz4In = new ClickHouseLZ4InputStream(in);
ByteArrayOutputStream out = new ByteArrayOutputStream();) {
int result = 0;
@@ -77,13 +100,31 @@ public void testRead(String prefix) throws IOException {
Assert.assertTrue(readAll, "All bytes should have read without any issue");
}
- @Test(dataProvider = "prefixes", groups = { "unit" })
- public void testReadBytes(String prefix) throws IOException {
+ @Test(dataProvider = "samples", groups = { "unit" })
+ public void testReadWithAvailable(String prefix, int samples) throws IOException {
+ StringBuilder builder = new StringBuilder();
+ boolean readAll = false;
+ try (InputStream in = generateInputStream(prefix, samples, builder);
+ ClickHouseLZ4InputStream lz4In = new ClickHouseLZ4InputStream(in);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();) {
+ while (lz4In.available() > 0) {
+ out.write(lz4In.read());
+ }
+ out.flush();
+ readAll = true;
+
+ Assert.assertEquals(new String(out.toByteArray(), StandardCharsets.UTF_8), builder.toString());
+ }
+ Assert.assertTrue(readAll, "All bytes should have read without any issue");
+ }
+
+ @Test(dataProvider = "samples", groups = { "unit" })
+ public void testReadBytes(String prefix, int samples) throws IOException {
StringBuilder builder = new StringBuilder();
boolean readAll = false;
- for (int i = 1; i < 1025; i++) {
+ for (int i : new int[] { 1, 2, 3, 11, 1025 }) {
byte[] bytes = new byte[i];
- try (InputStream in = generateInputStream(prefix, 10000, builder);
+ try (InputStream in = generateInputStream(prefix, samples, builder);
ClickHouseLZ4InputStream lz4In = new ClickHouseLZ4InputStream(in);
ByteArrayOutputStream out = new ByteArrayOutputStream();) {
int result = 0;
@@ -99,6 +140,28 @@ public void testReadBytes(String prefix) throws IOException {
}
}
+ @Test(dataProvider = "samples", groups = { "unit" })
+ public void testReadBytesWithAvailable(String prefix, int samples) throws IOException {
+ StringBuilder builder = new StringBuilder();
+ boolean readAll = false;
+ for (int i : new int[] { 1, 2, 3, 11, 1025 }) {
+ byte[] bytes = new byte[i];
+ try (InputStream in = generateInputStream(prefix, samples, builder);
+ ClickHouseLZ4InputStream lz4In = new ClickHouseLZ4InputStream(in);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();) {
+ while (lz4In.available() > 0) {
+ int result = lz4In.read(bytes);
+ out.write(bytes, 0, result);
+ }
+ out.flush();
+ readAll = true;
+
+ Assert.assertEquals(new String(out.toByteArray(), StandardCharsets.UTF_8), builder.toString());
+ }
+ Assert.assertTrue(readAll, "All bytes should have read without any issue");
+ }
+ }
+
@Test(groups = { "unit" })
public void testLZ4Stream() throws IOException {
StringBuilder sb = new StringBuilder();
diff --git a/clickhouse-client/src/test/resources/log4j.properties b/clickhouse-client/src/test/resources/log4j.properties
deleted file mode 100644
index 2596a2ac7..000000000
--- a/clickhouse-client/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-plog4j.rootLogger=WARN, STDOUT
-log4j.category.com.clickhouse.client=DEBUG
-log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
-log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
-log4j.appender.STDOUT.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.sss} [%t] [%-5p] {%c{1}:%L} - %m%n
diff --git a/clickhouse-client/src/test/resources/simplelogger.properties b/clickhouse-client/src/test/resources/simplelogger.properties
new file mode 100644
index 000000000..b6db3b98b
--- /dev/null
+++ b/clickhouse-client/src/test/resources/simplelogger.properties
@@ -0,0 +1,7 @@
+org.slf4j.simpleLogger.defaultLogLevel=info
+org.slf4j.simpleLogger.log.com.clickhouse.client=info
+org.slf4j.simpleLogger.showDateTime=true
+org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z
+org.slf4j.simpleLogger.showThreadName=true
+org.slf4j.simpleLogger.showLogName=true
+org.slf4j.simpleLogger.showShortLogName=true
diff --git a/clickhouse-grpc-client/pom.xml b/clickhouse-grpc-client/pom.xml
index bcc28f1e3..9f480de05 100644
--- a/clickhouse-grpc-client/pom.xml
+++ b/clickhouse-grpc-client/pom.xml
@@ -61,7 +61,7 @@