From a4555cd119bbbed68444fae9c73e109bc1143989 Mon Sep 17 00:00:00 2001 From: sheyanjie-qq <249478495@qq.com> Date: Fri, 20 Dec 2024 15:44:24 +0800 Subject: [PATCH 1/4] add ws timezone and app info support --- .../com/taosdata/jdbc/AbstractDriver.java | 14 +- .../java/com/taosdata/jdbc/TSDBDriver.java | 5 +- .../taosdata/jdbc/TSDBResultSetBlockData.java | 13 +- .../taosdata/jdbc/common/SerializeBlock.java | 37 +++++- .../com/taosdata/jdbc/rs/ConnectionParam.java | 80 ++++++++++- .../com/taosdata/jdbc/rs/RestfulDriver.java | 3 +- .../taosdata/jdbc/rs/RestfulResultSet.java | 6 +- .../jdbc/utils/DataTypeConverUtil.java | 40 +++++- .../java/com/taosdata/jdbc/utils/Utils.java | 64 ++++++--- .../com/taosdata/jdbc/ws/BlockResultSet.java | 41 +++++- .../jdbc/ws/TSWSPreparedStatement.java | 5 +- .../java/com/taosdata/jdbc/ws/Transport.java | 11 +- .../com/taosdata/jdbc/ws/WSConnection.java | 22 ++- .../com/taosdata/jdbc/ws/WSStatement.java | 8 +- .../com/taosdata/jdbc/ws/WebSocketDriver.java | 2 +- .../taosdata/jdbc/ws/entity/ConnectReq.java | 47 +++++++ .../com/taosdata/jdbc/ws/tmq/WSConsumer.java | 13 +- .../jdbc/ws/tmq/WSConsumerResultSet.java | 12 +- .../jdbc/ws/tmq/entity/SubscribeReq.java | 29 ++++ .../jdbc/ws/tmq/entity/TMQRequestFactory.java | 25 ++-- .../jdbc/utils/DataTypeConverUtilTest.java | 58 ++++---- .../com/taosdata/jdbc/ws/WSAppInfoTest.java | 72 ++++++++++ .../jdbc/ws/WSConsumerResultSetTest.java | 2 +- .../com/taosdata/jdbc/ws/WSConsumerTest.java | 4 + .../jdbc/ws/WSDatabaseMetaDataTest.java | 2 +- .../com/taosdata/jdbc/ws/WSTimeZoneTest.java | 125 ++++++++++++++++++ .../ws/tmq/entity/TMQRequestFactoryTest.java | 35 ++++- 27 files changed, 628 insertions(+), 147 deletions(-) create mode 100644 src/test/java/com/taosdata/jdbc/ws/WSAppInfoTest.java create mode 100644 src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java diff --git a/src/main/java/com/taosdata/jdbc/AbstractDriver.java b/src/main/java/com/taosdata/jdbc/AbstractDriver.java index 1d4f5d30..24e0788a 100644 --- a/src/main/java/com/taosdata/jdbc/AbstractDriver.java +++ b/src/main/java/com/taosdata/jdbc/AbstractDriver.java @@ -7,7 +7,9 @@ import com.taosdata.jdbc.enums.WSFunction; import com.taosdata.jdbc.rs.ConnectionParam; import com.taosdata.jdbc.utils.JsonUtil; +import com.taosdata.jdbc.utils.ReqId; import com.taosdata.jdbc.utils.StringUtils; +import com.taosdata.jdbc.utils.Utils; import com.taosdata.jdbc.ws.*; import com.taosdata.jdbc.ws.entity.*; import org.slf4j.LoggerFactory; @@ -100,17 +102,7 @@ protected Connection getWSConnection(String url, ConnectionParam param, Properti transport.checkConnection(param.getConnectTimeout()); - ConnectReq connectReq = new ConnectReq(); - connectReq.setReqId(1); - connectReq.setUser(param.getUser()); - connectReq.setPassword(param.getPassword()); - connectReq.setDb(param.getDatabase()); - - // Currently, only BI mode is supported. The downstream interface value is 0, so a conversion is performed here. - if(param.getConnectMode() == ConnectionParam.CONNECT_MODE_BI){ - connectReq.setMode(0); - } - + ConnectReq connectReq = new ConnectReq(param); ConnectResp auth = (ConnectResp) transport.send(new Request(Action.CONN.getAction(), connectReq)); if (Code.SUCCESS.getCode() != auth.getCode()) { diff --git a/src/main/java/com/taosdata/jdbc/TSDBDriver.java b/src/main/java/com/taosdata/jdbc/TSDBDriver.java index 78150b09..6218fa67 100755 --- a/src/main/java/com/taosdata/jdbc/TSDBDriver.java +++ b/src/main/java/com/taosdata/jdbc/TSDBDriver.java @@ -119,6 +119,9 @@ public class TSDBDriver extends AbstractDriver { public static final String PROPERTY_KEY_RECONNECT_RETRY_COUNT = "reconnectRetryCount"; public static final String PROPERTY_KEY_DISABLE_SSL_CERT_VALIDATION = "disableSSLCertValidation"; + public static final String PROPERTY_KEY_APP_IP = "app_ip"; + public static final String PROPERTY_KEY_APP_NAME = "app_name"; + /** * max message number send to server concurrently @@ -145,8 +148,6 @@ public class TSDBDriver extends AbstractDriver { */ public static final String HTTP_SOCKET_TIMEOUT = "httpSocketTimeout"; - public static final String HTTP_TIME_ZONE= "tz"; - private TSDBDatabaseMetaData dbMetaData = null; static { diff --git a/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java b/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java index c886b40f..6b7def24 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java +++ b/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java @@ -14,25 +14,18 @@ *****************************************************************************/ package com.taosdata.jdbc; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; -import com.google.common.primitives.Shorts; -import com.taosdata.jdbc.enums.TimestampPrecision; import com.taosdata.jdbc.utils.DataTypeConverUtil; import com.taosdata.jdbc.utils.Utils; import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.sql.SQLDataException; import java.sql.SQLException; import java.sql.Timestamp; -import java.sql.Types; -import java.time.Instant; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.*; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import static com.taosdata.jdbc.TSDBConstants.*; import static com.taosdata.jdbc.utils.UnsignedDataUtils.*; @@ -387,7 +380,7 @@ public Timestamp getTimestamp(int col) throws SQLException { String charset = TaosGlobalConfig.getCharset(); try { tmp = new String((byte[]) obj, charset); - return Utils.parseTimestamp(tmp); + return Utils.parseTimestamp(tmp, null); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage()); } diff --git a/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java b/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java index 0f428060..8d64fc57 100644 --- a/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java +++ b/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java @@ -6,6 +6,9 @@ import java.io.IOException; import java.sql.SQLException; import java.sql.Timestamp; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; @@ -59,6 +62,18 @@ private static void SerializeShort(byte[] buf, int offset, short v){ buf[offset] = (byte) (v & 0xFF); buf[offset + 1] = (byte) ((v >> 8) & 0xFF); } + + private static Long getLongFromTimestamp(Timestamp o, int precision){ + long v; + if (precision == TimestampPrecision.MS) { + v = o.getTime(); + } else if (precision == TimestampPrecision.US) { + v = o.getTime() * 1000L + o.getNanos() / 1000 % 1000; + } else { + v = o.getTime() * 1000_000L + o.getNanos() % 1000_000L; + } + return v; + } private static void handleNormalDataType(int dataType ,byte[] buf, int rowIndex, int startOffset, Object o, int precision) throws SQLException { switch (dataType) { case TSDB_DATA_TYPE_BOOL: { @@ -102,14 +117,24 @@ private static void handleNormalDataType(int dataType ,byte[] buf, int rowIndex, break; } case TSDB_DATA_TYPE_TIMESTAMP: { - Timestamp t = (Timestamp) o; long v; - if (precision == TimestampPrecision.MS) { - v = t.getTime(); - } else if (precision == TimestampPrecision.US) { - v = t.getTime() * 1000L + t.getNanos() / 1000 % 1000; + if (o instanceof Timestamp) { + Timestamp t = (Timestamp) o; + v = getLongFromTimestamp(t, precision); + } else if (o instanceof Instant){ + Instant t = (Instant) o; + Timestamp ts = Timestamp.from(t); + v = getLongFromTimestamp(ts, precision); + } else if (o instanceof OffsetDateTime){ + OffsetDateTime t = (OffsetDateTime) o; + Timestamp ts = Timestamp.from(t.toInstant()); + v = getLongFromTimestamp(ts, precision); + } else if (o instanceof ZonedDateTime){ + ZonedDateTime t = (ZonedDateTime) o; + Timestamp ts = Timestamp.from(t.toInstant()); + v = getLongFromTimestamp(ts, precision); } else { - v = t.getTime() * 1000_000L + t.getNanos() % 1000_000L; + throw new SQLException("unsupported data type : " + o.getClass().getName()); } int offset = rowIndex * Long.BYTES + startOffset; diff --git a/src/main/java/com/taosdata/jdbc/rs/ConnectionParam.java b/src/main/java/com/taosdata/jdbc/rs/ConnectionParam.java index 8055003b..3df2db38 100644 --- a/src/main/java/com/taosdata/jdbc/rs/ConnectionParam.java +++ b/src/main/java/com/taosdata/jdbc/rs/ConnectionParam.java @@ -4,12 +4,16 @@ import com.taosdata.jdbc.TSDBError; import com.taosdata.jdbc.TSDBErrorNumbers; import com.taosdata.jdbc.utils.HttpClientPoolUtil; +import com.taosdata.jdbc.utils.StringUtils; +import com.taosdata.jdbc.utils.Utils; import com.taosdata.jdbc.ws.Transport; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.sql.SQLException; +import java.time.DateTimeException; +import java.time.ZoneId; import java.util.Properties; public class ConnectionParam { @@ -20,6 +24,7 @@ public class ConnectionParam { private String user; private String password; private String tz; + private ZoneId zoneId; private boolean useSsl; private int maxRequest; private int connectTimeout; @@ -33,6 +38,8 @@ public class ConnectionParam { private int reconnectIntervalMs; private int reconnectRetryCount; private boolean disableSslCertValidation; + private String appName; + private String appIp; static public final int CONNECT_MODE_BI = 1; @@ -56,6 +63,8 @@ private ConnectionParam(Builder builder) { this.reconnectRetryCount = builder.reconnectRetryCount; this.enableAutoConnect = builder.enableAutoReconnect; this.disableSslCertValidation = builder.disableSslCertValidation; + this.appName = builder.appName; + this.appIp = builder.appIp; } public String getHost() { @@ -113,7 +122,13 @@ public String getTz() { public void setTz(String tz) { this.tz = tz; } + public ZoneId getZoneId() { + return zoneId; + } + public void setZoneId(ZoneId zoneId) { + this.zoneId = zoneId; + } public boolean isUseSsl() { return useSsl; } @@ -208,6 +223,46 @@ public void setDisableSslCertValidation(boolean disableSslCertValidation) { this.disableSslCertValidation = disableSslCertValidation; } + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getAppIp() { + return appIp; + } + + public void setAppIp(String appIp) { + this.appIp = appIp; + } + + public static ConnectionParam getParamWs(Properties perperties) throws SQLException { + ConnectionParam connectionParam = getParam(perperties); + if (connectionParam.getTz().contains("+") + || connectionParam.getTz().contains("-") + || !connectionParam.getTz().contains("/")){ + // for history reason, we will not support time zone with offset in websocket connection + connectionParam.setTz(""); + } + + try { + ZoneId zoneId = ZoneId.of(connectionParam.getTz()); + ZoneId defaultZoneId = ZoneId.systemDefault(); + if (!StringUtils.isEmpty(connectionParam.getTz()) && defaultZoneId.equals(zoneId)){ + // for performance, if the time zone is the same as the system default time zone, we ignore it + connectionParam.setTz(""); + } else { + connectionParam.setZoneId(zoneId); + } + } catch (DateTimeException e) { + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "invalid time zone"); + } + return connectionParam; + } + public static ConnectionParam getParam(Properties properties) throws SQLException { String host = properties.getProperty(TSDBDriver.PROPERTY_KEY_HOST); String port = properties.getProperty(TSDBDriver.PROPERTY_KEY_PORT); @@ -243,7 +298,7 @@ public static ConnectionParam getParam(Properties properties) throws SQLExceptio + ", password: " + properties.getProperty(TSDBDriver.PROPERTY_KEY_PASSWORD)); } - String tz = properties.getProperty(TSDBDriver.HTTP_TIME_ZONE); + String tz = properties.getProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE); boolean useSsl = Boolean.parseBoolean(properties.getProperty(TSDBDriver.PROPERTY_KEY_USE_SSL, "false")); @@ -279,6 +334,16 @@ public static ConnectionParam getParam(Properties properties) throws SQLExceptio boolean enableAutoReconnect = Boolean.parseBoolean(properties.getProperty(TSDBDriver.PROPERTY_KEY_ENABLE_AUTO_RECONNECT,"false")); boolean disableSslCertValidation = Boolean.parseBoolean(properties.getProperty(TSDBDriver.PROPERTY_KEY_DISABLE_SSL_CERT_VALIDATION,"false")); + String appName = properties.getProperty(TSDBDriver.PROPERTY_KEY_APP_NAME, "java"); + if (appName.getBytes().length > 23){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "invalid app name, max length is 23"); + } + + String appIp = properties.getProperty(TSDBDriver.PROPERTY_KEY_APP_IP, ""); + if (!Utils.isValidIP(appIp)){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "invalid app ip address"); + } + return new Builder(host, port) .setDatabase(database) .setCloudToken(cloudToken) @@ -296,6 +361,8 @@ public static ConnectionParam getParam(Properties properties) throws SQLExceptio .setReconnectRetryCount(reconnectRetryCount) .setEnableAutoReconnect(enableAutoReconnect) .setDisableSslCertValidation(disableSslCertValidation) + .setAppIp(appIp) + .setAppName(appName) .build(); } @@ -321,6 +388,9 @@ public static class Builder { private int reconnectRetryCount; private boolean disableSslCertValidation; + private String appName; + private String appIp; + public Builder(String host, String port) { this.host = host; this.port = port; @@ -404,7 +474,15 @@ public Builder setDisableSslCertValidation(boolean disableSslCertValidation) { this.disableSslCertValidation = disableSslCertValidation; return this; } + public Builder setAppName(String appName) { + this.appName = appName; + return this; + } + public Builder setAppIp(String appIp) { + this.appIp = appIp; + return this; + } public ConnectionParam build() { return new ConnectionParam(this); } diff --git a/src/main/java/com/taosdata/jdbc/rs/RestfulDriver.java b/src/main/java/com/taosdata/jdbc/rs/RestfulDriver.java index d0442a75..be907800 100644 --- a/src/main/java/com/taosdata/jdbc/rs/RestfulDriver.java +++ b/src/main/java/com/taosdata/jdbc/rs/RestfulDriver.java @@ -44,12 +44,13 @@ public Connection connect(String url, Properties info) throws SQLException { return null; Properties props = parseURL(url, info); - ConnectionParam param = ConnectionParam.getParam(props); String batchLoad = info.getProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD); if (Boolean.parseBoolean(batchLoad)) { + ConnectionParam param = ConnectionParam.getParamWs(props); return getWSConnection(url, param, props); } HttpClientPoolUtil.init(props); + ConnectionParam param = ConnectionParam.getParam(props); String auth = null; diff --git a/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java b/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java index 69f9e1a2..f1ac0a71 100644 --- a/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java +++ b/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java @@ -413,7 +413,7 @@ public Date getDate(int columnIndex) throws SQLException { return null; if (value instanceof Timestamp) return new Date(((Timestamp) value).getTime()); - return Utils.parseDate(value.toString()); + return Utils.parseDate(value.toString(), null); } @Override @@ -428,7 +428,7 @@ public Time getTime(int columnIndex) throws SQLException { return new Time(((Timestamp) value).getTime()); Time time = null; try { - time = Utils.parseTime(value.toString()); + time = Utils.parseTime(value.toString(), null); } catch (DateTimeParseException ignored) { } return time; @@ -453,7 +453,7 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { } Timestamp ret; try { - ret = Utils.parseTimestamp(value.toString()); + ret = Utils.parseTimestamp(value.toString(), null); } catch (Exception e) { ret = null; wasNull = true; diff --git a/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java b/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java index 2863fa44..a5a18578 100644 --- a/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java +++ b/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java @@ -12,6 +12,7 @@ import java.math.BigDecimal; import java.sql.*; import java.time.Instant; +import java.time.ZoneId; import java.time.format.DateTimeParseException; import static com.taosdata.jdbc.TSDBConstants.*; @@ -475,7 +476,7 @@ public static String getString(Object value) throws SQLException { return value.toString(); } - public static Date getDate(Object value) { + public static Date getDate(Object value, ZoneId zoneId) { if (value instanceof Timestamp) return new Date(((Timestamp) value).getTime()); if (value instanceof byte[]) { @@ -486,12 +487,12 @@ public static Date getDate(Object value) { } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage()); } - return Utils.parseDate(tmp); + return Utils.parseDate(tmp, zoneId); } - return Utils.parseDate(value.toString()); + return Utils.parseDate(value.toString(), zoneId); } - public static Time getTime(Object value) { + public static Time getTime(Object value, ZoneId zoneId) { if (value instanceof Timestamp) return new Time(((Timestamp) value).getTime()); String tmp = ""; @@ -507,7 +508,7 @@ public static Time getTime(Object value) { } Time time = null; try { - time = Utils.parseTime(tmp); + time = Utils.parseTime(tmp, zoneId); } catch (DateTimeParseException e) { throw new RuntimeException(e.getMessage()); } @@ -556,7 +557,7 @@ public static BigDecimal getBigDecimal(int taosType, Object value) { return new BigDecimal(0); } - static public Object parseValue(int type, Object source, int timestampPrecision){ + static public Object parseValue(int type, Object source, int timestampPrecision, ZoneId zoneId){ switch (type) { case TSDB_DATA_TYPE_BOOL: { byte val = (byte) source; @@ -588,7 +589,7 @@ static public Object parseValue(int type, Object source, int timestampPrecision) } case TSDB_DATA_TYPE_TIMESTAMP: { long val = (long) source; - return parseTimestampColumnData(val, timestampPrecision); + return parseTimestampColumnDataWithZoneId(val, timestampPrecision, zoneId); } case TSDB_DATA_TYPE_UBIGINT: { long val = (long) source; @@ -620,5 +621,30 @@ public static Timestamp parseTimestampColumnData(long value, int timestampPrecis } return null; } + public static Timestamp parseTimestampColumnDataWithZoneId(long value, int timestampPrecision, ZoneId zoneId) { + if (zoneId == null){ + return parseTimestampColumnData(value, timestampPrecision); + } + + if (TimestampPrecision.MS == timestampPrecision) { + Instant instant1 = Instant.ofEpochMilli(value); + Instant instant = instant1.atZone(zoneId).toInstant(); + return Timestamp.from(instant); + } + + if (TimestampPrecision.US == timestampPrecision) { + long epochSec = value / 1000_000L; + long nanoAdjustment = value % 1000_000L * 1000L; + Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment); + return Timestamp.from(instant.atZone(zoneId).toInstant()); + } + if (TimestampPrecision.NS == timestampPrecision) { + long epochSec = value / 1000_000_000L; + long nanoAdjustment = value % 1000_000_000L; + Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment); + return Timestamp.from(instant.atZone(zoneId).toInstant()); + } + return null; + } } diff --git a/src/main/java/com/taosdata/jdbc/utils/Utils.java b/src/main/java/com/taosdata/jdbc/utils/Utils.java index b1a52ad8..7c85adb3 100644 --- a/src/main/java/com/taosdata/jdbc/utils/Utils.java +++ b/src/main/java/com/taosdata/jdbc/utils/Utils.java @@ -3,13 +3,19 @@ import com.google.common.collect.Range; import com.google.common.collect.RangeSet; import com.google.common.collect.TreeRangeSet; +import com.taosdata.jdbc.rs.ConnectionParam; +import com.taosdata.jdbc.ws.entity.ConnectReq; import java.lang.reflect.Constructor; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; @@ -27,43 +33,61 @@ public class Utils { private static final DateTimeFormatter microSecFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSSSSS").toFormatter(); private static final DateTimeFormatter nanoSecFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS").toFormatter(); - public static Time parseTime(String timestampStr) throws DateTimeParseException { - LocalDateTime dateTime = parseLocalDateTime(timestampStr); + public static Time parseTime(String timestampStr, ZoneId zoneId) throws DateTimeParseException { + LocalDateTime dateTime = parseLocalDateTime(timestampStr, zoneId); return dateTime != null ? Time.valueOf(dateTime.toLocalTime()) : null; } - public static Date parseDate(String timestampStr) { - LocalDateTime dateTime = parseLocalDateTime(timestampStr); + public static Date parseDate(String timestampStr, ZoneId zoneId) { + LocalDateTime dateTime = parseLocalDateTime(timestampStr, zoneId); return dateTime != null ? Date.valueOf(dateTime.toLocalDate()) : null; } - public static Timestamp parseTimestamp(String timeStampStr) { - LocalDateTime dateTime = parseLocalDateTime(timeStampStr); + public static Timestamp parseTimestamp(String timeStampStr, ZoneId zoneId) { + LocalDateTime dateTime = parseLocalDateTime(timeStampStr, zoneId); return dateTime != null ? Timestamp.valueOf(dateTime) : null; } - private static LocalDateTime parseLocalDateTime(String timeStampStr) { + private static LocalDateTime parseLocalDateTime(String timeStampStr, ZoneId zoneId) { try { - return parseMilliSecTimestamp(timeStampStr); + return parseMilliSecTimestamp(timeStampStr, zoneId); } catch (DateTimeParseException e) { try { - return parseMicroSecTimestamp(timeStampStr); + return parseMicroSecTimestamp(timeStampStr, zoneId); } catch (DateTimeParseException ee) { - return parseNanoSecTimestamp(timeStampStr); + return parseNanoSecTimestamp(timeStampStr, zoneId); } } } - private static LocalDateTime parseMilliSecTimestamp(String timeStampStr) throws DateTimeParseException { - return LocalDateTime.parse(timeStampStr, milliSecFormatter); + private static LocalDateTime parseMilliSecTimestamp(String timeStampStr, ZoneId zoneId) throws DateTimeParseException { + if (zoneId == null){ + return LocalDateTime.parse(timeStampStr, milliSecFormatter); + } else { + LocalDateTime dateTime = LocalDateTime.parse(timeStampStr, milliSecFormatter); + ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); + return zonedDateTime.toLocalDateTime(); + } } - private static LocalDateTime parseMicroSecTimestamp(String timeStampStr) throws DateTimeParseException { - return LocalDateTime.parse(timeStampStr, microSecFormatter); + private static LocalDateTime parseMicroSecTimestamp(String timeStampStr, ZoneId zoneId) throws DateTimeParseException { + if (zoneId == null) { + return LocalDateTime.parse(timeStampStr, microSecFormatter); + } else { + LocalDateTime dateTime = LocalDateTime.parse(timeStampStr, microSecFormatter); + ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); + return zonedDateTime.toLocalDateTime(); + } } - private static LocalDateTime parseNanoSecTimestamp(String timeStampStr) throws DateTimeParseException { - return LocalDateTime.parse(timeStampStr, nanoSecFormatter); + private static LocalDateTime parseNanoSecTimestamp(String timeStampStr, ZoneId zoneId) throws DateTimeParseException { + if (zoneId == null) { + return LocalDateTime.parse(timeStampStr, nanoSecFormatter); + } else { + LocalDateTime dateTime = LocalDateTime.parse(timeStampStr, nanoSecFormatter); + ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); + return zonedDateTime.toLocalDateTime(); + } } public static String escapeSingleQuota(String origin) { @@ -221,4 +245,12 @@ public static T newInstance(Class c) { } } + public static boolean isValidIP(String ip) { + try { + InetAddress address = InetAddress.getByName(ip); + return true; + } catch (UnknownHostException e) { + return false; + } + } } diff --git a/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java b/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java index 8c5a82bf..62348e49 100644 --- a/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java +++ b/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java @@ -16,6 +16,7 @@ import java.sql.*; import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.format.DateTimeParseException; import java.util.Calendar; @@ -23,10 +24,12 @@ import static com.taosdata.jdbc.utils.UnsignedDataUtils.*; public class BlockResultSet extends AbstractWSResultSet { + private final ZoneId zoneId; public BlockResultSet(Statement statement, Transport transport, - QueryResp response, String database) throws SQLException { + QueryResp response, String database, ZoneId zoneId) throws SQLException { super(statement, transport, response, database); + this.zoneId = zoneId; } @@ -36,8 +39,16 @@ public Object parseValue(int columnIndex) { return null; int type = fields.get(columnIndex - 1).getTaosType(); - return DataTypeConverUtil.parseValue(type, source, this.timestampPrecision); + return DataTypeConverUtil.parseValue(type, source, this.timestampPrecision, zoneId); + } + + public Object parseValueWithZoneId(int columnIndex, ZoneId zoneId) { + Object source = result.get(columnIndex - 1).get(rowIndex); + if (null == source) + return null; + int type = fields.get(columnIndex - 1).getTaosType(); + return DataTypeConverUtil.parseValue(type, source, this.timestampPrecision, zoneId); } @Override @@ -205,7 +216,7 @@ public Date getDate(int columnIndex) throws SQLException { return null; } wasNull = false; - return DataTypeConverUtil.getDate(value); + return DataTypeConverUtil.getDate(value, zoneId); } @Override @@ -218,7 +229,7 @@ public Time getTime(int columnIndex) throws SQLException { return null; } wasNull = false; - return DataTypeConverUtil.getTime(value); + return DataTypeConverUtil.getTime(value, zoneId); } @Override @@ -234,7 +245,7 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { if (value instanceof Timestamp) return (Timestamp) value; if (value instanceof Long) { - return DataTypeConverUtil.parseTimestampColumnData((long) value, this.timestampPrecision); + return DataTypeConverUtil.parseTimestampColumnDataWithZoneId((long) value, this.timestampPrecision, this.zoneId); } String tmp = ""; if (value instanceof byte[]) { @@ -249,7 +260,7 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { } Timestamp ret; try { - ret = Utils.parseTimestamp(tmp); + ret = Utils.parseTimestamp(tmp, zoneId); } catch (Exception e) { ret = null; wasNull = true; @@ -299,7 +310,23 @@ public T getObject(int columnIndex, Class type) throws SQLException { } else if (type == LocalDateTime.class && value instanceof Timestamp) { Timestamp timestamp = (Timestamp) value; return type.cast(timestamp.toLocalDateTime()); - } else { + } + +// else if (type == Instant.class && value instanceof Timestamp) { +// Timestamp timestamp = (Timestamp) value; +// return type.cast(Instant.toLocalDateTime()); +// } +// +// else if (type == LocalDateTime.class && value instanceof Timestamp) { +// Timestamp timestamp = (Timestamp) value; +// return type.cast(timestamp.toLocalDateTime()); +// }else if (type == LocalDateTime.class && value instanceof Timestamp) { +// Timestamp timestamp = (Timestamp) value; +// return type.cast(timestamp.toLocalDateTime()); +// } + + + else { throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_TYPE_CONVERT_EXCEPTION, "Cannot convert " + value.getClass() + " to " + type); } } catch (ClassCastException | UnsupportedEncodingException e) { diff --git a/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java b/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java index 45dd13ef..7d860a7a 100644 --- a/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java +++ b/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java @@ -27,6 +27,7 @@ import java.sql.Date; import java.sql.*; import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; @@ -58,8 +59,8 @@ public class TSWSPreparedStatement extends WSStatement implements PreparedStatem private final PriorityQueue queue = new PriorityQueue<>(); - public TSWSPreparedStatement(Transport transport, ConnectionParam param, String database, AbstractConnection connection, String sql, Long instanceId) throws SQLException { - super(transport, database, connection, instanceId); + public TSWSPreparedStatement(Transport transport, ConnectionParam param, String database, AbstractConnection connection, String sql, Long instanceId, ZoneId zoneId) throws SQLException { + super(transport, database, connection, instanceId, zoneId); this.rawSql = sql; this.param = param; this.insertDbName = database; diff --git a/src/main/java/com/taosdata/jdbc/ws/Transport.java b/src/main/java/com/taosdata/jdbc/ws/Transport.java index 4b8041c8..a2efce29 100644 --- a/src/main/java/com/taosdata/jdbc/ws/Transport.java +++ b/src/main/java/com/taosdata/jdbc/ws/Transport.java @@ -8,6 +8,7 @@ import com.taosdata.jdbc.utils.CompletableFutureTimeout; import com.taosdata.jdbc.utils.ReqId; import com.taosdata.jdbc.utils.StringUtils; +import com.taosdata.jdbc.utils.Utils; import com.taosdata.jdbc.ws.entity.*; import org.java_websocket.exceptions.WebsocketNotConnectedException; import org.slf4j.Logger; @@ -401,15 +402,7 @@ public boolean reconnectCurNode() throws SQLException { boolean reconnected = clientArr.get(currentNodeIndex).reconnectBlocking(); if (reconnected) { // send con msgs - ConnectReq connectReq = new ConnectReq(); - connectReq.setReqId(ReqId.getReqID()); - connectReq.setUser(connectionParam.getUser()); - connectReq.setPassword(connectionParam.getPassword()); - connectReq.setDb(connectionParam.getDatabase()); - - if (connectionParam.getConnectMode() != 0) { - connectReq.setMode(connectionParam.getConnectMode()); - } + ConnectReq connectReq = new ConnectReq(connectionParam); ConnectResp auth; auth = (ConnectResp) sendWithoutRetry(new Request(Action.CONN.getAction(), connectReq)); diff --git a/src/main/java/com/taosdata/jdbc/ws/WSConnection.java b/src/main/java/com/taosdata/jdbc/ws/WSConnection.java index fa5ea213..db08197f 100644 --- a/src/main/java/com/taosdata/jdbc/ws/WSConnection.java +++ b/src/main/java/com/taosdata/jdbc/ws/WSConnection.java @@ -9,6 +9,7 @@ import com.taosdata.jdbc.rs.ConnectionParam; import com.taosdata.jdbc.rs.RestfulDatabaseMetaData; import com.taosdata.jdbc.utils.ReqId; +import com.taosdata.jdbc.utils.Utils; import com.taosdata.jdbc.ws.entity.*; import com.taosdata.jdbc.ws.entity.CommonResp; import com.taosdata.jdbc.ws.schemaless.InsertReq; @@ -47,7 +48,7 @@ public Statement createStatement() throws SQLException { if (this.getClientInfo(TSDBDriver.PROPERTY_KEY_DBNAME) != null) database = this.getClientInfo(TSDBDriver.PROPERTY_KEY_DBNAME); - WSStatement statement = new WSStatement(transport, database, this, idGenerator.getAndIncrement()); + WSStatement statement = new WSStatement(transport, database, this, idGenerator.getAndIncrement(), param.getZoneId()); statementsMap.put(statement.getInstanceId(), statement); return statement; @@ -62,7 +63,13 @@ public PreparedStatement prepareStatement(String sql) throws SQLException { database = this.getClientInfo(TSDBDriver.PROPERTY_KEY_DBNAME); if (transport != null && !transport.isClosed()) { - return new TSWSPreparedStatement(transport, param, database, this, sql, idGenerator.getAndIncrement()); + return new TSWSPreparedStatement(transport, + param, + database, + this, + sql, + idGenerator.getAndIncrement(), + param.getZoneId()); } else { throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_CONNECTION_CLOSED); } @@ -94,16 +101,7 @@ public DatabaseMetaData getMetaData() throws SQLException { public static void reInitTransport(Transport transport, ConnectionParam param, String db) throws SQLException { transport.disconnectAndReconnect(); - ConnectReq connectReq = new ConnectReq(); - connectReq.setReqId(ReqId.getReqID()); - connectReq.setUser(param.getUser()); - connectReq.setPassword(param.getPassword()); - connectReq.setDb(db); - // 目前仅支持bi模式,下游接口值为0,此处做转换 - if(param.getConnectMode() == ConnectionParam.CONNECT_MODE_BI){ - connectReq.setMode(0); - } - + ConnectReq connectReq = new ConnectReq(param); ConnectResp auth = (ConnectResp) transport.send(new Request(Action.CONN.getAction(), connectReq)); if (Code.SUCCESS.getCode() != auth.getCode()) { diff --git a/src/main/java/com/taosdata/jdbc/ws/WSStatement.java b/src/main/java/com/taosdata/jdbc/ws/WSStatement.java index 157187a3..e873b310 100644 --- a/src/main/java/com/taosdata/jdbc/ws/WSStatement.java +++ b/src/main/java/com/taosdata/jdbc/ws/WSStatement.java @@ -10,6 +10,7 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.ZoneId; import static com.taosdata.jdbc.utils.SqlSyntaxValidator.getDatabaseName; @@ -23,12 +24,15 @@ public class WSStatement extends AbstractStatement { private int queryTimeout = 0; - public WSStatement(Transport transport, String database, AbstractConnection connection, Long instanceId) { + private final ZoneId zoneId; + + public WSStatement(Transport transport, String database, AbstractConnection connection, Long instanceId, ZoneId zoneId) { this.transport = transport; this.database = database; this.connection = connection; this.instanceId = instanceId; this.connection.registerStatement(this.instanceId, this); + this.zoneId = zoneId; } @Override @@ -101,7 +105,7 @@ public boolean execute(String sql, Long reqId) throws SQLException { this.affectedRows = queryResp.getAffectedRows(); return false; } else { - this.resultSet = new BlockResultSet(this, this.transport, queryResp, this.database); + this.resultSet = new BlockResultSet(this, this.transport, queryResp, this.database, this.zoneId); this.affectedRows = -1; return true; } diff --git a/src/main/java/com/taosdata/jdbc/ws/WebSocketDriver.java b/src/main/java/com/taosdata/jdbc/ws/WebSocketDriver.java index e406332f..33c3d85f 100644 --- a/src/main/java/com/taosdata/jdbc/ws/WebSocketDriver.java +++ b/src/main/java/com/taosdata/jdbc/ws/WebSocketDriver.java @@ -44,7 +44,7 @@ public Connection connect(String url, Properties info) throws SQLException { return null; Properties props = parseURL(url, info); - ConnectionParam param = ConnectionParam.getParam(props); + ConnectionParam param = ConnectionParam.getParamWs(props); return getWSConnection(url, param, props); } diff --git a/src/main/java/com/taosdata/jdbc/ws/entity/ConnectReq.java b/src/main/java/com/taosdata/jdbc/ws/entity/ConnectReq.java index ed2d11ba..ed405479 100644 --- a/src/main/java/com/taosdata/jdbc/ws/entity/ConnectReq.java +++ b/src/main/java/com/taosdata/jdbc/ws/entity/ConnectReq.java @@ -1,5 +1,8 @@ package com.taosdata.jdbc.ws.entity; +import com.taosdata.jdbc.rs.ConnectionParam; +import com.taosdata.jdbc.utils.ReqId; + /** * connection request pojo */ @@ -11,6 +14,10 @@ public class ConnectReq extends Payload { private String db; private Integer mode; + private String tz; + private String app; + private String ip; + public String getUser() { return user; } @@ -42,4 +49,44 @@ public Integer getMode() { public void setMode(Integer mode) { this.mode = mode; } + + + public String getTz() { + return tz; + } + + public void setTz(String tz) { + this.tz = tz; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public ConnectReq(ConnectionParam param) { + this.setReqId(ReqId.getReqID()); + this.setUser(param.getUser()); + this.setPassword(param.getPassword()); + this.setDb(param.getDatabase()); + this.setTz(param.getTz()); + this.setApp(param.getAppName()); + this.setIp(param.getAppIp()); + + // Currently, only BI mode is supported. The downstream interface value is 0, so a conversion is performed here. + if(param.getConnectMode() == ConnectionParam.CONNECT_MODE_BI){ + this.setMode(0); + } + } } diff --git a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumer.java b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumer.java index 06e0192b..a5c20eb7 100644 --- a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumer.java +++ b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumer.java @@ -23,6 +23,7 @@ import java.nio.ByteOrder; import java.sql.SQLException; import java.time.Duration; +import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; @@ -35,11 +36,13 @@ public class WSConsumer implements Consumer { private long messageId = 0L; private Collection topics; + private ZoneId zoneId = null; @Override public void create(Properties properties) throws SQLException { factory = new TMQRequestFactory(); param = new ConsumerParam(properties); + this.zoneId = param.getConnectionParam().getZoneId(); InFlightRequest inFlightRequest = new InFlightRequest(param.getConnectionParam().getRequestTimeout() , param.getConnectionParam().getMaxRequest()); transport = new Transport(WSFunction.TMQ, param.getConnectionParam(), inFlightRequest); @@ -76,15 +79,9 @@ public void create(Properties properties) throws SQLException { @Override public void subscribe(Collection topics) throws SQLException { - Request request = factory.generateSubscribe(param.getConnectionParam().getUser() - , param.getConnectionParam().getPassword() - , param.getConnectionParam().getDatabase() - , param.getGroupId() - , param.getClientId() - , param.getOffsetRest() + Request request = factory.generateSubscribe(param , topics.toArray(new String[0]) , String.valueOf(false) - , param.getMsgWithTableName() ); SubscribeResp response = (SubscribeResp) transport.send(request); if (Code.SUCCESS.getCode() != response.getCode()) { @@ -152,7 +149,7 @@ private ConsumerRecords doPoll(Duration timeout, Deserializer deserializer ConsumerRecords records = new ConsumerRecords<>(); - try (WSConsumerResultSet rs = new WSConsumerResultSet(transport, factory, pollResp.getMessageId(), pollResp.getDatabase())) { + try (WSConsumerResultSet rs = new WSConsumerResultSet(transport, factory, pollResp.getMessageId(), pollResp.getDatabase(), zoneId)) { while (rs.next()) { String topic = pollResp.getTopic(); String dbName = pollResp.getDatabase(); diff --git a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java index 65c83ed1..ed2f19bb 100644 --- a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java +++ b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java @@ -22,6 +22,7 @@ import java.math.BigDecimal; import java.sql.*; import java.time.Instant; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -46,11 +47,14 @@ public class WSConsumerResultSet extends AbstractResultSet { protected int numOfRows = 0; protected int rowIndex = 0; - public WSConsumerResultSet(Transport transport, TMQRequestFactory factory, long messageId, String database) { + private final ZoneId zoneId; + + public WSConsumerResultSet(Transport transport, TMQRequestFactory factory, long messageId, String database, ZoneId zoneId) { this.transport = transport; this.factory = factory; this.messageId = messageId; this.database = database; + this.zoneId = zoneId; } private boolean forward() { @@ -268,11 +272,11 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { if (value instanceof Timestamp) return (Timestamp) value; if (value instanceof Long) { - return DataTypeConverUtil.parseTimestampColumnData((long) value, this.timestampPrecision); + return DataTypeConverUtil.parseTimestampColumnDataWithZoneId((long) value, this.timestampPrecision, zoneId); } Timestamp ret; try { - ret = Utils.parseTimestamp(value.toString()); + ret = Utils.parseTimestamp(value.toString(), null); } catch (Exception e) { ret = null; wasNull = true; @@ -469,7 +473,7 @@ public Object parseValue(int columnIndex) { return null; int type = fields.get(columnIndex - 1).getTaosType(); - return DataTypeConverUtil.parseValue(type, source, this.timestampPrecision); + return DataTypeConverUtil.parseValue(type, source, this.timestampPrecision, zoneId); } // ceil(numOfRows/8.0) diff --git a/src/main/java/com/taosdata/jdbc/ws/tmq/entity/SubscribeReq.java b/src/main/java/com/taosdata/jdbc/ws/tmq/entity/SubscribeReq.java index f9136c37..37809406 100644 --- a/src/main/java/com/taosdata/jdbc/ws/tmq/entity/SubscribeReq.java +++ b/src/main/java/com/taosdata/jdbc/ws/tmq/entity/SubscribeReq.java @@ -22,6 +22,10 @@ public class SubscribeReq extends Payload { @JsonProperty("with_table_name") private String withTableName; + private String tz; + private String app; + private String ip; + public String getUser() { return user; } @@ -101,4 +105,29 @@ public String getWithTableName() { public void setWithTableName(String withTableName) { this.withTableName = withTableName; } + + public String getTz() { + return tz; + } + + public void setTz(String tz) { + this.tz = tz; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + } diff --git a/src/main/java/com/taosdata/jdbc/ws/tmq/entity/TMQRequestFactory.java b/src/main/java/com/taosdata/jdbc/ws/tmq/entity/TMQRequestFactory.java index eda2f823..859e2b84 100644 --- a/src/main/java/com/taosdata/jdbc/ws/tmq/entity/TMQRequestFactory.java +++ b/src/main/java/com/taosdata/jdbc/ws/tmq/entity/TMQRequestFactory.java @@ -1,5 +1,6 @@ package com.taosdata.jdbc.ws.tmq.entity; +import com.taosdata.jdbc.rs.ConnectionParam; import com.taosdata.jdbc.tmq.TopicPartition; import com.taosdata.jdbc.ws.entity.Request; import com.taosdata.jdbc.ws.tmq.ConsumerAction; @@ -24,22 +25,26 @@ public TMQRequestFactory() { } } - public Request generateSubscribe(String user, String password, String db, String groupId, - String clientId, String offsetRest, String[] topics - , String enableAutoCommit, String withTableName) { + public Request generateSubscribe(ConsumerParam param, String[] topics + , String enableAutoCommit) { long reqId = this.getId(ConsumerAction.SUBSCRIBE.getAction()); SubscribeReq subscribeReq = new SubscribeReq(); + subscribeReq.setReqId(reqId); - subscribeReq.setUser(user); - subscribeReq.setPassword(password); - subscribeReq.setDb(db); - subscribeReq.setGroupId(groupId); - subscribeReq.setClientId(clientId); - subscribeReq.setOffsetRest(offsetRest); + subscribeReq.setUser(param.getConnectionParam().getUser()); + subscribeReq.setPassword(param.getConnectionParam().getPassword()); + subscribeReq.setDb(param.getConnectionParam().getDatabase()); + subscribeReq.setGroupId(param.getGroupId()); + subscribeReq.setClientId(param.getClientId()); + subscribeReq.setOffsetRest(param.getOffsetRest()); subscribeReq.setTopics(topics); subscribeReq.setAutoCommit(enableAutoCommit); - subscribeReq.setWithTableName(withTableName); + subscribeReq.setWithTableName(param.getMsgWithTableName()); + subscribeReq.setTz(param.getConnectionParam().getTz()); + subscribeReq.setApp(param.getConnectionParam().getAppName()); + subscribeReq.setIp(param.getConnectionParam().getAppIp()); + return new Request(ConsumerAction.SUBSCRIBE.getAction(), subscribeReq); } diff --git a/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java b/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java index 3bf42417..c79b3c08 100644 --- a/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java +++ b/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java @@ -483,21 +483,21 @@ public void testGetDate() { // Test with Timestamp Timestamp timestamp = new Timestamp(System.currentTimeMillis()); Date expectedDate = new Date(timestamp.getTime()); - assertEquals(expectedDate, DataTypeConverUtil.getDate(timestamp)); + assertEquals(expectedDate, DataTypeConverUtil.getDate(timestamp, null)); // Test with byte array representing a date string String dateString = "2023-10-10 10:10:10.123"; try { byte[] dateBytes = dateString.getBytes("UTF-8"); - Date parsedDate = DataTypeConverUtil.getDate(dateBytes); - assertEquals(Utils.parseDate(dateString), parsedDate); + Date parsedDate = DataTypeConverUtil.getDate(dateBytes, null); + assertEquals(Utils.parseDate(dateString, null), parsedDate); } catch (UnsupportedEncodingException e) { fail("Unexpected UnsupportedEncodingException: " + e.getMessage()); } // Test with String - Date parsedDateFromString = DataTypeConverUtil.getDate(dateString); - assertEquals(Utils.parseDate(dateString), parsedDateFromString); + Date parsedDateFromString = DataTypeConverUtil.getDate(dateString, null); + assertEquals(Utils.parseDate(dateString, null), parsedDateFromString); } @@ -507,29 +507,29 @@ public void testGetTime() { // Test with Timestamp Timestamp timestamp = new Timestamp(Instant.now().toEpochMilli()); Time expectedTime = new Time(timestamp.getTime()); - assertEquals(expectedTime, DataTypeConverUtil.getTime(timestamp)); + assertEquals(expectedTime, DataTypeConverUtil.getTime(timestamp, null)); // Test with byte array representing a valid time string String timeString = "12:34:56"; byte[] timeBytes = timeString.getBytes(); Time parsedTime = Time.valueOf(timeString); - assertEquals(parsedTime, DataTypeConverUtil.getTime(timeBytes)); + assertEquals(parsedTime, DataTypeConverUtil.getTime(timeBytes, null)); // Test with invalid byte array (should throw RuntimeException) byte[] invalidBytes = "invalid".getBytes(); try { - DataTypeConverUtil.getTime(invalidBytes); + DataTypeConverUtil.getTime(invalidBytes, null); fail("Expected RuntimeException for invalid byte array"); } catch (RuntimeException e) { // Expected exception } // Test with String - assertEquals(parsedTime, DataTypeConverUtil.getTime(timeString)); + assertEquals(parsedTime, DataTypeConverUtil.getTime(timeString, null)); // Test with invalid String (should throw RuntimeException) try { - DataTypeConverUtil.getTime("invalid"); + DataTypeConverUtil.getTime("invalid", null); fail("Expected RuntimeException for invalid string"); } catch (RuntimeException e) { // Expected exception @@ -581,44 +581,44 @@ public void testGetBigDecimal() { @Test public void testParseValue() { // BOOL - assertEquals(Boolean.TRUE, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BOOL, (byte) 1, TimestampPrecision.MS)); - assertEquals(Boolean.FALSE, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BOOL, (byte) 0, TimestampPrecision.MS)); + assertEquals(Boolean.TRUE, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BOOL, (byte) 1, TimestampPrecision.MS, null)); + assertEquals(Boolean.FALSE, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BOOL, (byte) 0, TimestampPrecision.MS, null)); // UTINYINT - assertEquals((short)255, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UTINYINT, (byte) -1, TimestampPrecision.MS)); + assertEquals((short)255, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UTINYINT, (byte) -1, TimestampPrecision.MS, null)); // TINYINT, SMALLINT, INT, BIGINT, FLOAT, DOUBLE, BINARY, JSON, VARBINARY, GEOMETRY - assertEquals((byte) 127, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_TINYINT, (byte) 127, TimestampPrecision.MS)); - assertEquals((short) 32767, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_SMALLINT, (short) 32767, TimestampPrecision.MS)); - assertEquals(2147483647, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_INT, 2147483647, TimestampPrecision.MS)); - assertEquals(9223372036854775807L, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BIGINT, 9223372036854775807L, TimestampPrecision.MS)); - assertEquals(3.14f, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_FLOAT, 3.14f, TimestampPrecision.MS)); - assertEquals(3.141592653589793, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_DOUBLE, 3.141592653589793, TimestampPrecision.MS)); - assertEquals("binaryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BINARY, "binaryData", TimestampPrecision.MS)); - assertEquals("jsonData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_JSON, "jsonData", TimestampPrecision.MS)); - assertEquals("varbinaryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_VARBINARY, "varbinaryData", TimestampPrecision.MS)); - assertEquals("geometryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_GEOMETRY, "geometryData", TimestampPrecision.MS)); + assertEquals((byte) 127, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_TINYINT, (byte) 127, TimestampPrecision.MS, null)); + assertEquals((short) 32767, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_SMALLINT, (short) 32767, TimestampPrecision.MS, null)); + assertEquals(2147483647, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_INT, 2147483647, TimestampPrecision.MS, null)); + assertEquals(9223372036854775807L, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BIGINT, 9223372036854775807L, TimestampPrecision.MS, null)); + assertEquals(3.14f, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_FLOAT, 3.14f, TimestampPrecision.MS, null)); + assertEquals(3.141592653589793, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_DOUBLE, 3.141592653589793, TimestampPrecision.MS, null)); + assertEquals("binaryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BINARY, "binaryData", TimestampPrecision.MS, null)); + assertEquals("jsonData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_JSON, "jsonData", TimestampPrecision.MS, null)); + assertEquals("varbinaryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_VARBINARY, "varbinaryData", TimestampPrecision.MS, null)); + assertEquals("geometryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_GEOMETRY, "geometryData", TimestampPrecision.MS, null)); // USMALLINT - assertEquals(65535, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_USMALLINT, (short) -1, TimestampPrecision.MS)); + assertEquals(65535, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_USMALLINT, (short) -1, TimestampPrecision.MS, null)); // UINT - assertEquals(4294967295L, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UINT, -1, TimestampPrecision.MS)); + assertEquals(4294967295L, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UINT, -1, TimestampPrecision.MS, null)); // TIMESTAMP long currentTimeMillis = System.currentTimeMillis(); Timestamp expectedTimestamp = new Timestamp(currentTimeMillis); - assertEquals(expectedTimestamp, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_TIMESTAMP, currentTimeMillis, TimestampPrecision.MS)); + assertEquals(expectedTimestamp, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_TIMESTAMP, currentTimeMillis, TimestampPrecision.MS, null)); // UBIGINT - assertEquals(new BigDecimal("18446744073709551615"), DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UBIGINT, -1L, TimestampPrecision.MS)); + assertEquals(new BigDecimal("18446744073709551615"), DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UBIGINT, -1L, TimestampPrecision.MS, null)); // NCHAR int[] ncharData = {65, 66, 67}; // Corresponds to "ABC" - assertEquals("ABC", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_NCHAR, ncharData, TimestampPrecision.MS)); + assertEquals("ABC", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_NCHAR, ncharData, TimestampPrecision.MS, null)); // Default case - assertNull(DataTypeConverUtil.parseValue(-1, "unknownType", TimestampPrecision.MS)); + assertNull(DataTypeConverUtil.parseValue(-1, "unknownType", TimestampPrecision.MS, null)); } @Test diff --git a/src/test/java/com/taosdata/jdbc/ws/WSAppInfoTest.java b/src/test/java/com/taosdata/jdbc/ws/WSAppInfoTest.java new file mode 100644 index 00000000..790556c5 --- /dev/null +++ b/src/test/java/com/taosdata/jdbc/ws/WSAppInfoTest.java @@ -0,0 +1,72 @@ +package com.taosdata.jdbc.ws; + +import com.taosdata.jdbc.TSDBDriver; +import com.taosdata.jdbc.annotation.CatalogRunner; +import com.taosdata.jdbc.annotation.TestTarget; +import com.taosdata.jdbc.utils.SpecifyAddress; +import org.junit.*; +import org.junit.runner.RunWith; + +import java.sql.*; +import java.util.Properties; + +@RunWith(CatalogRunner.class) +@TestTarget(alias = "websocket query test", author = "huolibo", version = "2.0.38") +@FixMethodOrder +public class WSAppInfoTest { + private static final String host = "127.0.0.1"; + private static final int port = 6041; + private Connection connection; + + + private static final String appName = "jdbc_appName"; + private static final String appIp = "192.168.1.1"; + + @Test + public void AppInfoTest() throws SQLException, InterruptedException { + + for (int i = 0; i < 10; i++) { + Thread.sleep(1000); + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("show connections")) { + while (resultSet.next()) { + + String name = resultSet.getString("user_app"); + String ip = resultSet.getString("user_ip"); + + System.out.println("name: " + name + " ip: " + ip); + if (appName.equals(name) + && appIp.equals(ip)) { + return; + } + } + } catch (SQLException e) { + e.printStackTrace(); + throw e; + } + } + Assert.fail("App info not found in show connections"); + } + + @Before + public void before() throws SQLException { + String url = SpecifyAddress.getInstance().getRestWithoutUrl(); + if (url == null) { + url = "jdbc:TAOS-WS://" + host + ":" + port + "/?user=root&password=taosdata"; + } else { + url += "?user=root&password=taosdata"; + } + Properties properties = new Properties(); + properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "Asia/Shanghai"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_APP_NAME, appName); + properties.setProperty(TSDBDriver.PROPERTY_KEY_APP_IP, appIp); + connection = DriverManager.getConnection(url, properties); + } + + @After + public void after() throws SQLException { + if (null != connection) { + connection.close(); + } + } +} diff --git a/src/test/java/com/taosdata/jdbc/ws/WSConsumerResultSetTest.java b/src/test/java/com/taosdata/jdbc/ws/WSConsumerResultSetTest.java index 1435360c..6b246edd 100644 --- a/src/test/java/com/taosdata/jdbc/ws/WSConsumerResultSetTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/WSConsumerResultSetTest.java @@ -16,7 +16,7 @@ public class WSConsumerResultSetTest { @Before public void setUp() { - wsConsumerResultSet = new WSConsumerResultSet(null, null, 0, null); + wsConsumerResultSet = new WSConsumerResultSet(null, null, 0, null, null); } @Test diff --git a/src/test/java/com/taosdata/jdbc/ws/WSConsumerTest.java b/src/test/java/com/taosdata/jdbc/ws/WSConsumerTest.java index 93f2e836..73a51547 100644 --- a/src/test/java/com/taosdata/jdbc/ws/WSConsumerTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/WSConsumerTest.java @@ -108,6 +108,10 @@ public void testWSBeanObject() throws Exception { properties.setProperty(TMQConstants.VALUE_DESERIALIZER, "com.taosdata.jdbc.tmq.ResultDeserializer"); properties.setProperty(TMQConstants.CONNECT_TYPE, "ws"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_APP_IP, "192.168.1.1"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_APP_NAME, "APP_NAME"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "Asia/Shanghai"); + try (TaosConsumer consumer = new TaosConsumer<>(properties)) { consumer.subscribe(Collections.singletonList(topic)); for (int i = 0; i < 10; i++) { diff --git a/src/test/java/com/taosdata/jdbc/ws/WSDatabaseMetaDataTest.java b/src/test/java/com/taosdata/jdbc/ws/WSDatabaseMetaDataTest.java index f82b0ac0..d949a717 100644 --- a/src/test/java/com/taosdata/jdbc/ws/WSDatabaseMetaDataTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/WSDatabaseMetaDataTest.java @@ -63,7 +63,7 @@ public static void beforeClass() throws SQLException { Properties properties = new Properties(); properties.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); properties.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8"); - properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC+8"); url = SpecifyAddress.getInstance().getRestUrl(); if (url == null) { url = "jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata&batchfetch=true&conmode=1"; diff --git a/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java b/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java new file mode 100644 index 00000000..c729c95e --- /dev/null +++ b/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java @@ -0,0 +1,125 @@ +package com.taosdata.jdbc.ws; + +import com.taosdata.jdbc.TSDBDriver; +import com.taosdata.jdbc.annotation.CatalogRunner; +import com.taosdata.jdbc.annotation.Description; +import com.taosdata.jdbc.annotation.TestTarget; +import com.taosdata.jdbc.utils.SpecifyAddress; +import org.junit.*; +import org.junit.runner.RunWith; + +import java.sql.*; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Properties; +import java.util.TimeZone; +import java.util.concurrent.CountDownLatch; +import java.util.stream.IntStream; + +@RunWith(CatalogRunner.class) +@TestTarget(alias = "websocket query test", author = "huolibo", version = "2.0.38") +@FixMethodOrder +public class WSTimeZoneTest { + private static final String host = "127.0.0.1"; + private static final int port = 6041; + private static final String db_name = "ws_query"; + private static final String tableName = "wq"; + private Connection connection; + + @Test + public void TimeZoneTest() throws SQLException, InterruptedException { + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("select * from " + db_name + "." + tableName + " limit 1")) { + while (resultSet.next()) { + + Timestamp ts = resultSet.getTimestamp("ts"); + + System.out.println("ts: " + ts); + System.out.println("ts: " + ts.getTime()); + Assert.assertEquals("2024-01-01 00:00:00.0", ts.toString()); + } + } catch (SQLException e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void TimeZoneTest2() throws SQLException, InterruptedException { + Instant instant = Instant.ofEpochMilli(1704038400000L); + ZonedDateTime zonedDateTime1 = instant.atZone(ZoneId.of("Asia/Tokyo")); + System.out.println("zonedDateTime1: " + zonedDateTime1); + + Instant instant2 = Instant.ofEpochMilli(1704038400000L); + ZonedDateTime zonedDateTime2 = instant2.atZone(ZoneId.of("Asia/Shanghai")); + System.out.println("zonedDateTime2: " + zonedDateTime2); + + } + + @Test + public void TimeZoneTest3() throws SQLException, InterruptedException { + long value = 1704038400000L; // 示例时间戳 + ZoneId zoneId = ZoneId.of("Asia/Tokyo"); + + // 获取当前 UTC 时间 + Instant instant1 = Instant.ofEpochMilli(value); + System.out.println("Original Instant (UTC): " + instant1); + + // 将 Instant 转换为特定时区的 ZonedDateTime + ZonedDateTime zonedDateTime = instant1.atZone(zoneId); + System.out.println("ZonedDateTime in Asia/Shanghai: " + zonedDateTime); + + // 将 ZonedDateTime 转换回 Timestamp + Timestamp timestamp = Timestamp.from(zonedDateTime.toInstant()); + System.out.println("Timestamp in Asia/Shanghai: " + timestamp); + } + + @Test(expected = Exception.class) + public void InvalidTimeZoneTest() throws SQLException, InterruptedException { + String url = SpecifyAddress.getInstance().getRestWithoutUrl(); + if (url == null) { + url = "jdbc:TAOS-WS://" + host + ":" + port + "/?user=root&password=taosdata"; + } else { + url += "?user=root&password=taosdata"; + } + Properties properties = new Properties(); + properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "invalid/Tokyo"); + DriverManager.getConnection(url, properties); + } + + @Before + public void before() throws SQLException { + String url = SpecifyAddress.getInstance().getRestWithoutUrl(); + if (url == null) { + url = "jdbc:TAOS-WS://" + host + ":" + port + "/?user=root&password=taosdata"; + } else { + url += "?user=root&password=taosdata"; + } + Properties properties = new Properties(); + properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "Asia/Tokyo"); + connection = DriverManager.getConnection(url, properties); + Statement statement = connection.createStatement(); + statement.execute("drop database if exists " + db_name); + statement.execute("create database " + db_name); + statement.execute("use " + db_name); + statement.execute("create table if not exists " + db_name + "." + tableName + "(ts timestamp, f int)"); + + // Asia/Shanghai +08:00, 2024-01-01 00:00:00 + statement.execute("insert into " + db_name + "." + tableName + " values (\"2024-01-01T00:00:00.000+08:00\", 1)"); + + statement.close(); + } + + @After + public void after() throws SQLException { + if (null != connection) { + try (Statement statement = connection.createStatement()) { + statement.executeUpdate("drop database if exists " + db_name); + } catch (SQLException e) { + // do nothing + } + connection.close(); + } + } +} diff --git a/src/test/java/com/taosdata/jdbc/ws/tmq/entity/TMQRequestFactoryTest.java b/src/test/java/com/taosdata/jdbc/ws/tmq/entity/TMQRequestFactoryTest.java index 4fa903b3..e99966c6 100644 --- a/src/test/java/com/taosdata/jdbc/ws/tmq/entity/TMQRequestFactoryTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/tmq/entity/TMQRequestFactoryTest.java @@ -3,9 +3,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.taosdata.jdbc.TSDBDriver; import com.taosdata.jdbc.annotation.CatalogRunner; import com.taosdata.jdbc.annotation.Description; import com.taosdata.jdbc.annotation.TestTarget; +import com.taosdata.jdbc.tmq.TMQConstants; import com.taosdata.jdbc.utils.JsonUtil; import com.taosdata.jdbc.ws.entity.Request; import org.junit.Assert; @@ -13,6 +15,9 @@ import org.junit.Test; import org.junit.runner.RunWith; +import java.sql.SQLException; +import java.util.Properties; + @TestTarget(alias = "consumer request content", author = "huolibo", version = "3.1.0") @RunWith(CatalogRunner.class) public class TMQRequestFactoryTest { @@ -21,11 +26,29 @@ public class TMQRequestFactoryTest { @Test @Description("Generate Subscribe") - public void testGenerateSubscribe() throws JsonProcessingException { + public void testGenerateSubscribe() throws JsonProcessingException, SQLException { + Properties properties = new Properties(); + properties.setProperty(TMQConstants.CONNECT_USER, "root"); + properties.setProperty(TMQConstants.CONNECT_PASS, "taosdata"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_DBNAME, "test"); + + properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "Asia/Shanghai"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_APP_NAME, "app_name"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_APP_IP, "192.168.1.1"); + + properties.setProperty(TMQConstants.MSG_WITH_TABLE_NAME, "true"); + properties.setProperty(TMQConstants.ENABLE_AUTO_COMMIT, "test"); + + properties.setProperty(TMQConstants.GROUP_ID, "gId"); + properties.setProperty(TMQConstants.CLIENT_ID, "cId"); + properties.setProperty(TMQConstants.AUTO_OFFSET_RESET, "offset"); + + + ConsumerParam param = new ConsumerParam(properties); + String[] topics = {"topic_1", "topic_2"}; - Request request = factory.generateSubscribe("root", "taosdata", "test", "gId", - "cId", "offset", topics - , null, null); + Request request = factory.generateSubscribe(param, topics + , null); JsonNode jsonObject = objectMapper.readTree(request.toString()); SubscribeReq req = objectMapper.treeToValue(jsonObject.get("args"), SubscribeReq.class); Assert.assertEquals(1, req.getReqId()); @@ -36,6 +59,10 @@ public void testGenerateSubscribe() throws JsonProcessingException { Assert.assertEquals("cId", req.getClientId()); Assert.assertEquals("offset", req.getOffsetRest()); Assert.assertEquals("topic_2", req.getTopics()[1]); + + Assert.assertEquals("Asia/Shanghai", req.getTz()); + Assert.assertEquals("app_name", req.getApp()); + Assert.assertEquals("192.168.1.1", req.getIp()); } @Test From e4373a6d9f6a670ff4f828688223acceb0ad9e4a Mon Sep 17 00:00:00 2001 From: sheyanjie-qq <249478495@qq.com> Date: Tue, 24 Dec 2024 19:28:06 +0800 Subject: [PATCH 2/4] add timezone test case --- .../com/taosdata/jdbc/ColumnMetaData.java | 14 --- .../jdbc/DatabaseMetaDataResultSet.java | 14 --- .../com/taosdata/jdbc/EmptyResultSet.java | 14 --- .../java/com/taosdata/jdbc/TSDBConstants.java | 14 --- .../taosdata/jdbc/TSDBDatabaseMetaData.java | 14 --- .../taosdata/jdbc/TSDBPreparedStatement.java | 14 --- .../java/com/taosdata/jdbc/TSDBResultSet.java | 18 --- .../taosdata/jdbc/TSDBResultSetBlockData.java | 17 +-- .../taosdata/jdbc/TSDBResultSetMetaData.java | 16 --- .../taosdata/jdbc/TSDBResultSetRowData.java | 20 --- .../java/com/taosdata/jdbc/TSDBStatement.java | 14 --- .../com/taosdata/jdbc/TaosGlobalConfig.java | 14 --- .../taosdata/jdbc/common/SerializeBlock.java | 20 ++- .../taosdata/jdbc/rs/RestfulResultSet.java | 7 +- .../jdbc/utils/DataTypeConverUtil.java | 19 +-- .../taosdata/jdbc/utils/DateTimeUtils.java | 115 ++++++++++++++++++ .../jdbc/utils/SqlSyntaxValidator.java | 14 --- .../java/com/taosdata/jdbc/utils/Utils.java | 61 ---------- .../com/taosdata/jdbc/ws/BlockResultSet.java | 3 +- .../jdbc/ws/TSWSPreparedStatement.java | 42 +++++-- .../com/taosdata/jdbc/ws/WSStatement.java | 3 +- .../jdbc/ws/tmq/WSConsumerResultSet.java | 3 +- .../jdbc/utils/DataTypeConverUtilTest.java | 4 +- .../com/taosdata/jdbc/ws/WSTimeZoneTest.java | 72 ++++++++--- 24 files changed, 238 insertions(+), 308 deletions(-) create mode 100644 src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java diff --git a/src/main/java/com/taosdata/jdbc/ColumnMetaData.java b/src/main/java/com/taosdata/jdbc/ColumnMetaData.java index 8398c8f8..845a1ed4 100644 --- a/src/main/java/com/taosdata/jdbc/ColumnMetaData.java +++ b/src/main/java/com/taosdata/jdbc/ColumnMetaData.java @@ -1,17 +1,3 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; public class ColumnMetaData { diff --git a/src/main/java/com/taosdata/jdbc/DatabaseMetaDataResultSet.java b/src/main/java/com/taosdata/jdbc/DatabaseMetaDataResultSet.java index 8a494f3a..b2d38bd4 100644 --- a/src/main/java/com/taosdata/jdbc/DatabaseMetaDataResultSet.java +++ b/src/main/java/com/taosdata/jdbc/DatabaseMetaDataResultSet.java @@ -1,17 +1,3 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; import java.math.BigDecimal; diff --git a/src/main/java/com/taosdata/jdbc/EmptyResultSet.java b/src/main/java/com/taosdata/jdbc/EmptyResultSet.java index fa8bf9e7..db70e653 100644 --- a/src/main/java/com/taosdata/jdbc/EmptyResultSet.java +++ b/src/main/java/com/taosdata/jdbc/EmptyResultSet.java @@ -1,17 +1,3 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; import java.io.InputStream; diff --git a/src/main/java/com/taosdata/jdbc/TSDBConstants.java b/src/main/java/com/taosdata/jdbc/TSDBConstants.java index f87ddd3b..3d91e152 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBConstants.java +++ b/src/main/java/com/taosdata/jdbc/TSDBConstants.java @@ -1,17 +1,3 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; import java.sql.SQLException; diff --git a/src/main/java/com/taosdata/jdbc/TSDBDatabaseMetaData.java b/src/main/java/com/taosdata/jdbc/TSDBDatabaseMetaData.java index 9a5eda4c..f8e8af2d 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBDatabaseMetaData.java +++ b/src/main/java/com/taosdata/jdbc/TSDBDatabaseMetaData.java @@ -1,17 +1,3 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; import java.sql.Connection; diff --git a/src/main/java/com/taosdata/jdbc/TSDBPreparedStatement.java b/src/main/java/com/taosdata/jdbc/TSDBPreparedStatement.java index ef7fdeaf..97b9aa5b 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBPreparedStatement.java +++ b/src/main/java/com/taosdata/jdbc/TSDBPreparedStatement.java @@ -1,17 +1,3 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; import com.taosdata.jdbc.utils.Utils; diff --git a/src/main/java/com/taosdata/jdbc/TSDBResultSet.java b/src/main/java/com/taosdata/jdbc/TSDBResultSet.java index 02dee831..ec0e668c 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBResultSet.java +++ b/src/main/java/com/taosdata/jdbc/TSDBResultSet.java @@ -1,23 +1,5 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; -import com.google.common.primitives.Shorts; - import java.math.BigDecimal; import java.sql.ResultSetMetaData; import java.sql.SQLException; diff --git a/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java b/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java index 6b7def24..dc23253d 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java +++ b/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java @@ -1,20 +1,7 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; import com.taosdata.jdbc.utils.DataTypeConverUtil; +import com.taosdata.jdbc.utils.DateTimeUtils; import com.taosdata.jdbc.utils.Utils; import java.io.UnsupportedEncodingException; @@ -380,7 +367,7 @@ public Timestamp getTimestamp(int col) throws SQLException { String charset = TaosGlobalConfig.getCharset(); try { tmp = new String((byte[]) obj, charset); - return Utils.parseTimestamp(tmp, null); + return DateTimeUtils.parseTimestamp(tmp, null); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage()); } diff --git a/src/main/java/com/taosdata/jdbc/TSDBResultSetMetaData.java b/src/main/java/com/taosdata/jdbc/TSDBResultSetMetaData.java index 34ce7332..1e665904 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBResultSetMetaData.java +++ b/src/main/java/com/taosdata/jdbc/TSDBResultSetMetaData.java @@ -1,17 +1,3 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; import com.taosdata.jdbc.enums.DataType; @@ -19,8 +5,6 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.Timestamp; -import java.sql.Types; import java.util.List; public class TSDBResultSetMetaData extends WrapperImpl implements ResultSetMetaData { diff --git a/src/main/java/com/taosdata/jdbc/TSDBResultSetRowData.java b/src/main/java/com/taosdata/jdbc/TSDBResultSetRowData.java index e17950b2..5fc1e1d0 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBResultSetRowData.java +++ b/src/main/java/com/taosdata/jdbc/TSDBResultSetRowData.java @@ -1,35 +1,15 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; import com.taosdata.jdbc.enums.TimestampPrecision; import com.taosdata.jdbc.utils.DataTypeConverUtil; -import com.taosdata.jdbc.utils.UnsignedDataUtils; import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; import java.sql.SQLException; import java.sql.Timestamp; -import java.sql.Types; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; -import static com.taosdata.jdbc.TSDBConstants.*; -import static com.taosdata.jdbc.utils.UnsignedDataUtils.*; - public class TSDBResultSetRowData { private ArrayList data; diff --git a/src/main/java/com/taosdata/jdbc/TSDBStatement.java b/src/main/java/com/taosdata/jdbc/TSDBStatement.java index 7a852c5e..26bf827f 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBStatement.java +++ b/src/main/java/com/taosdata/jdbc/TSDBStatement.java @@ -1,17 +1,3 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; import java.sql.Connection; diff --git a/src/main/java/com/taosdata/jdbc/TaosGlobalConfig.java b/src/main/java/com/taosdata/jdbc/TaosGlobalConfig.java index 17118976..05f12076 100644 --- a/src/main/java/com/taosdata/jdbc/TaosGlobalConfig.java +++ b/src/main/java/com/taosdata/jdbc/TaosGlobalConfig.java @@ -1,17 +1,3 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc; public class TaosGlobalConfig { diff --git a/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java b/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java index 8d64fc57..82fc71f1 100644 --- a/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java +++ b/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java @@ -121,18 +121,6 @@ private static void handleNormalDataType(int dataType ,byte[] buf, int rowIndex, if (o instanceof Timestamp) { Timestamp t = (Timestamp) o; v = getLongFromTimestamp(t, precision); - } else if (o instanceof Instant){ - Instant t = (Instant) o; - Timestamp ts = Timestamp.from(t); - v = getLongFromTimestamp(ts, precision); - } else if (o instanceof OffsetDateTime){ - OffsetDateTime t = (OffsetDateTime) o; - Timestamp ts = Timestamp.from(t.toInstant()); - v = getLongFromTimestamp(ts, precision); - } else if (o instanceof ZonedDateTime){ - ZonedDateTime t = (ZonedDateTime) o; - Timestamp ts = Timestamp.from(t.toInstant()); - v = getLongFromTimestamp(ts, precision); } else { throw new SQLException("unsupported data type : " + o.getClass().getName()); } @@ -298,6 +286,14 @@ public static byte[] getRawBlock(List list, int precision) throws IO for (int i = 0; i < Integer.BYTES; i++) { block[4 + i] = (byte) (block.length >> (8 * i)); } + + for (int i = 0; i < block.length; i++) { + int bb = block[i] & 0xff; + System.out.print(bb); + System.out.print(","); + } + System.out.println(); + return block; } diff --git a/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java b/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java index f1ac0a71..69603616 100644 --- a/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java +++ b/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java @@ -12,6 +12,7 @@ import com.taosdata.jdbc.TSDBErrorNumbers; import com.taosdata.jdbc.enums.DataType; import com.taosdata.jdbc.enums.TimestampPrecision; +import com.taosdata.jdbc.utils.DateTimeUtils; import com.taosdata.jdbc.utils.JsonUtil; import com.taosdata.jdbc.utils.Utils; @@ -413,7 +414,7 @@ public Date getDate(int columnIndex) throws SQLException { return null; if (value instanceof Timestamp) return new Date(((Timestamp) value).getTime()); - return Utils.parseDate(value.toString(), null); + return DateTimeUtils.parseDate(value.toString(), null); } @Override @@ -428,7 +429,7 @@ public Time getTime(int columnIndex) throws SQLException { return new Time(((Timestamp) value).getTime()); Time time = null; try { - time = Utils.parseTime(value.toString(), null); + time = DateTimeUtils.parseTime(value.toString(), null); } catch (DateTimeParseException ignored) { } return time; @@ -453,7 +454,7 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { } Timestamp ret; try { - ret = Utils.parseTimestamp(value.toString(), null); + ret = DateTimeUtils.parseTimestamp(value.toString(), null); } catch (Exception e) { ret = null; wasNull = true; diff --git a/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java b/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java index a5a18578..cc3649fb 100644 --- a/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java +++ b/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java @@ -12,6 +12,7 @@ import java.math.BigDecimal; import java.sql.*; import java.time.Instant; +import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeParseException; @@ -487,9 +488,9 @@ public static Date getDate(Object value, ZoneId zoneId) { } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage()); } - return Utils.parseDate(tmp, zoneId); + return DateTimeUtils.parseDate(tmp, zoneId); } - return Utils.parseDate(value.toString(), zoneId); + return DateTimeUtils.parseDate(value.toString(), zoneId); } public static Time getTime(Object value, ZoneId zoneId) { @@ -508,7 +509,7 @@ public static Time getTime(Object value, ZoneId zoneId) { } Time time = null; try { - time = Utils.parseTime(tmp, zoneId); + time = DateTimeUtils.parseTime(tmp, zoneId); } catch (DateTimeParseException e) { throw new RuntimeException(e.getMessage()); } @@ -627,22 +628,24 @@ public static Timestamp parseTimestampColumnDataWithZoneId(long value, int times } if (TimestampPrecision.MS == timestampPrecision) { - Instant instant1 = Instant.ofEpochMilli(value); - Instant instant = instant1.atZone(zoneId).toInstant(); - return Timestamp.from(instant); + Instant instant = Instant.ofEpochMilli(value); + LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); + return Timestamp.valueOf(localDateTime); } if (TimestampPrecision.US == timestampPrecision) { long epochSec = value / 1000_000L; long nanoAdjustment = value % 1000_000L * 1000L; Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment); - return Timestamp.from(instant.atZone(zoneId).toInstant()); + LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); + return Timestamp.valueOf(localDateTime); } if (TimestampPrecision.NS == timestampPrecision) { long epochSec = value / 1000_000_000L; long nanoAdjustment = value % 1000_000_000L; Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment); - return Timestamp.from(instant.atZone(zoneId).toInstant()); + LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); + return Timestamp.valueOf(localDateTime); } return null; } diff --git a/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java b/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java new file mode 100644 index 00000000..ad43004d --- /dev/null +++ b/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java @@ -0,0 +1,115 @@ +package com.taosdata.jdbc.utils; + +import com.google.common.collect.Range; +import com.google.common.collect.RangeSet; +import com.google.common.collect.TreeRangeSet; + +import java.lang.reflect.Constructor; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class DateTimeUtils { + + private static final ZoneId stdZoneId = ZoneId.of("UTC"); + private static final Pattern ptn = Pattern.compile(".*?'"); + private static final DateTimeFormatter milliSecFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSS").toFormatter(); + private static final DateTimeFormatter microSecFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSSSSS").toFormatter(); + private static final DateTimeFormatter nanoSecFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS").toFormatter(); + + public static Time parseTime(String timestampStr, ZoneId zoneId) throws DateTimeParseException { + LocalDateTime dateTime = parseLocalDateTime(timestampStr, zoneId); + return dateTime != null ? Time.valueOf(dateTime.toLocalTime()) : null; + } + + public static Date parseDate(String timestampStr, ZoneId zoneId) { + LocalDateTime dateTime = parseLocalDateTime(timestampStr, zoneId); + return dateTime != null ? Date.valueOf(dateTime.toLocalDate()) : null; + } + + public static Timestamp parseTimestamp(String timeStampStr, ZoneId zoneId) { + LocalDateTime dateTime = parseLocalDateTime(timeStampStr, zoneId); + return dateTime != null ? Timestamp.valueOf(dateTime) : null; + } + + private static LocalDateTime parseLocalDateTime(String timeStampStr, ZoneId zoneId) { + try { + return parseMilliSecTimestamp(timeStampStr, zoneId); + } catch (DateTimeParseException e) { + try { + return parseMicroSecTimestamp(timeStampStr, zoneId); + } catch (DateTimeParseException ee) { + return parseNanoSecTimestamp(timeStampStr, zoneId); + } + } + } + + private static LocalDateTime parseMilliSecTimestamp(String timeStampStr, ZoneId zoneId) throws DateTimeParseException { + if (zoneId == null){ + return LocalDateTime.parse(timeStampStr, milliSecFormatter); + } else { + LocalDateTime dateTime = LocalDateTime.parse(timeStampStr, milliSecFormatter); + ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); + return zonedDateTime.toLocalDateTime(); + } + } + + private static LocalDateTime parseMicroSecTimestamp(String timeStampStr, ZoneId zoneId) throws DateTimeParseException { + if (zoneId == null) { + return LocalDateTime.parse(timeStampStr, microSecFormatter); + } else { + LocalDateTime dateTime = LocalDateTime.parse(timeStampStr, microSecFormatter); + ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); + return zonedDateTime.toLocalDateTime(); + } + } + + private static LocalDateTime parseNanoSecTimestamp(String timeStampStr, ZoneId zoneId) throws DateTimeParseException { + if (zoneId == null) { + return LocalDateTime.parse(timeStampStr, nanoSecFormatter); + } else { + LocalDateTime dateTime = LocalDateTime.parse(timeStampStr, nanoSecFormatter); + ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); + return zonedDateTime.toLocalDateTime(); + } + } + + public static Timestamp toUTC(Timestamp timestamp, ZoneId zoneId) { + if (zoneId == null) { + return timestamp; + } + + Instant instant = timestamp.toInstant(); + ZonedDateTime zonedDateTime = instant.atZone(zoneId); + ZonedDateTime utcDateTime = zonedDateTime.withZoneSameInstant(stdZoneId); + return Timestamp.valueOf(utcDateTime.toLocalDateTime()); + } + public static Timestamp toUTC(Timestamp timestamp, Calendar cal) { + if (cal == null) { + return timestamp; + } + + TimeZone calTimeZone = cal.getTimeZone(); + ZoneId zoneId = calTimeZone.toZoneId(); + + return toUTC(timestamp, zoneId); + } +} diff --git a/src/main/java/com/taosdata/jdbc/utils/SqlSyntaxValidator.java b/src/main/java/com/taosdata/jdbc/utils/SqlSyntaxValidator.java index 77730286..ec3c5046 100644 --- a/src/main/java/com/taosdata/jdbc/utils/SqlSyntaxValidator.java +++ b/src/main/java/com/taosdata/jdbc/utils/SqlSyntaxValidator.java @@ -1,17 +1,3 @@ -/*************************************************************************** - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - *****************************************************************************/ package com.taosdata.jdbc.utils; import java.util.regex.Pattern; diff --git a/src/main/java/com/taosdata/jdbc/utils/Utils.java b/src/main/java/com/taosdata/jdbc/utils/Utils.java index 7c85adb3..24e582cb 100644 --- a/src/main/java/com/taosdata/jdbc/utils/Utils.java +++ b/src/main/java/com/taosdata/jdbc/utils/Utils.java @@ -29,67 +29,6 @@ public class Utils { private static final Pattern ptn = Pattern.compile(".*?'"); - private static final DateTimeFormatter milliSecFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSS").toFormatter(); - private static final DateTimeFormatter microSecFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSSSSS").toFormatter(); - private static final DateTimeFormatter nanoSecFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS").toFormatter(); - - public static Time parseTime(String timestampStr, ZoneId zoneId) throws DateTimeParseException { - LocalDateTime dateTime = parseLocalDateTime(timestampStr, zoneId); - return dateTime != null ? Time.valueOf(dateTime.toLocalTime()) : null; - } - - public static Date parseDate(String timestampStr, ZoneId zoneId) { - LocalDateTime dateTime = parseLocalDateTime(timestampStr, zoneId); - return dateTime != null ? Date.valueOf(dateTime.toLocalDate()) : null; - } - - public static Timestamp parseTimestamp(String timeStampStr, ZoneId zoneId) { - LocalDateTime dateTime = parseLocalDateTime(timeStampStr, zoneId); - return dateTime != null ? Timestamp.valueOf(dateTime) : null; - } - - private static LocalDateTime parseLocalDateTime(String timeStampStr, ZoneId zoneId) { - try { - return parseMilliSecTimestamp(timeStampStr, zoneId); - } catch (DateTimeParseException e) { - try { - return parseMicroSecTimestamp(timeStampStr, zoneId); - } catch (DateTimeParseException ee) { - return parseNanoSecTimestamp(timeStampStr, zoneId); - } - } - } - - private static LocalDateTime parseMilliSecTimestamp(String timeStampStr, ZoneId zoneId) throws DateTimeParseException { - if (zoneId == null){ - return LocalDateTime.parse(timeStampStr, milliSecFormatter); - } else { - LocalDateTime dateTime = LocalDateTime.parse(timeStampStr, milliSecFormatter); - ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); - return zonedDateTime.toLocalDateTime(); - } - } - - private static LocalDateTime parseMicroSecTimestamp(String timeStampStr, ZoneId zoneId) throws DateTimeParseException { - if (zoneId == null) { - return LocalDateTime.parse(timeStampStr, microSecFormatter); - } else { - LocalDateTime dateTime = LocalDateTime.parse(timeStampStr, microSecFormatter); - ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); - return zonedDateTime.toLocalDateTime(); - } - } - - private static LocalDateTime parseNanoSecTimestamp(String timeStampStr, ZoneId zoneId) throws DateTimeParseException { - if (zoneId == null) { - return LocalDateTime.parse(timeStampStr, nanoSecFormatter); - } else { - LocalDateTime dateTime = LocalDateTime.parse(timeStampStr, nanoSecFormatter); - ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); - return zonedDateTime.toLocalDateTime(); - } - } - public static String escapeSingleQuota(String origin) { Matcher m = ptn.matcher(origin); StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java b/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java index 62348e49..812c050a 100644 --- a/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java +++ b/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java @@ -8,6 +8,7 @@ import com.taosdata.jdbc.TaosGlobalConfig; import com.taosdata.jdbc.enums.TimestampPrecision; import com.taosdata.jdbc.utils.DataTypeConverUtil; +import com.taosdata.jdbc.utils.DateTimeUtils; import com.taosdata.jdbc.utils.Utils; import com.taosdata.jdbc.ws.entity.QueryResp; @@ -260,7 +261,7 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { } Timestamp ret; try { - ret = Utils.parseTimestamp(tmp, zoneId); + ret = DateTimeUtils.parseTimestamp(tmp, zoneId); } catch (Exception e) { ret = null; wasNull = true; diff --git a/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java b/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java index 7d860a7a..c96bc2ac 100644 --- a/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java +++ b/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java @@ -4,9 +4,9 @@ import com.taosdata.jdbc.common.ColumnInfo; import com.taosdata.jdbc.common.SerializeBlock; import com.taosdata.jdbc.enums.BindType; -import com.taosdata.jdbc.enums.DataType; import com.taosdata.jdbc.enums.TimestampPrecision; import com.taosdata.jdbc.rs.ConnectionParam; +import com.taosdata.jdbc.utils.DateTimeUtils; import com.taosdata.jdbc.utils.ReqId; import com.taosdata.jdbc.utils.Utils; import com.taosdata.jdbc.ws.entity.Action; @@ -14,7 +14,6 @@ import com.taosdata.jdbc.ws.entity.Request; import com.taosdata.jdbc.ws.entity.Response; import com.taosdata.jdbc.ws.stmt.entity.ExecResp; -import com.taosdata.jdbc.ws.stmt.entity.GetColFieldsResp; import com.taosdata.jdbc.ws.stmt.entity.RequestFactory; import com.taosdata.jdbc.ws.stmt.entity.StmtResp; @@ -26,8 +25,7 @@ import java.nio.charset.StandardCharsets; import java.sql.Date; import java.sql.*; -import java.time.LocalDateTime; -import java.time.ZoneId; +import java.time.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; @@ -558,7 +556,16 @@ public void setTime(int parameterIndex, Time x) throws SQLException { @Override public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + if (this.zoneId == null) { + column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + } else { + Instant instant = x.toInstant(); + ZonedDateTime zonedDateTime = instant.atZone(this.zoneId); + ZonedDateTime utcDateTime = zonedDateTime.withZoneSameInstant(ZoneId.systemDefault()); + Timestamp utcTimestamp = Timestamp.valueOf(utcDateTime.toLocalDateTime()); + + column.put(parameterIndex, new Column(utcTimestamp, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + } } @Override @@ -608,7 +615,8 @@ public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQ column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_DOUBLE, parameterIndex)); break; case Types.TIMESTAMP: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + Timestamp timestamp = DateTimeUtils.toUTC((Timestamp) x, zoneId); + column.put(parameterIndex, new Column(timestamp, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); break; case Types.BINARY: case Types.VARCHAR: @@ -656,7 +664,24 @@ public void setObject(int parameterIndex, Object x) throws SQLException { } else if (x instanceof Timestamp) { setTimestamp(parameterIndex, (Timestamp) x); } else if (x instanceof LocalDateTime) { - setTimestamp(parameterIndex, Timestamp.valueOf((LocalDateTime)x)); + if (zoneId == null) { + setTimestamp(parameterIndex, Timestamp.valueOf((LocalDateTime) x)); + } else { + ZonedDateTime zonedDateTime = ((LocalDateTime) x).atZone(zoneId); + Instant instant = zonedDateTime.toInstant(); + Timestamp timestamp = Timestamp.from(instant); + setTimestamp(parameterIndex, timestamp); + } + } else if (x instanceof Instant) { + column.put(parameterIndex, new Column(Timestamp.from((Instant) x), TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + } else if (x instanceof ZonedDateTime) { + ZonedDateTime zonedDateTime = (ZonedDateTime) x; + Instant instant = zonedDateTime.toInstant(); + column.put(parameterIndex, new Column(Timestamp.from(instant), TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + } else if (x instanceof OffsetDateTime) { + OffsetDateTime offsetDateTime = (OffsetDateTime) x; + Instant instant = offsetDateTime.toInstant(); + column.put(parameterIndex, new Column(Timestamp.from(instant), TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); } else { throw new SQLException("Unsupported data type: " + x.getClass().getName()); } @@ -844,7 +869,8 @@ public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLExceptio @Override public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { - throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD); + Timestamp timestamp = DateTimeUtils.toUTC(x, cal); + column.put(parameterIndex, new Column(timestamp, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); } @Override diff --git a/src/main/java/com/taosdata/jdbc/ws/WSStatement.java b/src/main/java/com/taosdata/jdbc/ws/WSStatement.java index e873b310..0ac62d58 100644 --- a/src/main/java/com/taosdata/jdbc/ws/WSStatement.java +++ b/src/main/java/com/taosdata/jdbc/ws/WSStatement.java @@ -24,7 +24,8 @@ public class WSStatement extends AbstractStatement { private int queryTimeout = 0; - private final ZoneId zoneId; + protected final ZoneId zoneId; + protected final ZoneId stdZoneId = ZoneId.of("UTC"); public WSStatement(Transport transport, String database, AbstractConnection connection, Long instanceId, ZoneId zoneId) { this.transport = transport; diff --git a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java index ed2f19bb..c9d79a2c 100644 --- a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java +++ b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java @@ -11,6 +11,7 @@ import com.taosdata.jdbc.rs.RestfulResultSet; import com.taosdata.jdbc.rs.RestfulResultSetMetaData; import com.taosdata.jdbc.utils.DataTypeConverUtil; +import com.taosdata.jdbc.utils.DateTimeUtils; import com.taosdata.jdbc.utils.Utils; import com.taosdata.jdbc.ws.Transport; import com.taosdata.jdbc.ws.entity.Code; @@ -276,7 +277,7 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { } Timestamp ret; try { - ret = Utils.parseTimestamp(value.toString(), null); + ret = DateTimeUtils.parseTimestamp(value.toString(), null); } catch (Exception e) { ret = null; wasNull = true; diff --git a/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java b/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java index c79b3c08..76e3896a 100644 --- a/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java +++ b/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java @@ -490,14 +490,14 @@ public void testGetDate() { try { byte[] dateBytes = dateString.getBytes("UTF-8"); Date parsedDate = DataTypeConverUtil.getDate(dateBytes, null); - assertEquals(Utils.parseDate(dateString, null), parsedDate); + assertEquals(DateTimeUtils.parseDate(dateString, null), parsedDate); } catch (UnsupportedEncodingException e) { fail("Unexpected UnsupportedEncodingException: " + e.getMessage()); } // Test with String Date parsedDateFromString = DataTypeConverUtil.getDate(dateString, null); - assertEquals(Utils.parseDate(dateString, null), parsedDateFromString); + assertEquals(DateTimeUtils.parseDate(dateString, null), parsedDateFromString); } diff --git a/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java b/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java index c729c95e..330cb009 100644 --- a/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java @@ -7,28 +7,50 @@ import com.taosdata.jdbc.utils.SpecifyAddress; import org.junit.*; import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import java.sql.*; import java.time.Instant; +import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.util.Properties; -import java.util.TimeZone; +import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.stream.IntStream; +import java.util.stream.Stream; -@RunWith(CatalogRunner.class) -@TestTarget(alias = "websocket query test", author = "huolibo", version = "2.0.38") -@FixMethodOrder +@RunWith(Parameterized.class) +@TestTarget(alias = "websocket timezon test", author = "sheyj", version = "3.5.0") public class WSTimeZoneTest { + + // 定义参数 + private String precision; + + // 构造函数 + public WSTimeZoneTest(String precision) { + this.precision = precision; + } + private static final String host = "127.0.0.1"; private static final int port = 6041; private static final String db_name = "ws_query"; private static final String tableName = "wq"; private Connection connection; + // 提供参数 + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList("'ms'", "'us'", "'ns'"); + } + + private static HashMap precisionMap = new HashMap() {{ + put("'ms'", "123"); + put("'us'", "123456"); + put("'ns'", "123456789"); + }}; + @Test - public void TimeZoneTest() throws SQLException, InterruptedException { + public void TimeZoneQueryTest() throws SQLException, InterruptedException { try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("select * from " + db_name + "." + tableName + " limit 1")) { while (resultSet.next()) { @@ -37,7 +59,7 @@ public void TimeZoneTest() throws SQLException, InterruptedException { System.out.println("ts: " + ts); System.out.println("ts: " + ts.getTime()); - Assert.assertEquals("2024-01-01 00:00:00.0", ts.toString()); + Assert.assertEquals("2024-01-01 01:00:00." + precisionMap.get(this.precision), ts.toString()); } } catch (SQLException e) { e.printStackTrace(); @@ -46,14 +68,32 @@ public void TimeZoneTest() throws SQLException, InterruptedException { } @Test - public void TimeZoneTest2() throws SQLException, InterruptedException { - Instant instant = Instant.ofEpochMilli(1704038400000L); - ZonedDateTime zonedDateTime1 = instant.atZone(ZoneId.of("Asia/Tokyo")); - System.out.println("zonedDateTime1: " + zonedDateTime1); + public void TimeZoneStmtTest() throws SQLException, InterruptedException { + String sql = "insert into " + db_name + "." + tableName + " values(?, ?)"; + PreparedStatement statement = connection.prepareStatement(sql); + + LocalDateTime localDateTime = LocalDateTime.of(2024, 1, 2, 0, 0, 0, 123456789); + statement.setTimestamp(1, Timestamp.valueOf(localDateTime)); + statement.setInt(2, 2); + int i = statement.executeUpdate(); + Assert.assertEquals(1, i); + statement.close(); + + try (Statement qStmt = connection.createStatement(); + ResultSet resultSet = qStmt.executeQuery("select * from " + db_name + "." + tableName + " where f = 2 limit 1")) { + while (resultSet.next()) { + + Timestamp ts = resultSet.getTimestamp("ts"); + + System.out.println("ts: " + ts); + System.out.println("ts: " + ts.getTime()); + Assert.assertEquals("2024-01-02 01:00:00." + precisionMap.get(this.precision), ts.toString()); + } + } catch (SQLException e) { + e.printStackTrace(); + throw e; + } - Instant instant2 = Instant.ofEpochMilli(1704038400000L); - ZonedDateTime zonedDateTime2 = instant2.atZone(ZoneId.of("Asia/Shanghai")); - System.out.println("zonedDateTime2: " + zonedDateTime2); } @@ -101,12 +141,12 @@ public void before() throws SQLException { connection = DriverManager.getConnection(url, properties); Statement statement = connection.createStatement(); statement.execute("drop database if exists " + db_name); - statement.execute("create database " + db_name); + statement.execute("create database " + db_name + " precision " + precision); statement.execute("use " + db_name); statement.execute("create table if not exists " + db_name + "." + tableName + "(ts timestamp, f int)"); // Asia/Shanghai +08:00, 2024-01-01 00:00:00 - statement.execute("insert into " + db_name + "." + tableName + " values (\"2024-01-01T00:00:00.000+08:00\", 1)"); + statement.execute("insert into " + db_name + "." + tableName + " values (\"2024-01-01T00:00:00.123456789+08:00\", 1)"); statement.close(); } From 21605063d9c3772adbbfc814a9cd71f6b54b4c09 Mon Sep 17 00:00:00 2001 From: sheyanjie-qq <249478495@qq.com> Date: Fri, 27 Dec 2024 11:10:11 +0800 Subject: [PATCH 3/4] support set timezone on websocket connection --- README-CN.md | 2 +- README.md | 2 +- deploy-pom.xml | 2 +- pom.xml | 2 +- .../com/taosdata/jdbc/AbstractDriver.java | 1 + .../jdbc/AbstractParameterMetaData.java | 5 + .../com/taosdata/jdbc/AbstractStatement.java | 1 - .../java/com/taosdata/jdbc/BlockData.java | 36 +- .../taosdata/jdbc/TSDBPreparedStatement.java | 74 +- .../java/com/taosdata/jdbc/TSDBResultSet.java | 7 +- .../taosdata/jdbc/TSDBResultSetBlockData.java | 11 +- .../taosdata/jdbc/TaosPrepareStatement.java | 65 ++ .../com/taosdata/jdbc/common/ColumnInfo.java | 26 +- .../taosdata/jdbc/common/DataLengthCfg.java | 33 +- .../taosdata/jdbc/common/SerializeBlock.java | 678 ++++++++++----- .../com/taosdata/jdbc/common/TableInfo.java | 45 + .../taosdata/jdbc/enums/FeildBindType.java | 27 + .../com/taosdata/jdbc/rs/ConnectionParam.java | 4 +- .../taosdata/jdbc/rs/RestfulStatement.java | 8 +- .../jdbc/utils/DataTypeConverUtil.java | 121 ++- .../taosdata/jdbc/utils/DateTimeUtils.java | 148 +++- .../taosdata/jdbc/ws/AbstractWSResultSet.java | 13 +- .../com/taosdata/jdbc/ws/BlockResultSet.java | 60 +- .../jdbc/ws/TSWSPreparedStatement.java | 782 +++++++++--------- .../java/com/taosdata/jdbc/ws/Transport.java | 42 +- .../com/taosdata/jdbc/ws/WSStatement.java | 4 +- .../com/taosdata/jdbc/ws/entity/Action.java | 25 +- .../taosdata/jdbc/ws/stmt/entity/BindReq.java | 29 - .../jdbc/ws/stmt/entity/GetColFieldsReq.java | 21 - .../taosdata/jdbc/ws/stmt/entity/InitReq.java | 6 - .../jdbc/ws/stmt/entity/RequestFactory.java | 79 -- .../jdbc/ws/stmt/entity/SetTableNameReq.java | 29 - .../jdbc/ws/stmt/entity/SetTagReq.java | 29 - .../ws/{stmt => stmt2}/entity/CloseReq.java | 2 +- .../ws/{stmt => stmt2}/entity/ExecReq.java | 2 +- .../jdbc/ws/{stmt => stmt2}/entity/Field.java | 24 +- .../jdbc/ws/stmt2/entity/InitReq.java | 27 + .../ws/{stmt => stmt2}/entity/PrepareReq.java | 14 +- .../jdbc/ws/stmt2/entity/RequestFactory.java | 49 ++ .../entity/ResultReq.java} | 4 +- .../entity/ResultResp.java} | 20 +- .../entity/Stmt2ExecResp.java} | 4 +- .../ws/stmt2/entity/Stmt2PrepareResp.java | 50 ++ .../entity/Stmt2Resp.java} | 6 +- .../jdbc/ws/tmq/WSConsumerResultSet.java | 5 +- .../jdbc/TSDBPreparedStatementPerfTest.java | 1 + .../com/taosdata/jdbc/TSDBResultSetTest.java | 1 + .../jdbc/utils/DataTypeConverUtilTest.java | 84 +- .../jdbc/utils/InsertSqlPatternTest.java | 26 +- .../jdbc/ws/TSWSPreparedStatementTest.java | 7 +- .../com/taosdata/jdbc/ws/TaosAdapterMock.java | 2 +- .../taosdata/jdbc/ws/WSDriverBaseTest.java | 7 + .../com/taosdata/jdbc/ws/WSTimeZoneTest.java | 105 ++- .../jdbc/ws/stmt/WsPStmtAllTypeNullTest.java | 183 ++++ .../WsPstmtAllTypeTest.java} | 43 +- .../WsPstmtNsTest.java} | 4 +- .../jdbc/ws/stmt/WsPstmtStmt2Test.java | 366 ++++++++ .../WsPstmtSubTableTest.java} | 7 +- .../WsPstmtTest.java} | 7 +- 59 files changed, 2320 insertions(+), 1147 deletions(-) create mode 100644 src/main/java/com/taosdata/jdbc/TaosPrepareStatement.java create mode 100644 src/main/java/com/taosdata/jdbc/common/TableInfo.java create mode 100644 src/main/java/com/taosdata/jdbc/enums/FeildBindType.java delete mode 100644 src/main/java/com/taosdata/jdbc/ws/stmt/entity/BindReq.java delete mode 100644 src/main/java/com/taosdata/jdbc/ws/stmt/entity/GetColFieldsReq.java delete mode 100644 src/main/java/com/taosdata/jdbc/ws/stmt/entity/InitReq.java delete mode 100644 src/main/java/com/taosdata/jdbc/ws/stmt/entity/RequestFactory.java delete mode 100644 src/main/java/com/taosdata/jdbc/ws/stmt/entity/SetTableNameReq.java delete mode 100644 src/main/java/com/taosdata/jdbc/ws/stmt/entity/SetTagReq.java rename src/main/java/com/taosdata/jdbc/ws/{stmt => stmt2}/entity/CloseReq.java (92%) rename src/main/java/com/taosdata/jdbc/ws/{stmt => stmt2}/entity/ExecReq.java (92%) rename src/main/java/com/taosdata/jdbc/ws/{stmt => stmt2}/entity/Field.java (60%) create mode 100644 src/main/java/com/taosdata/jdbc/ws/stmt2/entity/InitReq.java rename src/main/java/com/taosdata/jdbc/ws/{stmt => stmt2}/entity/PrepareReq.java (70%) create mode 100644 src/main/java/com/taosdata/jdbc/ws/stmt2/entity/RequestFactory.java rename src/main/java/com/taosdata/jdbc/ws/{stmt/entity/AddBatchReq.java => stmt2/entity/ResultReq.java} (84%) rename src/main/java/com/taosdata/jdbc/ws/{stmt/entity/GetColFieldsResp.java => stmt2/entity/ResultResp.java} (60%) rename src/main/java/com/taosdata/jdbc/ws/{stmt/entity/ExecResp.java => stmt2/entity/Stmt2ExecResp.java} (87%) create mode 100644 src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Stmt2PrepareResp.java rename src/main/java/com/taosdata/jdbc/ws/{stmt/entity/StmtResp.java => stmt2/entity/Stmt2Resp.java} (82%) create mode 100644 src/test/java/com/taosdata/jdbc/ws/stmt/WsPStmtAllTypeNullTest.java rename src/test/java/com/taosdata/jdbc/ws/{WSPreparedStatementAllTypeTest.java => stmt/WsPstmtAllTypeTest.java} (79%) rename src/test/java/com/taosdata/jdbc/ws/{WSPreparedStatementNsTest.java => stmt/WsPstmtNsTest.java} (98%) create mode 100644 src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtStmt2Test.java rename src/test/java/com/taosdata/jdbc/ws/{TaosPrepareStatementTest.java => stmt/WsPstmtSubTableTest.java} (97%) rename src/test/java/com/taosdata/jdbc/ws/{WSPreparedStatementTest.java => stmt/WsPstmtTest.java} (98%) diff --git a/README-CN.md b/README-CN.md index f0cf2860..b8eae118 100644 --- a/README-CN.md +++ b/README-CN.md @@ -76,7 +76,7 @@ Maven 项目中,在 pom.xml 中添加以下依赖: com.taosdata.jdbc taos-jdbcdriver - 3.3.0 + 3.5.0 ``` diff --git a/README.md b/README.md index aa92a84d..1b425708 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Add following dependency in the `pom.xml` file of your Maven project: com.taosdata.jdbc taos-jdbcdriver - 3.0.* + 3.5.0 ``` diff --git a/deploy-pom.xml b/deploy-pom.xml index 303d53f7..157206dd 100755 --- a/deploy-pom.xml +++ b/deploy-pom.xml @@ -5,7 +5,7 @@ com.taosdata.jdbc taos-jdbcdriver - 3.4.1 + 3.5.0 jar JDBCDriver diff --git a/pom.xml b/pom.xml index e0292459..2300804b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.taosdata.jdbc taos-jdbcdriver - 3.4.1-SNAPSHOT + 3.5.0 jar JDBCDriver diff --git a/src/main/java/com/taosdata/jdbc/AbstractDriver.java b/src/main/java/com/taosdata/jdbc/AbstractDriver.java index 24e0788a..f0adbad1 100644 --- a/src/main/java/com/taosdata/jdbc/AbstractDriver.java +++ b/src/main/java/com/taosdata/jdbc/AbstractDriver.java @@ -76,6 +76,7 @@ protected Connection getWSConnection(String url, ConnectionParam param, Properti transport.setTextMessageHandler(message -> { try { + log.trace("received message: {}", message); JsonNode jsonObject = JsonUtil.getObjectReader().readTree(message); Action action = Action.of(jsonObject.get("action").asText()); ObjectReader actionReader = JsonUtil.getObjectReader(action.getResponseClazz()); diff --git a/src/main/java/com/taosdata/jdbc/AbstractParameterMetaData.java b/src/main/java/com/taosdata/jdbc/AbstractParameterMetaData.java index 7d9d8ee5..1cc81616 100644 --- a/src/main/java/com/taosdata/jdbc/AbstractParameterMetaData.java +++ b/src/main/java/com/taosdata/jdbc/AbstractParameterMetaData.java @@ -4,6 +4,7 @@ import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; +import java.time.Instant; public abstract class AbstractParameterMetaData extends WrapperImpl implements ParameterMetaData { @@ -91,6 +92,8 @@ public int getParameterType(int param) throws SQLException { if (parameters[param - 1] instanceof Timestamp) return Types.TIMESTAMP; + if (parameters[param - 1] instanceof Instant) + return Types.TIMESTAMP; if (parameters[param - 1] instanceof Byte) return Types.TINYINT; if (parameters[param - 1] instanceof Short) @@ -117,6 +120,8 @@ public String getParameterTypeName(int param) throws SQLException { if (param < 1 && param >= parameters.length) throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_PARAMETER_INDEX_OUT_RANGE); + if (parameters[param - 1] instanceof Instant) + return TSDBConstants.jdbcType2TaosTypeName(Types.TIMESTAMP); if (parameters[param - 1] instanceof Timestamp) return TSDBConstants.jdbcType2TaosTypeName(Types.TIMESTAMP); if (parameters[param - 1] instanceof Byte) diff --git a/src/main/java/com/taosdata/jdbc/AbstractStatement.java b/src/main/java/com/taosdata/jdbc/AbstractStatement.java index 7f443add..34a7d990 100644 --- a/src/main/java/com/taosdata/jdbc/AbstractStatement.java +++ b/src/main/java/com/taosdata/jdbc/AbstractStatement.java @@ -367,5 +367,4 @@ public boolean isCloseOnCompletion() throws SQLException { public Long getInstanceId(){ return this.instanceId; } - } diff --git a/src/main/java/com/taosdata/jdbc/BlockData.java b/src/main/java/com/taosdata/jdbc/BlockData.java index 7908f1f2..3ebe7414 100644 --- a/src/main/java/com/taosdata/jdbc/BlockData.java +++ b/src/main/java/com/taosdata/jdbc/BlockData.java @@ -1,9 +1,12 @@ package com.taosdata.jdbc; import com.taosdata.jdbc.rs.RestfulResultSet; +import com.taosdata.jdbc.utils.DataTypeConverUtil; +import com.taosdata.jdbc.utils.DateTimeUtils; import java.nio.ByteBuffer; import java.sql.SQLException; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Semaphore; @@ -19,9 +22,15 @@ public class BlockData { private int numOfRows; private ByteBuffer buffer; private List fields; - Semaphore semaphore; + private final Semaphore semaphore; + private int precision; - public BlockData(List> data, int returnCode, int numOfRows, ByteBuffer buffer, List fields) { + public BlockData(List> data, + int returnCode, + int numOfRows, + ByteBuffer buffer, + List fields, + int precision) { this.data = data; this.returnCode = returnCode; this.numOfRows = numOfRows; @@ -29,10 +38,11 @@ public BlockData(List> data, int returnCode, int numOfRows, ByteBuf this.fields = fields; this.semaphore = new Semaphore(0); this.isCompleted = false; + this.precision = precision; } - public static BlockData getEmptyBlockData(List fields) { - return new BlockData(null, 0, 0, null, fields); + public static BlockData getEmptyBlockData(List fields, int precision) { + return new BlockData(null, 0, 0, null, fields, precision); } public void handleData() { @@ -106,8 +116,7 @@ public void handleData() { break; } case TSDB_DATA_TYPE_BIGINT: - case TSDB_DATA_TYPE_UBIGINT: - case TSDB_DATA_TYPE_TIMESTAMP: { + case TSDB_DATA_TYPE_UBIGINT: { length = bitMapOffset; byte[] tmp = new byte[bitMapOffset]; buffer.get(tmp); @@ -121,6 +130,21 @@ public void handleData() { } break; } + case TSDB_DATA_TYPE_TIMESTAMP: { + length = bitMapOffset; + byte[] tmp = new byte[bitMapOffset]; + buffer.get(tmp); + for (int j = 0; j < numOfRows; j++) { + long l = buffer.getLong(); + if (isNull(tmp, j)) { + col.add(null); + } else { + Instant instant = DateTimeUtils.parseTimestampColumnData(l, precision); + col.add(instant); + } + } + break; + } case TSDB_DATA_TYPE_FLOAT: { length = bitMapOffset; byte[] tmp = new byte[bitMapOffset]; diff --git a/src/main/java/com/taosdata/jdbc/TSDBPreparedStatement.java b/src/main/java/com/taosdata/jdbc/TSDBPreparedStatement.java index 97b9aa5b..ebea08ae 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBPreparedStatement.java +++ b/src/main/java/com/taosdata/jdbc/TSDBPreparedStatement.java @@ -10,28 +10,27 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.sql.*; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.PriorityQueue; +import java.sql.Date; +import java.util.*; /* * TDengine only supports a subset of the standard SQL, thus this implementation of the * standard JDBC API contains more or less some adjustments customized for certain * compatibility needs. */ -public class TSDBPreparedStatement extends TSDBStatement implements PreparedStatement { +public class TSDBPreparedStatement extends TSDBStatement implements TaosPrepareStatement { // for jdbc preparedStatement interface private String rawSql; private Object[] parameters = new Object[0]; // for parameter binding private long nativeStmtHandle; private String tableName; - private ArrayList tableTags; + private List tableTags; private int tagValueLength; private PriorityQueue queue = new PriorityQueue<>(); + TSDBPreparedStatement(TSDBConnection connection, String sql, Long instanceId) throws SQLException { super(connection, instanceId); this.rawSql = sql; @@ -356,7 +355,7 @@ public void setNClob(int parameterIndex, Reader reader) throws SQLException { // parameter binding private static class ColumnInfo implements Comparable { @SuppressWarnings("rawtypes") - private ArrayList data; + private List data; private int type; private int bytes; private boolean typeIsSet; @@ -402,6 +401,7 @@ public static TableTagInfo createNullTag(int type) { } } + @Override public void setTableName(String name) throws SQLException { if (this.nativeStmtHandle == 0) { @@ -423,65 +423,81 @@ private void ensureTagCapacity(int index) { } } + @Override public void setTagNull(int index, int type) { ensureTagCapacity(index); this.tableTags.set(index, TableTagInfo.createNullTag(type)); } + @Override public void setTagBoolean(int index, boolean value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_BOOL)); this.tagValueLength += Byte.BYTES; } + @Override public void setTagInt(int index, int value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_INT)); this.tagValueLength += Integer.BYTES; } + @Override public void setTagByte(int index, byte value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_TINYINT)); this.tagValueLength += Byte.BYTES; } + @Override public void setTagShort(int index, short value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_SMALLINT)); this.tagValueLength += Short.BYTES; } + @Override public void setTagLong(int index, long value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_BIGINT)); this.tagValueLength += Long.BYTES; } + @Override public void setTagTimestamp(int index, long value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_TIMESTAMP)); this.tagValueLength += Long.BYTES; } + @Override + public void setTagTimestamp(int index, Timestamp value) { + throw new RuntimeException("not supported"); + } + + @Override public void setTagFloat(int index, float value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_FLOAT)); this.tagValueLength += Float.BYTES; } + @Override public void setTagDouble(int index, double value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_DOUBLE)); this.tagValueLength += Double.BYTES; } + @Override public void setTagString(int index, String value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_BINARY)); this.tagValueLength += value.getBytes().length; } + @Override public void setTagNString(int index, String value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_NCHAR)); @@ -494,6 +510,7 @@ public void setTagNString(int index, String value) { } } + @Override public void setTagJson(int index, String value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_JSON)); @@ -506,75 +523,88 @@ public void setTagJson(int index, String value) { } } + @Override public void setTagVarbinary(int index, byte[] value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_VARBINARY)); this.tagValueLength += value.length; } + @Override public void setTagGeometry(int index, byte[] value) { ensureTagCapacity(index); this.tableTags.set(index, new TableTagInfo(value, TSDBConstants.TSDB_DATA_TYPE_GEOMETRY)); this.tagValueLength += value.length; } - public void setValueImpl(int columnIndex, ArrayList list, int type, int bytes) throws SQLException { + public void setValueImpl(int columnIndex, List list, int type, int bytes) throws SQLException { ColumnInfo p = new ColumnInfo(); p.setType(type); p.bytes = bytes; - p.data = (ArrayList) list.clone(); + p.data = list; p.index = columnIndex; queue.add(p); } - public void setInt(int columnIndex, ArrayList list) throws SQLException { + @Override + public void setInt(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_INT, Integer.BYTES); } - - public void setFloat(int columnIndex, ArrayList list) throws SQLException { + @Override + public void setFloat(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_FLOAT, Float.BYTES); } - public void setTimestamp(int columnIndex, ArrayList list) throws SQLException { + @Override + public void setTimestamp(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_TIMESTAMP, Long.BYTES); } - public void setLong(int columnIndex, ArrayList list) throws SQLException { + @Override + public void setLong(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_BIGINT, Long.BYTES); } - public void setDouble(int columnIndex, ArrayList list) throws SQLException { + @Override + public void setDouble(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_DOUBLE, Double.BYTES); } - public void setBoolean(int columnIndex, ArrayList list) throws SQLException { + @Override + public void setBoolean(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_BOOL, Byte.BYTES); } - public void setByte(int columnIndex, ArrayList list) throws SQLException { + public void setByte(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_TINYINT, Byte.BYTES); } - public void setShort(int columnIndex, ArrayList list) throws SQLException { + @Override + public void setShort(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_SMALLINT, Short.BYTES); } - public void setString(int columnIndex, ArrayList list, int size) throws SQLException { + @Override + public void setString(int columnIndex, List list, int size) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_BINARY, size); } - public void setVarbinary(int columnIndex, ArrayList list, int size) throws SQLException { + @Override + public void setVarbinary(int columnIndex, List list, int size) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_VARBINARY, size); } - public void setGeometry(int columnIndex, ArrayList list, int size) throws SQLException { + @Override + public void setGeometry(int columnIndex, List list, int size) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_GEOMETRY, size); } // note: expand the required space for each NChar character - public void setNString(int columnIndex, ArrayList list, int size) throws SQLException { + @Override + public void setNString(int columnIndex, List list, int size) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_NCHAR, size * Integer.BYTES); } + @Override public void columnDataAddBatch() throws SQLException { // pass the data block to native code if (rawSql == null) { diff --git a/src/main/java/com/taosdata/jdbc/TSDBResultSet.java b/src/main/java/com/taosdata/jdbc/TSDBResultSet.java index ec0e668c..606ad05b 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBResultSet.java +++ b/src/main/java/com/taosdata/jdbc/TSDBResultSet.java @@ -5,6 +5,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; +import java.time.Instant; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -192,7 +193,11 @@ public ResultSetMetaData getMetaData() throws SQLException { @Override public Object getObject(int columnIndex) throws SQLException { checkAvailability(columnIndex, this.columnMetaDataList.size()); - return this.blockData.get(columnIndex - 1); + Object o = this.blockData.get(columnIndex - 1); + if (o instanceof Instant){ + return Timestamp.from((Instant) o); + } + return o; } public int findColumn(String columnLabel) throws SQLException { diff --git a/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java b/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java index dc23253d..21c12209 100644 --- a/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java +++ b/src/main/java/com/taosdata/jdbc/TSDBResultSetBlockData.java @@ -9,6 +9,7 @@ import java.nio.ByteOrder; import java.sql.SQLException; import java.sql.Timestamp; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Semaphore; @@ -173,7 +174,7 @@ public void doSetByteArray() { if (isNull(tmp, j)) { col.add(null); } else { - col.add(DataTypeConverUtil.parseTimestampColumnData(l, this.timestampPrecision)); + col.add(DateTimeUtils.parseTimestampColumnData(l, this.timestampPrecision)); } } break; @@ -358,10 +359,12 @@ public Timestamp getTimestamp(int col) throws SQLException { } wasNull = false; int type = this.columnMetaDataList.get(col).getColType(); - if (type == TSDB_DATA_TYPE_BIGINT) - return DataTypeConverUtil.parseTimestampColumnData((long) obj, this.timestampPrecision); + if (type == TSDB_DATA_TYPE_BIGINT) { + Instant instant = DateTimeUtils.parseTimestampColumnData((long) obj, this.timestampPrecision); + return DateTimeUtils.getTimestamp(instant, null); + } if (type == TSDB_DATA_TYPE_TIMESTAMP) - return (Timestamp) obj; + return DateTimeUtils.getTimestamp((Instant) obj, null); if (obj instanceof byte[]) { String tmp = ""; String charset = TaosGlobalConfig.getCharset(); diff --git a/src/main/java/com/taosdata/jdbc/TaosPrepareStatement.java b/src/main/java/com/taosdata/jdbc/TaosPrepareStatement.java new file mode 100644 index 00000000..cf407f69 --- /dev/null +++ b/src/main/java/com/taosdata/jdbc/TaosPrepareStatement.java @@ -0,0 +1,65 @@ +package com.taosdata.jdbc; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.List; + +public interface TaosPrepareStatement extends PreparedStatement { + + void setTableName(String name) throws SQLException; + + void setTagNull(int index, int type) throws SQLException; + + void setTagBoolean(int index, boolean value); + + void setTagByte(int index, byte value); + + void setTagShort(int index, short value); + + void setTagInt(int index, int value); + + void setTagLong(int index, long value); + + void setTagFloat(int index, float value); + + void setTagDouble(int index, double value); + + void setTagTimestamp(int index, long value); + + void setTagTimestamp(int index, Timestamp value); + + void setTagString(int index, String value); + + void setTagVarbinary(int index, byte[] value); + void setTagGeometry(int index, byte[] value); + + void setTagNString(int index, String value); + + void setTagJson(int index, String value); + void setInt(int columnIndex, List list) throws SQLException; + void setFloat(int columnIndex, List list) throws SQLException; + + void setTimestamp(int columnIndex, List list) throws SQLException; + + void setLong(int columnIndex, List list) throws SQLException; + + void setDouble(int columnIndex, List list) throws SQLException; + + void setBoolean(int columnIndex, List list) throws SQLException; + + void setByte(int columnIndex, List list) throws SQLException; + + void setShort(int columnIndex, List list) throws SQLException; + + void setString(int columnIndex, List list, int size) throws SQLException; + + void setVarbinary(int columnIndex, List list, int size) throws SQLException; + void setGeometry(int columnIndex, List list, int size) throws SQLException; + // note: expand the required space for each NChar character + void setNString(int columnIndex, List list, int size) throws SQLException; + + void columnDataAddBatch() throws SQLException; + + void columnDataExecuteBatch() throws SQLException; +} diff --git a/src/main/java/com/taosdata/jdbc/common/ColumnInfo.java b/src/main/java/com/taosdata/jdbc/common/ColumnInfo.java index c30a7d02..854e8a1f 100644 --- a/src/main/java/com/taosdata/jdbc/common/ColumnInfo.java +++ b/src/main/java/com/taosdata/jdbc/common/ColumnInfo.java @@ -4,18 +4,22 @@ import java.util.List; public class ColumnInfo implements Comparable { - private List dataList = new ArrayList<>(); + private List dataList; // taos data type private final int type; private final int index; - public ColumnInfo(int columnIndex, Object data, int type) { - this.index = columnIndex; - this.dataList.add(data); - this.type = type; - } - public ColumnInfo(int columnIndex, List dataList, int type, Integer flag) { + + private int serializeSize; + +// public ColumnInfo(int columnIndex, Object data, int type) { +// this.index = columnIndex; +// this.dataList.add(data); +// this.type = type; +// } + + public ColumnInfo(int columnIndex, List dataList, int type) { this.index = columnIndex; this.dataList = dataList; this.type = type; @@ -37,6 +41,14 @@ public int getIndex() { return index; } + public int getSerializeSize() { + return serializeSize; + } + + public void setSerializeSize(int serializeSize) { + this.serializeSize = serializeSize; + } + @Override public int compareTo(ColumnInfo c) { return this.index > c.index ? 1 : -1; diff --git a/src/main/java/com/taosdata/jdbc/common/DataLengthCfg.java b/src/main/java/com/taosdata/jdbc/common/DataLengthCfg.java index e2f8fc7f..e0da523a 100644 --- a/src/main/java/com/taosdata/jdbc/common/DataLengthCfg.java +++ b/src/main/java/com/taosdata/jdbc/common/DataLengthCfg.java @@ -1,22 +1,23 @@ package com.taosdata.jdbc.common; -import java.util.HashMap; - -import static com.taosdata.jdbc.TSDBConstants.*; - public class DataLengthCfg { - private static final HashMap dataLengthMap= new HashMap(){{ - put(TSDB_DATA_TYPE_NULL, 1); - put(TSDB_DATA_TYPE_BOOL, 1); - put(TSDB_DATA_TYPE_TINYINT, 1); - put(TSDB_DATA_TYPE_SMALLINT, 2); - put(TSDB_DATA_TYPE_INT, 4); - put(TSDB_DATA_TYPE_BIGINT, 8); - put(TSDB_DATA_TYPE_FLOAT, 4); - put(TSDB_DATA_TYPE_DOUBLE, 8); - put(TSDB_DATA_TYPE_TIMESTAMP, 8); - }}; + private static final Integer[] dataLenArr = { + 1,//TSDB_DATA_TYPE_NULL + 1,//TSDB_DATA_TYPE_BOOL + 1,//TSDB_DATA_TYPE_TINYINT + 2,//TSDB_DATA_TYPE_SMALLINT + 4,//TSDB_DATA_TYPE_INT + 8,//TSDB_DATA_TYPE_BIGINT + 4,//TSDB_DATA_TYPE_FLOAT + 8,//TSDB_DATA_TYPE_DOUBLE + null, + 8//TSDB_DATA_TYPE_TIMESTAMP + }; + public static Integer getDataLength(int dataType){ - return dataLengthMap.get(dataType); + if (dataType < dataLenArr.length){ + return dataLenArr[dataType]; + } + return null; } } diff --git a/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java b/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java index 82fc71f1..0a28b57e 100644 --- a/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java +++ b/src/main/java/com/taosdata/jdbc/common/SerializeBlock.java @@ -1,9 +1,15 @@ package com.taosdata.jdbc.common; +import com.taosdata.jdbc.TSDBError; +import com.taosdata.jdbc.TSDBErrorNumbers; import com.taosdata.jdbc.enums.TimestampPrecision; +import com.taosdata.jdbc.utils.DataTypeUtil; +import com.taosdata.jdbc.utils.DateTimeUtils; +import com.taosdata.jdbc.utils.StringUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.ByteBuffer; import java.sql.SQLException; import java.sql.Timestamp; import java.time.Instant; @@ -34,12 +40,12 @@ private static byte bmSetNull(byte c, int n) { return (byte) (c + (1 << (7 - bitPos(n)))); } - private static void handleBoolean(byte[] buf, int rowIndex, int offset, Object o){ + private static void handleBoolean(ByteArrayOutputStream buf, Object o){ boolean v = (Boolean) o; if (v) { - buf[offset + rowIndex] = 1; + buf.write(1); }else { - buf[offset + rowIndex] = 0; + buf.write(0); } } private static void SerializeInt(byte[] buf, int offset, int v){ @@ -48,6 +54,7 @@ private static void SerializeInt(byte[] buf, int offset, int v){ buf[offset + 2] = (byte) ((v >> 16) & 0xFF); buf[offset + 3] = (byte) ((v >> 24) & 0xFF); } + private static void SerializeLong(byte[] buf, int offset, long v){ buf[offset] = (byte) (v & 0xFF); buf[offset + 1] = (byte) ((v >> 8) & 0xFF); @@ -62,239 +69,536 @@ private static void SerializeShort(byte[] buf, int offset, short v){ buf[offset] = (byte) (v & 0xFF); buf[offset + 1] = (byte) ((v >> 8) & 0xFF); } + public static void serializeByteArray(byte[] buf, int offset, byte[] data) { + System.arraycopy(data, 0, buf, offset, data.length); + } - private static Long getLongFromTimestamp(Timestamp o, int precision){ - long v; - if (precision == TimestampPrecision.MS) { - v = o.getTime(); - } else if (precision == TimestampPrecision.US) { - v = o.getTime() * 1000L + o.getNanos() / 1000 % 1000; - } else { - v = o.getTime() * 1000_000L + o.getNanos() % 1000_000L; + private static int serializeColumn(ColumnInfo columnInfo, byte[] buf, int offset, int precision) throws IOException, SQLException { + Integer dataLen = DataLengthCfg.getDataLength(columnInfo.getType()); + + // TotalLength + SerializeInt(buf, offset, columnInfo.getSerializeSize()); + offset += Integer.BYTES; + + // Type + SerializeInt(buf, offset, columnInfo.getType()); + offset += Integer.BYTES; + + // Num + SerializeInt(buf, offset, columnInfo.getDataList().size()); + offset += Integer.BYTES; + + // IsNull + for (Object o: columnInfo.getDataList()) { + if (o == null) { + buf[offset++] = 1; + } else { + buf[offset++] = 0; + } + } + + // haveLength + if (dataLen != null){ + buf[offset++] = 0; + // buffer + SerializeNormalDataType(columnInfo.getType(), buf, offset, columnInfo.getDataList(), precision); + return offset; + } + + // data is array type + buf[offset++] = 1; + // length + int bufferLength = 0; + for (Object o: columnInfo.getDataList()){ + if (o == null){ + SerializeInt(buf, offset, 0); + offset += Integer.BYTES; + } else { + switch (columnInfo.getType()) { + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_JSON: + case TSDB_DATA_TYPE_VARBINARY: + case TSDB_DATA_TYPE_GEOMETRY:{ + byte[] v = (byte[]) o; + SerializeInt(buf, offset, v.length); + offset += Integer.BYTES; + bufferLength += v.length; + break; + } + case TSDB_DATA_TYPE_NCHAR: { + String v = (String) o; + int len = v.getBytes().length; + SerializeInt(buf, offset, len); + offset += Integer.BYTES; + bufferLength += len; + break; + } + default: + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "unsupported data type : " + columnInfo.getType()); + } + } } - return v; + + // buffer length + SerializeInt(buf, offset, bufferLength); + offset += Integer.BYTES; + + // buffer + SerializeArrayDataType(columnInfo.getType(), buf, offset, columnInfo.getDataList()); + return offset; } - private static void handleNormalDataType(int dataType ,byte[] buf, int rowIndex, int startOffset, Object o, int precision) throws SQLException { + + private static void SerializeNormalDataType(int dataType , byte[] buf, int offset, List objectList, int precision) throws IOException, SQLException { switch (dataType) { case TSDB_DATA_TYPE_BOOL: { - handleBoolean(buf, rowIndex, startOffset, o); + SerializeInt(buf, offset, objectList.size()); + offset += Integer.BYTES; + + for (Object o: objectList){ + if (o == null) { + buf[offset++] = 0; + } else { + boolean v = (Boolean) o; + buf[offset++] = v ? (byte) 1 : (byte) 0; + } + } break; } case TSDB_DATA_TYPE_TINYINT: { - buf[rowIndex + startOffset] = (Byte) o; + SerializeInt(buf, offset, objectList.size()); + offset += Integer.BYTES; + + for (Object o: objectList){ + if (o == null) { + buf[offset++] = 0; + } else { + byte v = (Byte) o; + buf[offset++] = v; + } + } break; } case TSDB_DATA_TYPE_SMALLINT: { - short v = (Short) o; - int offset = rowIndex * Short.BYTES + startOffset; - SerializeShort(buf, offset, v); + SerializeInt(buf, offset, objectList.size() * Short.BYTES); + offset += Integer.BYTES; + + for (Object o: objectList){ + if (o != null) { + SerializeShort(buf, offset, (Short) o); + } else { + SerializeShort(buf, offset, (short)0); + } + + offset += Short.BYTES; + } break; } case TSDB_DATA_TYPE_INT: { - int v = (Integer) o; - int offset = rowIndex * Integer.BYTES + startOffset; - SerializeInt(buf, offset, v); + SerializeInt(buf, offset, objectList.size() * Integer.BYTES); + offset += Integer.BYTES; + + for (Object o: objectList){ + if (o != null) { + SerializeInt(buf, offset, (Integer) o); + } else { + SerializeInt(buf, offset, 0); + } + offset += Integer.BYTES; + } break; } case TSDB_DATA_TYPE_BIGINT: { - long v = (Long) o; - int offset = rowIndex * Long.BYTES + startOffset; - SerializeLong(buf, offset, v); - break; + SerializeInt(buf, offset, objectList.size() * Long.BYTES); + offset += Integer.BYTES; + + for (Object o: objectList){ + if (o != null) { + SerializeLong(buf, offset, (Long)o); + } else { + SerializeLong(buf, offset, 0L); + } + offset += Long.BYTES; + } + break; } case TSDB_DATA_TYPE_FLOAT: { - float v = (Float) o; - int offset = rowIndex * Float.BYTES + startOffset; - int f = Float.floatToIntBits(v); - SerializeInt(buf, offset, f); + SerializeInt(buf, offset, objectList.size() * Integer.BYTES); + offset += Integer.BYTES; + + for (Object o: objectList){ + float v = 0; + if (o != null) { + v = (Float) o; + } + int f = Float.floatToIntBits(v); + SerializeInt(buf, offset, f); + offset += Integer.BYTES; + } break; } case TSDB_DATA_TYPE_DOUBLE: { - double v = (Double) o; - int offset = rowIndex * Double.BYTES + startOffset; - long l = Double.doubleToLongBits(v); - SerializeLong(buf, offset, l); + SerializeInt(buf, offset, objectList.size() * Long.BYTES); + offset += Integer.BYTES; + + for (Object o: objectList){ + double v = 0; + if (o != null) { + v = (Double) o; + } + long l = Double.doubleToLongBits(v); + SerializeLong(buf, offset, l); + offset += Long.BYTES; + } break; } case TSDB_DATA_TYPE_TIMESTAMP: { - long v; - if (o instanceof Timestamp) { - Timestamp t = (Timestamp) o; - v = getLongFromTimestamp(t, precision); - } else { - throw new SQLException("unsupported data type : " + o.getClass().getName()); + SerializeInt(buf, offset, objectList.size() * Long.BYTES); + offset += Integer.BYTES; + + for (Object o: objectList){ + if (o != null) { + if (o instanceof Instant){ + Instant instant = (Instant) o; + long v = DateTimeUtils.toLong(instant, precision); + + SerializeLong(buf, offset, v); + offset += Long.BYTES; + } else if (o instanceof OffsetDateTime){ + OffsetDateTime offsetDateTime = (OffsetDateTime) o; + long v = DateTimeUtils.toLong(offsetDateTime.toInstant(), precision); + + SerializeLong(buf, offset, v); + offset += Long.BYTES; + } else if (o instanceof ZonedDateTime){ + ZonedDateTime zonedDateTime = (ZonedDateTime) o; + long v = DateTimeUtils.toLong(zonedDateTime.toInstant(), precision); + + SerializeLong(buf, offset, v); + offset += Long.BYTES; + } else if (o instanceof Long){ + SerializeLong(buf, offset, (Long) o); + offset += Long.BYTES; + } else { + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "unsupported timestamp data type : " + o.getClass().getName()); + } + + } else { + SerializeLong(buf, offset, 0); + offset += Long.BYTES; + } } + break; + } + default: + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "unsupported data type : " + dataType); + } + } - int offset = rowIndex * Long.BYTES + startOffset; - SerializeLong(buf, offset, v); + private static void SerializeArrayDataType(int dataType , byte[] buf, int offset, List objectList) throws SQLException { + switch (dataType) { + case TSDB_DATA_TYPE_JSON: + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_VARBINARY: + case TSDB_DATA_TYPE_GEOMETRY:{ + for (Object o: objectList){ + if (o != null) { + byte[] v = (byte[]) o; + serializeByteArray(buf, offset, v); + offset += v.length; + } + } + break; + } + case TSDB_DATA_TYPE_NCHAR: { + for (Object o: objectList){ + if (o != null) { + String v = (String) o; + byte[] bytes = v.getBytes(); + serializeByteArray(buf, offset, bytes); + offset += bytes.length; + } + } break; } default: - throw new SQLException("unsupported data type : " + dataType); + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "unsupported data type : " + dataType); } } + public static int getTagTotalLengthByTableIndex(List tableInfoList, int index, int toBebindTagCount) throws SQLException{ + int totalLength = 0; + if (toBebindTagCount > 0){ + if (tableInfoList.get(index).getTagInfo().size() != toBebindTagCount){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "table tag size is not match"); + } + for (ColumnInfo tag : tableInfoList.get(index).getTagInfo()){ + if (tag.getDataList().isEmpty()){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "tag value is null, index: " + index); + } + int columnSize = getColumnSize(tag); + tag.setSerializeSize(columnSize); + totalLength += columnSize; + } + } + return totalLength; + } - public static byte[] getRawBlock(List list, int precision) throws IOException, SQLException { - int columns = list.size(); - int rows = list.get(0).getDataList().size(); - - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - // version int32 - buffer.write(intToBytes(1)); - // length int32 - buffer.write(intToBytes(0)); - // rows int32 - buffer.write(intToBytes(rows)); - // columns int32 - buffer.write(intToBytes(columns)); - // flagSegment int32 - buffer.write(intToBytes(0)); - // groupID uint64 - buffer.write(longToBytes(0)); - - byte[] colInfoData = new byte[5 * columns]; - byte[] lengthData = new byte[4 * columns]; - - int bitMapLen = bitMapLen(rows); - ByteArrayOutputStream data = new ByteArrayOutputStream(); - for (int colIndex = 0; colIndex < list.size(); colIndex++) { - ColumnInfo column = list.get(colIndex); - - Integer dataLen = DataLengthCfg.getDataLength(column.getType()); - - // 不支持的数据类型 - if (column.getType() == TSDB_DATA_TYPE_UTINYINT - || column.getType() == TSDB_DATA_TYPE_USMALLINT - || column.getType() == TSDB_DATA_TYPE_UINT - || column.getType() == TSDB_DATA_TYPE_UBIGINT - ) { - break; + public static int getColTotalLengthByTableIndex(List tableInfoList, int index, int toBebindColCount) throws SQLException{ + int totalLength = 0; + if (toBebindColCount > 0){ + if (tableInfoList.get(index).getDataList().size() != toBebindColCount){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "table column size is not match"); } - //非数组类型 - if (dataLen != null){ - colInfoData[colIndex * 5] = (byte) column.getType(); - int typeLen = dataLen; + for (ColumnInfo columnInfo : tableInfoList.get(index).getDataList()){ + int columnSize = getColumnSize(columnInfo); + columnInfo.setSerializeSize(columnSize); + totalLength += columnSize; + } + } + return totalLength; + } - byte[] typeBytes = intToBytes(typeLen); - System.arraycopy(typeBytes, 0, colInfoData, colIndex * 5 + 1, 4); - byte[] array = intToBytes(typeLen * rows); - System.arraycopy(array, 0, lengthData, colIndex * 4, 4); + public static int getColumnSize(ColumnInfo column) throws SQLException { + Integer dataLen = DataLengthCfg.getDataLength(column.getType()); - byte[] tmp = new byte[bitMapLen + rows * dataLen]; - List rowData = column.getDataList(); + if (dataLen != null) { + // TotalLength(4) + Type (4) + Num(4) + IsNull(1) * size + haveLength(1) + BufferLength(4) + size * dataLen + return 17 + (dataLen + 1) * column.getDataList().size(); + } - for (int rowIndex = 0; rowIndex < rows; rowIndex++) { - if (rowData.get(rowIndex) == null) { - int charOffset = charOffset(rowIndex); - tmp[charOffset] = bmSetNull(tmp[charOffset], rowIndex); - } else { - handleNormalDataType(column.getType(), tmp, rowIndex, bitMapLen, rowData.get(rowIndex), precision); + switch (column.getType()) { + case TSDB_DATA_TYPE_JSON: + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_VARBINARY: + case TSDB_DATA_TYPE_GEOMETRY:{ + int totalLength = 0; + for (Object o : column.getDataList()) { + if (o != null) { + byte[] v = (byte[]) o; + totalLength += v.length; } } - data.write(tmp); - }else{ - // 数组类型 - switch (column.getType()) { - case TSDB_DATA_TYPE_BINARY: - case TSDB_DATA_TYPE_JSON: - case TSDB_DATA_TYPE_VARBINARY: - case TSDB_DATA_TYPE_GEOMETRY: - { - colInfoData[colIndex * 5] = (byte) column.getType(); - // 4 bytes for 0 - - int length = 0; - List rowData = column.getDataList(); - byte[] index = new byte[rows * Integer.BYTES]; - List tmp = new ArrayList<>(); - for (int rowIndex = 0; rowIndex < rows; rowIndex++) { - int offset = rowIndex * Integer.BYTES; - if (rowData.get(rowIndex) == null) { - for (int i = 0; i < Integer.BYTES; i++) { - index[offset + i] = (byte) 0xFF; - } - } else { - byte[] v = (byte[]) rowData.get(rowIndex); - for (int i = 0; i < Integer.BYTES; i++) { - index[offset + i] = (byte) (length >> (8 * i) & 0xFF); - } - short len = (short) v.length; - tmp.add((byte) (len & 0xFF)); - tmp.add((byte) ((len >> 8) & 0xFF)); - for (byte b : v) { - tmp.add(b); - } - length += v.length + Short.BYTES; - } - } - byte[] array = intToBytes(length); - System.arraycopy(array, 0, lengthData, colIndex * 4, 4); - data.write(index); - byte[] bytes = new byte[tmp.size()]; - for (int i = 0; i < tmp.size(); i++) { - bytes[i] = tmp.get(i); - } - data.write(bytes); - break; - } - case TSDB_DATA_TYPE_NCHAR: { - colInfoData[colIndex * 5] = (byte) column.getType();; - // 4 bytes for 0 - - int length = 0; - byte[] index = new byte[rows * Integer.BYTES]; - ByteArrayOutputStream tmp = new ByteArrayOutputStream(); - List rowData = column.getDataList(); - for (int rowIndex = 0; rowIndex < rows; rowIndex++) { - int offset = rowIndex * Integer.BYTES; - if (rowData.get(rowIndex) == null) { - for (int i = 0; i < Integer.BYTES; i++) { - index[offset + i] = (byte) 0xFF; - } - } else { - String v = (String) rowData.get(rowIndex); - for (int i = 0; i < Integer.BYTES; i++) { - index[offset + i] = (byte) ((length >> (8 * i)) & 0xFF); - } - short len = (short) (v.length() * 4); - tmp.write((byte) (len & 0xFF)); - tmp.write((byte) ((len >> 8) & 0xFF)); - int[] t = v.codePoints().toArray(); - for (int i : t) { - tmp.write(intToBytes(i)); - } - length += t.length * 4 + Short.BYTES; - } - } - byte[] array = intToBytes(length); - System.arraycopy(array, 0, lengthData, colIndex * 4, 4); - data.write(index); - data.write(tmp.toByteArray()); - break; + // TotalLength(4) + Type (4) + Num(4) + IsNull(1) * size + haveLength(1) + BufferLength(4) + 4 * v.length + totalLength + return 17 + (5 * column.getDataList().size()) + totalLength; + } + case TSDB_DATA_TYPE_NCHAR: { + int totalLength = 0; + for (Object o : column.getDataList()) { + if (o != null) { + String v = (String) o; + totalLength += v.getBytes().length; } - default: - throw new SQLException("unsupported data type : " + column.getType()); } + // TotalLength(4) + Type (4) + Num(4) + IsNull(1) * size + haveLength(1) + BufferLength(4) + 4 * v.length + totalLength + return 17 + (5 * column.getDataList().size()) + totalLength; + } + default: + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "unsupported data type : " + column.getType()); + } + } + + public static byte[] getStmt2BindBlock(long reqId, + long stmtId, + List tableInfoList, + int toBeBindTableNameIndex, + int toBebindTagCount, + int toBebindColCount, + int precision) throws IOException, SQLException { + + // cloc totol size + int totalTableNameSize = 0; + List tableNameSizeList = new ArrayList<>(); + if (toBeBindTableNameIndex >= 0) { + for (TableInfo tableInfo: tableInfoList) { + if (StringUtils.isEmpty(tableInfo.getTableName())){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "table name is empty"); + } + int tableNameSize = tableInfo.getTableName().length() + 1; + totalTableNameSize += tableNameSize; + tableNameSizeList.add((short) tableNameSize); + } + } + + int totalTagSize = 0; + List tagSizeList = new ArrayList<>(); + if (toBebindTagCount > 0){ + for (int i = 0; i < tableInfoList.size(); i++) { + int tagSize = getTagTotalLengthByTableIndex(tableInfoList, i, toBebindTagCount); + totalTagSize += tagSize; + tagSizeList.add(tagSize); + } + } + + + int totalColSize = 0; + List colSizeList = new ArrayList<>(); + if (toBebindColCount > 0) { + for (int i = 0; i < tableInfoList.size(); i++) { + int colSize = getColTotalLengthByTableIndex(tableInfoList, i, toBebindColCount); + totalColSize += colSize; + colSizeList.add(colSize); + } + } + + int totalSize = totalTableNameSize + totalTagSize + totalColSize; + int toBebindTableNameCount = toBeBindTableNameIndex >= 0 ? 1 : 0; + + totalSize += tableInfoList.size() * ( + toBebindTableNameCount * Short.BYTES + + (toBebindTagCount > 0 ? 1 : 0) * Integer.BYTES + + (toBebindColCount > 0 ? 1 : 0) * Integer.BYTES); + + byte[] buf = new byte[58 + totalSize]; + int offset = 0; + + //************ header ***************** + // ReqId + SerializeLong(buf, offset, reqId); + offset += Long.BYTES; + // stmtId + SerializeLong(buf, offset, stmtId); + offset += Long.BYTES; + // actionId + SerializeLong(buf, offset, 9L); + offset += Long.BYTES; + + // version + SerializeShort(buf, offset, (short) 1); + offset += Short.BYTES; + + // col_idx + SerializeInt(buf, offset, -1); + offset += Integer.BYTES; + + //************ data ***************** + // TotalLength + SerializeInt(buf, offset, totalSize + 28); + offset += Integer.BYTES; + + // tableCount + SerializeInt(buf, offset, tableInfoList.size()); + offset += Integer.BYTES; + + // TagCount + SerializeInt(buf, offset, toBebindTagCount); + offset += Integer.BYTES; + + // ColCount + SerializeInt(buf, offset, toBebindColCount); + offset += Integer.BYTES; + + // tableNameOffset + if (toBebindTableNameCount > 0){ + SerializeInt(buf, offset, 0x1C); + offset += Integer.BYTES; + } else { + SerializeInt(buf, offset, 0); + offset += Integer.BYTES; + } + + // tagOffset + if (toBebindTagCount > 0){ + if (toBebindTableNameCount > 0){ + SerializeInt(buf, offset, 28 + totalTableNameSize + Short.BYTES * tableInfoList.size()); + offset += Integer.BYTES; + } else { + SerializeInt(buf, offset, 28); + offset += Integer.BYTES; + } + } else { + SerializeInt(buf, offset, 0); + offset += Integer.BYTES; + } + + // colOffset + if (toBebindColCount > 0){ + int skipSize = 0; + if (toBebindTableNameCount > 0){ + skipSize += totalTableNameSize + Short.BYTES * tableInfoList.size(); + } + + if (toBebindTagCount > 0){ + skipSize += totalTagSize + Integer.BYTES * tableInfoList.size(); + } + SerializeInt(buf, offset, 28 + skipSize); + offset += Integer.BYTES; + } else { + SerializeInt(buf, offset, 0); + offset += Integer.BYTES; + } + + // TableNameLength + if (toBebindTableNameCount > 0){ + for (Short tableNameLen: tableNameSizeList){ + if (tableNameLen == 0) { + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "table name is empty"); + } + + SerializeShort(buf, offset, tableNameLen); + offset += Short.BYTES; + } + + for (TableInfo tableInfo: tableInfoList){ + if (StringUtils.isEmpty(tableInfo.getTableName())) { + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "table name is empty"); + } + + serializeByteArray(buf, offset, tableInfo.getTableName().getBytes()); + offset += tableInfo.getTableName().length(); + buf[offset++] = 0; } } - buffer.write(colInfoData); - buffer.write(lengthData); - buffer.write(data.toByteArray()); - byte[] block = buffer.toByteArray(); - for (int i = 0; i < Integer.BYTES; i++) { - block[4 + i] = (byte) (block.length >> (8 * i)); + + // TagsDataLength + if (toBebindTagCount > 0){ + for (Integer tagsize: tagSizeList) { + SerializeInt(buf, offset, tagsize); + offset += Integer.BYTES; + } + + for (int i = 0; i < tableInfoList.size(); i++) { + for (ColumnInfo tag : tableInfoList.get(i).getTagInfo()){ + if (tag.getDataList().isEmpty()){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "tag value is null, index: " + i); + } + serializeColumn(tag, buf, offset, precision); + offset += tag.getSerializeSize(); + } + } } - for (int i = 0; i < block.length; i++) { - int bb = block[i] & 0xff; - System.out.print(bb); - System.out.print(","); + // ColsDataLength + if (toBebindColCount > 0){ + for (Integer colSize: colSizeList) { + SerializeInt(buf, offset, colSize); + offset += Integer.BYTES; + } + + for (int i = 0; i < tableInfoList.size(); i++) { + for (ColumnInfo col : tableInfoList.get(i).getDataList()){ + serializeColumn(col, buf, offset, precision); + offset += col.getSerializeSize(); + } + } } - System.out.println(); - return block; + +// for (int i = 30; i < buf.length; i++) { +// int bb = buf[i] & 0xff; +// System.out.print(bb); +// System.out.print(","); +// } +// System.out.println(); + return buf; + } + + // little endian + public static byte[] shortToBytes(int v) { + byte[] result = new byte[2]; + result[0] = (byte) (v & 0xFF); + result[1] = (byte) ((v >> 8) & 0xFF); + return result; } // little endian diff --git a/src/main/java/com/taosdata/jdbc/common/TableInfo.java b/src/main/java/com/taosdata/jdbc/common/TableInfo.java new file mode 100644 index 00000000..24596440 --- /dev/null +++ b/src/main/java/com/taosdata/jdbc/common/TableInfo.java @@ -0,0 +1,45 @@ +package com.taosdata.jdbc.common; + +import java.util.ArrayList; +import java.util.List; + +public class TableInfo { + private List dataList; + private String tableName; + private List tagInfo; + + public TableInfo(List dataList, String tableName, List tagInfo) { + this.dataList = dataList; + this.tableName = tableName; + this.tagInfo = tagInfo; + } + + public static TableInfo getEmptyTableInfo() { + return new TableInfo(new ArrayList<>(), "", new ArrayList<>()); + } + public List getDataList() { + return dataList; + } + + public String getTableName() { + return tableName; + } + + public List getTagInfo() { + return tagInfo; + } + + public void setDataList(List dataList) { + this.dataList = dataList; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public void setTagInfo(List tagInfo) { + this.tagInfo = tagInfo; + } + + +} diff --git a/src/main/java/com/taosdata/jdbc/enums/FeildBindType.java b/src/main/java/com/taosdata/jdbc/enums/FeildBindType.java new file mode 100644 index 00000000..1bc6fc3c --- /dev/null +++ b/src/main/java/com/taosdata/jdbc/enums/FeildBindType.java @@ -0,0 +1,27 @@ +package com.taosdata.jdbc.enums; + +public enum FeildBindType { + TAOS_FIELD_COL(1), + TAOS_FIELD_TAG(2), + TAOS_FIELD_QUERY(3), + TAOS_FIELD_TBNAME(4); + + private final int value; + + FeildBindType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static FeildBindType fromValue(int value) { + for (FeildBindType field : FeildBindType.values()) { + if (field.value == value) { + return field; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/com/taosdata/jdbc/rs/ConnectionParam.java b/src/main/java/com/taosdata/jdbc/rs/ConnectionParam.java index 3df2db38..c4eeede9 100644 --- a/src/main/java/com/taosdata/jdbc/rs/ConnectionParam.java +++ b/src/main/java/com/taosdata/jdbc/rs/ConnectionParam.java @@ -241,11 +241,13 @@ public void setAppIp(String appIp) { public static ConnectionParam getParamWs(Properties perperties) throws SQLException { ConnectionParam connectionParam = getParam(perperties); - if (connectionParam.getTz().contains("+") + if (connectionParam.getTz() == null + || connectionParam.getTz().contains("+") || connectionParam.getTz().contains("-") || !connectionParam.getTz().contains("/")){ // for history reason, we will not support time zone with offset in websocket connection connectionParam.setTz(""); + return connectionParam; } try { diff --git a/src/main/java/com/taosdata/jdbc/rs/RestfulStatement.java b/src/main/java/com/taosdata/jdbc/rs/RestfulStatement.java index c9ff09d6..57d5b69d 100644 --- a/src/main/java/com/taosdata/jdbc/rs/RestfulStatement.java +++ b/src/main/java/com/taosdata/jdbc/rs/RestfulStatement.java @@ -136,13 +136,9 @@ private String getUrl() throws SQLException { String url; String port = null != conn.getPort() ? ":" + conn.getPort() : ""; if (this.conn.getToken() != null && !"".equals(this.conn.getToken().trim())) { - - String tz = (null == conn.getTz() || "".equals(conn.getTz().trim())) ? "" : "&tz=" + conn.getTz().trim(); - url = protocol + "://" + conn.getHost() + port + "/rest/sql" + dbname + "?token=" + this.conn.getToken() + tz; + url = protocol + "://" + conn.getHost() + port + "/rest/sql" + dbname + "?token=" + this.conn.getToken(); } else { - String tz = (null == conn.getTz() || "".equals(conn.getTz().trim())) ? "" : "?tz=" + conn.getTz().trim(); - - url = protocol + "://" + conn.getHost() + port + "/rest/sql" + dbname + tz; + url = protocol + "://" + conn.getHost() + port + "/rest/sql" + dbname; } return url; } diff --git a/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java b/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java index cc3649fb..42895128 100644 --- a/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java +++ b/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java @@ -35,7 +35,7 @@ public static boolean getBoolean(int taosType, Object value) throws SQLDataExcep case TSDB_DATA_TYPE_BIGINT: return (((long) value) == 0L) ? Boolean.FALSE : Boolean.TRUE; case TSDB_DATA_TYPE_TIMESTAMP: - return ((Timestamp) value).getTime() == 0L ? Boolean.FALSE : Boolean.TRUE; + return ((Instant) value).toEpochMilli() == 0L ? Boolean.FALSE : Boolean.TRUE; case TSDB_DATA_TYPE_UBIGINT: return value.equals(new BigDecimal(0)) ? Boolean.FALSE : Boolean.TRUE; @@ -299,16 +299,8 @@ public static long getLong(int taosType, Object value, int columnIndex, int time return tmp.longValue(); } case TSDB_DATA_TYPE_TIMESTAMP: { - Timestamp ts = (Timestamp) value; - switch (timestampPrecision) { - case TimestampPrecision.MS: - default: - return ts.getTime(); - case TimestampPrecision.US: - return ts.getTime() * 1000 + ts.getNanos() / 1000 % 1000; - case TimestampPrecision.NS: - return ts.getTime() * 1000_000 + ts.getNanos() % 1000_000; - } + Instant ts = (Instant) value; + return DateTimeUtils.toLong(ts, timestampPrecision); } case TSDB_DATA_TYPE_FLOAT: { float tmp = (float) value; @@ -415,16 +407,8 @@ public static double getDouble(int taosType, Object value, int columnIndex, int } case TSDB_DATA_TYPE_TIMESTAMP: { - Timestamp ts = (Timestamp) value; - switch (timestampPrecision) { - case TimestampPrecision.MS: - default: - return ts.getTime(); - case TimestampPrecision.US: - return ts.getTime() * 1000 + ts.getNanos() / 1000 % 1000; - case TimestampPrecision.NS: - return ts.getTime() * 1000_000 + ts.getNanos() % 1000_000; - } + Instant ts = (Instant) value; + return DateTimeUtils.toLong(ts, timestampPrecision); } case TSDB_DATA_TYPE_NCHAR: @@ -458,14 +442,16 @@ public static byte[] getBytes(Object value) throws SQLException { return Shorts.toByteArray((short) value); if (value instanceof Byte) return new byte[]{(byte) value}; - + if (value instanceof Instant) + return Timestamp.from((Instant) value).toString().getBytes(); return value.toString().getBytes(); } public static String getString(Object value) throws SQLException { if (value instanceof String) return (String) value; - + if (value instanceof Instant) + return Timestamp.from((Instant) value).toString(); if (value instanceof byte[]) { String charset = TaosGlobalConfig.getCharset(); try { @@ -478,8 +464,9 @@ public static String getString(Object value) throws SQLException { } public static Date getDate(Object value, ZoneId zoneId) { - if (value instanceof Timestamp) - return new Date(((Timestamp) value).getTime()); + if (value instanceof Instant) { + return DateTimeUtils.getDate((Instant) value, zoneId); + } if (value instanceof byte[]) { String charset = TaosGlobalConfig.getCharset(); String tmp; @@ -494,8 +481,9 @@ public static Date getDate(Object value, ZoneId zoneId) { } public static Time getTime(Object value, ZoneId zoneId) { - if (value instanceof Timestamp) - return new Time(((Timestamp) value).getTime()); + if (value instanceof Instant) { + return DateTimeUtils.getTime((Instant) value, zoneId); + } String tmp = ""; if (value instanceof byte[]) { String charset = TaosGlobalConfig.getCharset(); @@ -538,7 +526,7 @@ public static BigDecimal getBigDecimal(int taosType, Object value) { return BigDecimal.valueOf((double) value); case TSDB_DATA_TYPE_TIMESTAMP: - return new BigDecimal(((Timestamp) value).getTime()); + return new BigDecimal(((Instant) value).toEpochMilli()); case TSDB_DATA_TYPE_NCHAR: return new BigDecimal(value.toString()); case TSDB_DATA_TYPE_JSON: @@ -558,7 +546,7 @@ public static BigDecimal getBigDecimal(int taosType, Object value) { return new BigDecimal(0); } - static public Object parseValue(int type, Object source, int timestampPrecision, ZoneId zoneId){ + static public Object parseValue(int type, Object source){ switch (type) { case TSDB_DATA_TYPE_BOOL: { byte val = (byte) source; @@ -577,7 +565,8 @@ static public Object parseValue(int type, Object source, int timestampPrecision, case TSDB_DATA_TYPE_BINARY: case TSDB_DATA_TYPE_JSON: case TSDB_DATA_TYPE_VARBINARY: - case TSDB_DATA_TYPE_GEOMETRY:{ + case TSDB_DATA_TYPE_GEOMETRY: + case TSDB_DATA_TYPE_TIMESTAMP:{ return source; } case TSDB_DATA_TYPE_USMALLINT: { @@ -588,10 +577,6 @@ static public Object parseValue(int type, Object source, int timestampPrecision, int val = (int) source; return parseUInteger(val); } - case TSDB_DATA_TYPE_TIMESTAMP: { - long val = (long) source; - return parseTimestampColumnDataWithZoneId(val, timestampPrecision, zoneId); - } case TSDB_DATA_TYPE_UBIGINT: { long val = (long) source; return parseUBigInt(val); @@ -606,48 +591,34 @@ static public Object parseValue(int type, Object source, int timestampPrecision, } } - public static Timestamp parseTimestampColumnData(long value, int timestampPrecision) { - if (TimestampPrecision.MS == timestampPrecision) - return new Timestamp(value); - if (TimestampPrecision.US == timestampPrecision) { - long epochSec = value / 1000_000L; - long nanoAdjustment = value % 1000_000L * 1000L; - return Timestamp.from(Instant.ofEpochSecond(epochSec, nanoAdjustment)); - } - if (TimestampPrecision.NS == timestampPrecision) { - long epochSec = value / 1000_000_000L; - long nanoAdjustment = value % 1000_000_000L; - return Timestamp.from(Instant.ofEpochSecond(epochSec, nanoAdjustment)); - } - return null; - } - public static Timestamp parseTimestampColumnDataWithZoneId(long value, int timestampPrecision, ZoneId zoneId) { - if (zoneId == null){ - return parseTimestampColumnData(value, timestampPrecision); - } - if (TimestampPrecision.MS == timestampPrecision) { - Instant instant = Instant.ofEpochMilli(value); - LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); - return Timestamp.valueOf(localDateTime); - } - - if (TimestampPrecision.US == timestampPrecision) { - long epochSec = value / 1000_000L; - long nanoAdjustment = value % 1000_000L * 1000L; - Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment); - LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); - return Timestamp.valueOf(localDateTime); - } - if (TimestampPrecision.NS == timestampPrecision) { - long epochSec = value / 1000_000_000L; - long nanoAdjustment = value % 1000_000_000L; - Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment); - LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); - return Timestamp.valueOf(localDateTime); - } - return null; - } +// public static Timestamp parseTimestampColumnDataWithZoneId(long value, int timestampPrecision, ZoneId zoneId) { +// if (zoneId == null){ +// return parseTimestampColumnData(value, timestampPrecision); +// } +// +// if (TimestampPrecision.MS == timestampPrecision) { +// Instant instant = Instant.ofEpochMilli(value); +// LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); +// return Timestamp.valueOf(localDateTime); +// } +// +// if (TimestampPrecision.US == timestampPrecision) { +// long epochSec = value / 1000_000L; +// long nanoAdjustment = value % 1000_000L * 1000L; +// Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment); +// LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); +// return Timestamp.valueOf(localDateTime); +// } +// if (TimestampPrecision.NS == timestampPrecision) { +// long epochSec = value / 1000_000_000L; +// long nanoAdjustment = value % 1000_000_000L; +// Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment); +// LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); +// return Timestamp.valueOf(localDateTime); +// } +// return null; +// } } diff --git a/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java b/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java index ad43004d..ad294cd3 100644 --- a/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java +++ b/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java @@ -1,35 +1,22 @@ package com.taosdata.jdbc.utils; -import com.google.common.collect.Range; -import com.google.common.collect.RangeSet; -import com.google.common.collect.TreeRangeSet; - -import java.lang.reflect.Constructor; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.charset.StandardCharsets; +import com.taosdata.jdbc.enums.TimestampPrecision; + import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.time.*; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.util.Calendar; -import java.util.HashMap; -import java.util.Map; import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.IntStream; public class DateTimeUtils { private static final ZoneId stdZoneId = ZoneId.of("UTC"); + private static final ZoneId systemZoneId = ZoneId.systemDefault(); private static final Pattern ptn = Pattern.compile(".*?'"); private static final DateTimeFormatter milliSecFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSS").toFormatter(); private static final DateTimeFormatter microSecFormatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSSSSS").toFormatter(); @@ -92,24 +79,133 @@ private static LocalDateTime parseNanoSecTimestamp(String timeStampStr, ZoneId z } } - public static Timestamp toUTC(Timestamp timestamp, ZoneId zoneId) { + public static Instant toInstant(Timestamp timestamp, ZoneId zoneId) { + if (timestamp == null) { + return null; + } + if (zoneId == null) { - return timestamp; + return timestamp.toInstant(); } - Instant instant = timestamp.toInstant(); - ZonedDateTime zonedDateTime = instant.atZone(zoneId); - ZonedDateTime utcDateTime = zonedDateTime.withZoneSameInstant(stdZoneId); - return Timestamp.valueOf(utcDateTime.toLocalDateTime()); + LocalDateTime localDateTime = timestamp.toLocalDateTime(); + ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + return zonedDateTime.toInstant(); } - public static Timestamp toUTC(Timestamp timestamp, Calendar cal) { + public static Instant toInstant(Timestamp timestamp, Calendar cal) { if (cal == null) { - return timestamp; + return timestamp.toInstant(); } TimeZone calTimeZone = cal.getTimeZone(); ZoneId zoneId = calTimeZone.toZoneId(); - return toUTC(timestamp, zoneId); + return toInstant(timestamp, zoneId); + } + + public static Long toLong(Instant instant, int precision) { + long v; + if (precision == TimestampPrecision.MS) { + v = instant.toEpochMilli(); + } else if (precision == TimestampPrecision.US) { + v = instant.getEpochSecond() * 1_000_000L + instant.getNano() / 1_000L; + } else { + v = instant.getEpochSecond() * 1_000_000_000L + instant.getNano(); + } + return v; + } + +// public static ZonedDateTime getZonedDateTime(Timestamp timestamp, ZoneId zoneId) { +// if (zoneId == null) { +// return ; +// } +// +// TimeZone calTimeZone = cal.getTimeZone(); +// ZoneId zoneId = calTimeZone.toZoneId(); +// +// return toUTC(timestamp, zoneId); +// } +// public static OffsetDateTime getOffsetDateTime(Timestamp timestamp) { +// if (zoneId == null) { +// return timestamp; +// } +// +// Instant instant = timestamp.toInstant(); +// +// ZoneOffset systemOffset = systemZoneId.getRules().getOffset(instant); +// +// // 将 Instant 转换为 OffsetDateTime +// OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(instant, systemOffset); +// return offsetDateTime; +// } + + + + public static Instant parseTimestampColumnData(long value, int timestampPrecision) { + if (TimestampPrecision.MS == timestampPrecision) + return Instant.ofEpochMilli(value); + + if (TimestampPrecision.US == timestampPrecision) { + long epochSec = value / 1000_000L; + long nanoAdjustment = value % 1000_000L * 1000L; + return Instant.ofEpochSecond(epochSec, nanoAdjustment); + } + if (TimestampPrecision.NS == timestampPrecision) { + long epochSec = value / 1000_000_000L; + long nanoAdjustment = value % 1000_000_000L; + return Instant.ofEpochSecond(epochSec, nanoAdjustment); + } + return null; + } + + public static Timestamp getTimestamp(Instant instant, ZoneId zoneId) { + if (zoneId == null){ + return Timestamp.from(instant); + } + return Timestamp.valueOf(LocalDateTime.ofInstant(instant, zoneId)); + } + + public static LocalDateTime getLocalDateTime(Instant instant, ZoneId zoneId) { + if (zoneId == null){ + ZonedDateTime zonedDateTime = instant.atZone(systemZoneId); + return zonedDateTime.toLocalDateTime(); + } + + ZonedDateTime zonedDateTime = instant.atZone(zoneId); + return zonedDateTime.toLocalDateTime(); + } + public static OffsetDateTime getOffsetDateTime(Instant instant, ZoneId zoneId) { + if (zoneId == null){ + ZonedDateTime zonedDateTime = instant.atZone(systemZoneId); + return zonedDateTime.toOffsetDateTime(); + } + + ZonedDateTime zonedDateTime = instant.atZone(zoneId); + return zonedDateTime.toOffsetDateTime(); + } + + public static ZonedDateTime getZonedDateTime(Instant instant, ZoneId zoneId) { + if (zoneId == null){ + return instant.atZone(systemZoneId); + } + + return instant.atZone(zoneId); + } + + public static Date getDate(Instant instant, ZoneId zoneId) { + if (zoneId == null){ + return new Date(instant.toEpochMilli()); + } + + Timestamp timestamp = getTimestamp(instant, zoneId); + return new Date(timestamp.getTime()); + } + public static Time getTime(Instant instant, ZoneId zoneId) { + if (zoneId == null){ + return new Time(instant.toEpochMilli()); + } + + Timestamp timestamp = getTimestamp(instant, zoneId); + return new Time(timestamp.getTime()); } } diff --git a/src/main/java/com/taosdata/jdbc/ws/AbstractWSResultSet.java b/src/main/java/com/taosdata/jdbc/ws/AbstractWSResultSet.java index b528a981..b54ff330 100644 --- a/src/main/java/com/taosdata/jdbc/ws/AbstractWSResultSet.java +++ b/src/main/java/com/taosdata/jdbc/ws/AbstractWSResultSet.java @@ -1,12 +1,13 @@ package com.taosdata.jdbc.ws; -import com.taosdata.jdbc.*; -import com.taosdata.jdbc.enums.BindType; +import com.taosdata.jdbc.AbstractResultSet; +import com.taosdata.jdbc.BlockData; +import com.taosdata.jdbc.TSDBError; +import com.taosdata.jdbc.TSDBErrorNumbers; import com.taosdata.jdbc.enums.DataType; import com.taosdata.jdbc.rs.RestfulResultSet; import com.taosdata.jdbc.rs.RestfulResultSetMetaData; import com.taosdata.jdbc.ws.entity.*; -import com.taosdata.jdbc.ws.stmt.entity.StmtResp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,7 +68,7 @@ private void startBackendFetch(){ backFetchExecutor.submit(() -> { try { while (!isClosed){ - BlockData blockData = BlockData.getEmptyBlockData(fields); + BlockData blockData = BlockData.getEmptyBlockData(fields, timestampPrecision); byte[] version = {1, 0}; FetchBlockNewResp resp = (FetchBlockNewResp) transport.send(Action.FETCH_BLOCK_NEW.getAction(), @@ -94,7 +95,7 @@ private void startBackendFetch(){ Thread.currentThread().interrupt(); } catch (Exception e) { log.error("fetch block error", e); - BlockData blockData = BlockData.getEmptyBlockData(fields); + BlockData blockData = BlockData.getEmptyBlockData(fields, timestampPrecision); while (!isClosed) { try { if (blockingQueueOut.offer(blockData, 10, TimeUnit.MILLISECONDS)){ @@ -166,7 +167,7 @@ public boolean next() throws SQLException { throw TSDBError.createSQLException(resp.getCode(), "FETCH DATA ERROR"); } this.reset(); - BlockData blockData = BlockData.getEmptyBlockData(fields); + BlockData blockData = BlockData.getEmptyBlockData(fields, timestampPrecision); if (resp.isCompleted() || isClosed) { blockData.setCompleted(true); diff --git a/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java b/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java index 812c050a..c67e6c8d 100644 --- a/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java +++ b/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java @@ -15,9 +15,7 @@ import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.sql.*; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; +import java.time.*; import java.time.format.DateTimeParseException; import java.util.Calendar; @@ -40,7 +38,7 @@ public Object parseValue(int columnIndex) { return null; int type = fields.get(columnIndex - 1).getTaosType(); - return DataTypeConverUtil.parseValue(type, source, this.timestampPrecision, zoneId); + return DataTypeConverUtil.parseValue(type, source); } public Object parseValueWithZoneId(int columnIndex, ZoneId zoneId) { @@ -49,7 +47,7 @@ public Object parseValueWithZoneId(int columnIndex, ZoneId zoneId) { return null; int type = fields.get(columnIndex - 1).getTaosType(); - return DataTypeConverUtil.parseValue(type, source, this.timestampPrecision, zoneId); + return DataTypeConverUtil.parseValue(type, source); } @Override @@ -64,6 +62,8 @@ public String getString(int columnIndex) throws SQLException { wasNull = false; if (value instanceof String) return (String) value; + if (value instanceof Instant) + return DateTimeUtils.getTimestamp((Instant) value, zoneId).toString(); if (value instanceof byte[]) { String charset = TaosGlobalConfig.getCharset(); @@ -243,10 +243,13 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { return null; } wasNull = false; + if (value instanceof Instant) + return DateTimeUtils.getTimestamp((Instant) value, zoneId); if (value instanceof Timestamp) return (Timestamp) value; if (value instanceof Long) { - return DataTypeConverUtil.parseTimestampColumnDataWithZoneId((long) value, this.timestampPrecision, this.zoneId); + Instant instant = DateTimeUtils.parseTimestampColumnData((long) value, this.timestampPrecision); + return DateTimeUtils.getTimestamp(instant, zoneId); } String tmp = ""; if (value instanceof byte[]) { @@ -269,18 +272,27 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { return ret; } - @Override - public Object getObject(int columnIndex) throws SQLException { + private Object getObjectInternal(int columnIndex) throws SQLException { checkAvailability(columnIndex, fields.size()); Object value = parseValue(columnIndex); wasNull = value == null; return value; } + @Override + public Object getObject(int columnIndex) throws SQLException { + Object value = getObjectInternal(columnIndex); + + if (value instanceof Instant){ + return DateTimeUtils.getTimestamp((Instant) value, zoneId); + } else { + return value; + } + } @Override public T getObject(int columnIndex, Class type) throws SQLException { - Object value = getObject(columnIndex); + Object value = getObjectInternal(columnIndex); if (value == null) { return null; @@ -308,26 +320,16 @@ public T getObject(int columnIndex, Class type) throws SQLException { return type.cast(new BigDecimal(value.toString())); } else if (type == Byte.class && value instanceof Number) { return type.cast(((Number) value).byteValue()); - } else if (type == LocalDateTime.class && value instanceof Timestamp) { - Timestamp timestamp = (Timestamp) value; - return type.cast(timestamp.toLocalDateTime()); - } - -// else if (type == Instant.class && value instanceof Timestamp) { -// Timestamp timestamp = (Timestamp) value; -// return type.cast(Instant.toLocalDateTime()); -// } -// -// else if (type == LocalDateTime.class && value instanceof Timestamp) { -// Timestamp timestamp = (Timestamp) value; -// return type.cast(timestamp.toLocalDateTime()); -// }else if (type == LocalDateTime.class && value instanceof Timestamp) { -// Timestamp timestamp = (Timestamp) value; -// return type.cast(timestamp.toLocalDateTime()); -// } - - - else { + } else if (type == LocalDateTime.class && value instanceof Instant) { + Instant instant = (Instant) value; + return type.cast(DateTimeUtils.getLocalDateTime(instant, zoneId)); + } else if (type == OffsetDateTime.class && value instanceof Instant) { + Instant instant = (Instant) value; + return type.cast(DateTimeUtils.getOffsetDateTime(instant, zoneId)); + } else if (type == ZonedDateTime.class && value instanceof Instant) { + Instant instant = (Instant) value; + return type.cast(DateTimeUtils.getZonedDateTime(instant, zoneId)); + } else { throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_TYPE_CONVERT_EXCEPTION, "Cannot convert " + value.getClass() + " to " + type); } } catch (ClassCastException | UnsupportedEncodingException e) { diff --git a/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java b/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java index c96bc2ac..25da6e90 100644 --- a/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java +++ b/src/main/java/com/taosdata/jdbc/ws/TSWSPreparedStatement.java @@ -3,19 +3,17 @@ import com.taosdata.jdbc.*; import com.taosdata.jdbc.common.ColumnInfo; import com.taosdata.jdbc.common.SerializeBlock; -import com.taosdata.jdbc.enums.BindType; +import com.taosdata.jdbc.common.TableInfo; +import com.taosdata.jdbc.enums.FeildBindType; import com.taosdata.jdbc.enums.TimestampPrecision; import com.taosdata.jdbc.rs.ConnectionParam; import com.taosdata.jdbc.utils.DateTimeUtils; import com.taosdata.jdbc.utils.ReqId; -import com.taosdata.jdbc.utils.Utils; +import com.taosdata.jdbc.utils.StringUtils; import com.taosdata.jdbc.ws.entity.Action; import com.taosdata.jdbc.ws.entity.Code; import com.taosdata.jdbc.ws.entity.Request; -import com.taosdata.jdbc.ws.entity.Response; -import com.taosdata.jdbc.ws.stmt.entity.ExecResp; -import com.taosdata.jdbc.ws.stmt.entity.RequestFactory; -import com.taosdata.jdbc.ws.stmt.entity.StmtResp; +import com.taosdata.jdbc.ws.stmt2.entity.*; import java.io.IOException; import java.io.InputStream; @@ -27,19 +25,13 @@ import java.sql.*; import java.time.*; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import static com.taosdata.jdbc.TSDBConstants.*; -import static com.taosdata.jdbc.utils.SqlSyntaxValidator.getDatabaseName; -import static com.taosdata.jdbc.utils.SqlSyntaxValidator.isUseSql; -public class TSWSPreparedStatement extends WSStatement implements PreparedStatement { - public static final Pattern INSERT_PATTERN = Pattern.compile( - "insert\\s+into\\s+([.\\w]+|\\?)\\s+(using\\s+([.\\w]+)(\\s*\\(.*\\)\\s*|\\s+)tags\\s*\\(.*\\))?\\s*(\\(.*\\))?\\s*values\\s*\\(.*\\)" - ); +public class TSWSPreparedStatement extends WSStatement implements TaosPrepareStatement { + private static final List nullTag = Collections.singletonList(null); + private final ConnectionParam param; private long reqId; private long stmtId; @@ -47,81 +39,67 @@ public class TSWSPreparedStatement extends WSStatement implements PreparedStatem private int queryTimeout = 0; private int precision = TimestampPrecision.MS; + private int toBeBindTableNameIndex = -1; + private int toBeBindColCount = 0; + private int toBeBindTagCount = 0; + private List fields; + private boolean isInsert = false; + - private String insertDbName; - static private Map precisionHashMap = new ConcurrentHashMap(); - private final Map column = new HashMap<>(); + private final Map colOrderedMap = new TreeMap<>(); - private final Map tag = new HashMap<>(); - private final List data = new ArrayList<>(); + private final PriorityQueue tag = new PriorityQueue<>(); + private final PriorityQueue colListQueue = new PriorityQueue<>(); + + private List tableInfoList = new ArrayList<>(); + private TableInfo tableInfo; - private final PriorityQueue queue = new PriorityQueue<>(); public TSWSPreparedStatement(Transport transport, ConnectionParam param, String database, AbstractConnection connection, String sql, Long instanceId, ZoneId zoneId) throws SQLException { super(transport, database, connection, instanceId, zoneId); this.rawSql = sql; this.param = param; - this.insertDbName = database; if (!sql.contains("?")) return; - String useDb = null; - Matcher matcher = INSERT_PATTERN.matcher(sql); - if (matcher.find()) { - if (matcher.group(1).equals("?") && matcher.group(3) != null) { - String usingGroup = matcher.group(3); - if (usingGroup.contains(".")) { - String[] split = usingGroup.split("\\."); - useDb = split[0]; - } - } else { - String usingGroup = matcher.group(1); - if (usingGroup.contains(".")) { - String[] split = usingGroup.split("\\."); - useDb = split[0]; - } - } - - if (useDb == null && database != null) { - useDb = database; - } - if (useDb != null) { - insertDbName = useDb; - Integer precisionObj = precisionHashMap.get(useDb); - if (precisionObj != null){ - precision = precisionObj; - } else { - updatePrecision(useDb); - } - - - } - } - - reqId = ReqId.getReqID(); - Request request = RequestFactory.generateInit(reqId); - StmtResp resp = (StmtResp) transport.send(request); + Request request = RequestFactory.generateInit(reqId, true, true); + Stmt2Resp resp = (Stmt2Resp) transport.send(request); if (Code.SUCCESS.getCode() != resp.getCode()) { throw new SQLException("(0x" + Integer.toHexString(resp.getCode()) + "):" + resp.getMessage()); } stmtId = resp.getStmtId(); Request prepare = RequestFactory.generatePrepare(stmtId, reqId, sql); - StmtResp prepareResp = (StmtResp) transport.send(prepare); + Stmt2PrepareResp prepareResp = (Stmt2PrepareResp) transport.send(prepare); if (Code.SUCCESS.getCode() != prepareResp.getCode()) { throw new SQLException("(0x" + Integer.toHexString(prepareResp.getCode()) + "):" + prepareResp.getMessage()); } - } - private void updatePrecision(String database) throws SQLException{ - try (ResultSet resultSet = this.executeQuery("select `precision` from information_schema.ins_databases where name = '" + database + "'")) { - while (resultSet.next()) { - String tmp = resultSet.getString(1); - precision = TimestampPrecision.getPrecision(tmp); - precisionHashMap.put(database, precision); + isInsert = prepareResp.isInsert(); + if (isInsert){ + fields = prepareResp.getFields(); + if (!fields.isEmpty()){ + precision = fields.get(0).getPrecision(); + } + for (int i = 0; i < fields.size(); i++){ + Field field = fields.get(i); + if (field.getBindType() == FeildBindType.TAOS_FIELD_TBNAME.getValue()){ + toBeBindTableNameIndex = i; + } + if (field.getBindType() == FeildBindType.TAOS_FIELD_TAG.getValue()){ + toBeBindTagCount++; + } + if (field.getBindType() == FeildBindType.TAOS_FIELD_COL.getValue()){ + toBeBindColCount++; + } } + } else if (prepareResp.getFieldsCount() > 0){ + toBeBindColCount = prepareResp.getFieldsCount(); } + + this.tableInfo = TableInfo.getEmptyTableInfo(); } + @Override public int getQueryTimeout() throws SQLException { return queryTimeout; @@ -138,327 +116,288 @@ public void setQueryTimeout(int seconds) throws SQLException { transport.setTimeout(seconds * 1000L); } - private void checkUseStatement(String sql) throws SQLException { - if (sql == null || sql.isEmpty()) { - throw new SQLException("sql is empty"); - } - - if (isUseSql(sql)) { - String database = getDatabaseName(sql); - if (null != database) { - WSConnection.reInitTransport(transport, param, database); - - try (ResultSet resultSet = this.executeQuery("select `precision` from information_schema.ins_databases where name = '" + database + "'")) { - while (resultSet.next()) { - String tmp = resultSet.getString(1); - precision = TimestampPrecision.getPrecision(tmp); - } - } - - reqId = ReqId.getReqID(); - Request request = RequestFactory.generateInit(reqId); - StmtResp resp = (StmtResp) transport.send(request); - if (Code.SUCCESS.getCode() != resp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(resp.getCode()) + "):" + resp.getMessage()); - } - stmtId = resp.getStmtId(); - Request prepare = RequestFactory.generatePrepare(stmtId, reqId, rawSql); - StmtResp prepareResp = (StmtResp) transport.send(prepare); - if (Code.SUCCESS.getCode() != prepareResp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(prepareResp.getCode()) + "):" + prepareResp.getMessage()); - } - } - } - } - @Override public boolean execute(String sql, Long reqId) throws SQLException { - checkUseStatement(sql); return super.execute(sql, reqId); } @Override - public ResultSet executeQuery() throws SQLException { - List list = new ArrayList<>(); - if (!tag.isEmpty()) { - tag.keySet().stream().sorted().forEach(i -> { - Column col = this.tag.get(i); - list.add(col.data); - }); - } - if (!column.isEmpty()) { - column.keySet().stream().sorted().forEach(i -> { - Column col = this.column.get(i); - list.add(col.data); - }); + public boolean execute() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED); + + if (isInsert){ + executeUpdate(); + } else { + executeQuery(); } - Object[] parameters = list.toArray(new Object[0]); - this.clearParameters(); - final String sql = Utils.getNativeSql(this.rawSql, parameters); - return executeQuery(sql); + return !isInsert; } - @Override - public int executeUpdate() throws SQLException { - if (column.isEmpty()) - throw new SQLException("no parameter to execute"); - if (!data.isEmpty()) - throw TSDBError.undeterminedExecutionError(); - - //set tag - if (!tag.isEmpty()) { - List collect = tag.keySet().stream().sorted().map(i -> { - Column col = this.tag.get(i); - return new ColumnInfo(i, col.data, col.type); - }).collect(Collectors.toList()); - byte[] tagBlock; - try { - tagBlock = SerializeBlock.getRawBlock(collect, precision); - } catch (IOException e) { - throw new SQLException("data serialize error!", e); - } - StmtResp bindResp = (StmtResp) transport.send(Action.SET_TAGS.getAction(), - reqId, stmtId, BindType.TAG.get(), tagBlock); - if (Code.SUCCESS.getCode() != bindResp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(bindResp.getCode()) + "):" + bindResp.getMessage()); - } + public ResultSet executeQuery() throws SQLException { + if (this.isInsert){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "The query SQL must be prepared."); } - // bind - List collect = column.keySet().stream().sorted().map(i -> { - Column col = this.column.get(i); - return new ColumnInfo(i, col.data, col.type); - }).collect(Collectors.toList()); - byte[] rawBlock; - try { - rawBlock = SerializeBlock.getRawBlock(collect, precision); - } catch (IOException e) { - throw new SQLException("data serialize error!", e); + + if (!tag.isEmpty() || !colListQueue.isEmpty()){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "The query SQL only support bind columns."); } - StmtResp bindResp = (StmtResp) transport.send(Action.BIND.getAction(), - reqId, stmtId, BindType.BIND.get(), rawBlock); - if (Code.SUCCESS.getCode() != bindResp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(bindResp.getCode()) + "):" + bindResp.getMessage()); + + // only support jdbc standard bind api + if (colOrderedMap.isEmpty()){ + return executeQuery(this.rawSql); } - // add batch - Request batch = RequestFactory.generateBatch(stmtId, reqId); - Response send = transport.send(batch); - StmtResp batchResp = (StmtResp) send; - if (Code.SUCCESS.getCode() != batchResp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(batchResp.getCode()) + "):" + batchResp.getMessage()); + + onlyBindCol(); + if (!isTableInfoEmpty()){ + tableInfoList.add(tableInfo); } - this.clearParameters(); - // send - Request request = RequestFactory.generateExec(stmtId, reqId); - ExecResp resp = (ExecResp) transport.send(request); + + this.executeBatchImpl(); + + Request request = RequestFactory.generateUseResult(stmtId, reqId); + ResultResp resp = (ResultResp) transport.send(request); if (Code.SUCCESS.getCode() != resp.getCode()) { - if (TIMESTAMP_DATA_OUT_OF_RANGE == resp.getCode()){ - updatePrecision(insertDbName); - } - throw new SQLException("(0x" + Integer.toHexString(resp.getCode()) + "):" + resp.getMessage(), "P0001", resp.getCode()); + throw new SQLException("(0x" + Integer.toHexString(resp.getCode()) + "):" + resp.getMessage()); } - return resp.getAffected(); + this.resultSet = new BlockResultSet(this, this.transport, resp, this.database, this.zoneId); + this.affectedRows = -1; + return this.resultSet; } - // set sub-table name - public void setTableName(String name) throws SQLException { - Request request = RequestFactory.generateSetTableName(stmtId, reqId, name); - StmtResp resp = (StmtResp) transport.send(request); - if (Code.SUCCESS.getCode() != resp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(resp.getCode()) + "):" + resp.getMessage()); + @Override + public int executeUpdate() throws SQLException { + if (!this.isInsert){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE, "The insert SQL must be prepared."); + } + + if (fields.isEmpty()){ + return this.executeUpdate(this.rawSql); } + + if (colOrderedMap.size() == fields.size()){ + // bind all + bindAllColWithStdApi(); + } else{ + // mixed standard api and extended api, only support one table + onlyBindTag(); + onlyBindCol(); + } + + if (isTableInfoEmpty()){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_WITH_EXECUTEUPDATE, "no data to be bind"); + } + + tableInfoList.add(tableInfo); + return executeBatchImpl(); } public void setTagSqlTypeNull(int index, int type) throws SQLException { switch (type) { case Types.BOOLEAN: - tag.put(index, new Column(null, TSDB_DATA_TYPE_BOOL, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_BOOL)); break; case Types.TINYINT: - tag.put(index, new Column(null, TSDB_DATA_TYPE_TINYINT, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_TINYINT)); break; case Types.SMALLINT: - tag.put(index, new Column(null, TSDB_DATA_TYPE_SMALLINT, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_SMALLINT)); break; case Types.INTEGER: - tag.put(index, new Column(null, TSDB_DATA_TYPE_INT, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_INT)); break; case Types.BIGINT: - tag.put(index, new Column(null, TSDB_DATA_TYPE_BIGINT, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_BIGINT)); break; case Types.FLOAT: - tag.put(index, new Column(null, TSDB_DATA_TYPE_FLOAT, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_FLOAT)); break; case Types.DOUBLE: - tag.put(index, new Column(null, TSDB_DATA_TYPE_DOUBLE, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_DOUBLE)); break; case Types.TIMESTAMP: - tag.put(index, new Column(null, TSDB_DATA_TYPE_TIMESTAMP, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_TIMESTAMP)); break; case Types.BINARY: case Types.VARCHAR: - tag.put(index, new Column(null, TSDB_DATA_TYPE_BINARY, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_BINARY)); break; case Types.VARBINARY: - tag.put(index, new Column(null, TSDB_DATA_TYPE_VARBINARY, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_VARBINARY)); break; case Types.NCHAR: - tag.put(index, new Column(null, TSDB_DATA_TYPE_NCHAR, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_NCHAR)); break; // json case Types.OTHER: - tag.put(index, new Column(null, TSDB_DATA_TYPE_JSON, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_JSON)); break; default: throw new SQLException("unsupported type: " + type); } } + @Override public void setTagNull(int index, int type) throws SQLException { switch (type) { case TSDB_DATA_TYPE_BOOL: - tag.put(index, new Column(null, TSDB_DATA_TYPE_BOOL, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_BOOL)); break; case TSDB_DATA_TYPE_TINYINT: - tag.put(index, new Column(null, TSDB_DATA_TYPE_TINYINT, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_TINYINT)); break; case TSDB_DATA_TYPE_SMALLINT: - tag.put(index, new Column(null, TSDB_DATA_TYPE_SMALLINT, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_SMALLINT)); break; case TSDB_DATA_TYPE_INT: - tag.put(index, new Column(null, TSDB_DATA_TYPE_INT, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_INT)); break; case TSDB_DATA_TYPE_BIGINT: - tag.put(index, new Column(null, TSDB_DATA_TYPE_BIGINT, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_BIGINT)); break; case TSDB_DATA_TYPE_FLOAT: - tag.put(index, new Column(null, TSDB_DATA_TYPE_FLOAT, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_FLOAT)); break; case TSDB_DATA_TYPE_DOUBLE: - tag.put(index, new Column(null, TSDB_DATA_TYPE_DOUBLE, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_DOUBLE)); break; case TSDB_DATA_TYPE_TIMESTAMP: - tag.put(index, new Column(null, TSDB_DATA_TYPE_TIMESTAMP, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_TIMESTAMP)); break; case TSDB_DATA_TYPE_BINARY: - tag.put(index, new Column(null, TSDB_DATA_TYPE_BINARY, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_BINARY)); break; case TSDB_DATA_TYPE_VARBINARY: - tag.put(index, new Column(null, TSDB_DATA_TYPE_VARBINARY, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_VARBINARY)); break; case TSDB_DATA_TYPE_GEOMETRY: - tag.put(index, new Column(null, TSDB_DATA_TYPE_GEOMETRY, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_GEOMETRY)); break; case TSDB_DATA_TYPE_NCHAR: - tag.put(index, new Column(null, TSDB_DATA_TYPE_NCHAR, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_NCHAR)); break; // json case TSDB_DATA_TYPE_JSON: - tag.put(index, new Column(null, TSDB_DATA_TYPE_JSON, index)); + tag.add(new ColumnInfo(index, nullTag, TSDB_DATA_TYPE_JSON)); break; default: throw new SQLException("unsupported type: " + type); } } + @Override public void setTagBoolean(int index, boolean value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_BOOL, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(value), TSDB_DATA_TYPE_BOOL)); } + @Override public void setTagByte(int index, byte value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_TINYINT, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(value), TSDB_DATA_TYPE_TINYINT)); } + @Override public void setTagShort(int index, short value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_SMALLINT, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(value), TSDB_DATA_TYPE_SMALLINT)); } + @Override public void setTagInt(int index, int value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_INT, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(value), TSDB_DATA_TYPE_INT)); } + @Override public void setTagLong(int index, long value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_BIGINT, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(value), TSDB_DATA_TYPE_BIGINT)); } + @Override public void setTagFloat(int index, float value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_FLOAT, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(value), TSDB_DATA_TYPE_FLOAT)); } + @Override public void setTagDouble(int index, double value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_DOUBLE, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(value), TSDB_DATA_TYPE_DOUBLE)); } + @Override public void setTagTimestamp(int index, long value) { - tag.put(index, new Column(new Timestamp(value), TSDB_DATA_TYPE_TIMESTAMP, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(new Timestamp(value)), TSDB_DATA_TYPE_TIMESTAMP)); } + @Override public void setTagTimestamp(int index, Timestamp value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_TIMESTAMP, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(DateTimeUtils.toInstant(value, this.zoneId)), TSDB_DATA_TYPE_TIMESTAMP)); } + @Override public void setTagString(int index, String value) { byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - tag.put(index, new Column(bytes, TSDB_DATA_TYPE_BINARY, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(bytes), TSDB_DATA_TYPE_BINARY)); } + @Override public void setTagVarbinary(int index, byte[] value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_VARBINARY, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(value), TSDB_DATA_TYPE_VARBINARY)); } + @Override public void setTagGeometry(int index, byte[] value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_GEOMETRY, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(value), TSDB_DATA_TYPE_GEOMETRY)); } + @Override public void setTagNString(int index, String value) { - tag.put(index, new Column(value, TSDB_DATA_TYPE_NCHAR, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(value), TSDB_DATA_TYPE_NCHAR)); } + @Override public void setTagJson(int index, String value) { byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - tag.put(index, new Column(bytes, TSDB_DATA_TYPE_JSON, index)); + tag.add(new ColumnInfo(index, Collections.singletonList(bytes), TSDB_DATA_TYPE_JSON)); } @Override public void setNull(int parameterIndex, int sqlType) throws SQLException { switch (sqlType) { case Types.BOOLEAN: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_BOOL, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_BOOL, parameterIndex)); break; case Types.TINYINT: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_TINYINT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_TINYINT, parameterIndex)); break; case Types.SMALLINT: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_SMALLINT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_SMALLINT, parameterIndex)); break; case Types.INTEGER: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_INT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_INT, parameterIndex)); break; case Types.BIGINT: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_BIGINT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_BIGINT, parameterIndex)); break; case Types.FLOAT: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_FLOAT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_FLOAT, parameterIndex)); break; case Types.DOUBLE: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_DOUBLE, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_DOUBLE, parameterIndex)); break; case Types.TIMESTAMP: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); break; case Types.BINARY: case Types.VARCHAR: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_BINARY, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_BINARY, parameterIndex)); break; case Types.VARBINARY: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_VARBINARY, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_VARBINARY, parameterIndex)); break; case Types.NCHAR: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_NCHAR, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_NCHAR, parameterIndex)); break; // json case Types.OTHER: - column.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_JSON, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(null, TSDB_DATA_TYPE_JSON, parameterIndex)); break; default: throw new SQLException("unsupported type: " + sqlType); @@ -467,37 +406,37 @@ public void setNull(int parameterIndex, int sqlType) throws SQLException { @Override public void setBoolean(int parameterIndex, boolean x) throws SQLException { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BOOL, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BOOL, parameterIndex)); } @Override public void setByte(int parameterIndex, byte x) throws SQLException { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_TINYINT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_TINYINT, parameterIndex)); } @Override public void setShort(int parameterIndex, short x) throws SQLException { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_SMALLINT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_SMALLINT, parameterIndex)); } @Override public void setInt(int parameterIndex, int x) throws SQLException { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_INT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_INT, parameterIndex)); } @Override public void setLong(int parameterIndex, long x) throws SQLException { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BIGINT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BIGINT, parameterIndex)); } @Override public void setFloat(int parameterIndex, float x) throws SQLException { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_FLOAT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_FLOAT, parameterIndex)); } @Override public void setDouble(int parameterIndex, double x) throws SQLException { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_DOUBLE, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_DOUBLE, parameterIndex)); } @Override @@ -518,7 +457,7 @@ public void setString(int parameterIndex, String x) throws SQLException { @Override public void setBytes(int parameterIndex, byte[] x) throws SQLException { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BINARY, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BINARY, parameterIndex)); } public void setVarbinary(int parameterIndex, byte[] x) throws SQLException { @@ -527,11 +466,11 @@ public void setVarbinary(int parameterIndex, byte[] x) throws SQLException { setNull(parameterIndex, Types.VARBINARY); return; } - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_VARBINARY, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_VARBINARY, parameterIndex)); } public void setGeometry(int parameterIndex, byte[] x) throws SQLException { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_GEOMETRY, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_GEOMETRY, parameterIndex)); } @Override @@ -556,16 +495,7 @@ public void setTime(int parameterIndex, Time x) throws SQLException { @Override public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { - if (this.zoneId == null) { - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); - } else { - Instant instant = x.toInstant(); - ZonedDateTime zonedDateTime = instant.atZone(this.zoneId); - ZonedDateTime utcDateTime = zonedDateTime.withZoneSameInstant(ZoneId.systemDefault()); - Timestamp utcTimestamp = Timestamp.valueOf(utcDateTime.toLocalDateTime()); - - column.put(parameterIndex, new Column(utcTimestamp, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); - } + colOrderedMap.put(parameterIndex, new Column(DateTimeUtils.toInstant(x, this.zoneId), TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); } @Override @@ -585,52 +515,55 @@ public void setBinaryStream(int parameterIndex, InputStream x, int length) throw @Override public void clearParameters() throws SQLException { - column.clear(); + colOrderedMap.clear(); tag.clear(); - data.clear(); + colListQueue.clear(); + + tableInfo = TableInfo.getEmptyTableInfo(); + tableInfoList.clear(); } @Override public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { switch (targetSqlType) { case Types.BOOLEAN: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BOOL, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BOOL, parameterIndex)); break; case Types.TINYINT: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_TINYINT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_TINYINT, parameterIndex)); break; case Types.SMALLINT: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_SMALLINT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_SMALLINT, parameterIndex)); break; case Types.INTEGER: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_INT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_INT, parameterIndex)); break; case Types.BIGINT: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BIGINT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BIGINT, parameterIndex)); break; case Types.FLOAT: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_FLOAT, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_FLOAT, parameterIndex)); break; case Types.DOUBLE: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_DOUBLE, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_DOUBLE, parameterIndex)); break; case Types.TIMESTAMP: - Timestamp timestamp = DateTimeUtils.toUTC((Timestamp) x, zoneId); - column.put(parameterIndex, new Column(timestamp, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + Instant instant = DateTimeUtils.toInstant((Timestamp) x, zoneId); + colOrderedMap.put(parameterIndex, new Column(instant, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); break; case Types.BINARY: case Types.VARCHAR: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BINARY, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_BINARY, parameterIndex)); break; case Types.VARBINARY: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_VARBINARY, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_VARBINARY, parameterIndex)); break; case Types.NCHAR: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_NCHAR, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_NCHAR, parameterIndex)); break; // json case Types.OTHER: - column.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_JSON, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(x, TSDB_DATA_TYPE_JSON, parameterIndex)); break; default: throw new SQLException("unsupported type: " + targetSqlType); @@ -659,6 +592,8 @@ public void setObject(int parameterIndex, Object x) throws SQLException { setBytes(parameterIndex, (byte[]) x); } else if (x instanceof Double) { setDouble(parameterIndex, (Double) x); + } else if (x instanceof Date) { + setDate(parameterIndex, (Date) x); } else if (x instanceof Time) { setTime(parameterIndex, (Time) x); } else if (x instanceof Timestamp) { @@ -669,125 +604,134 @@ public void setObject(int parameterIndex, Object x) throws SQLException { } else { ZonedDateTime zonedDateTime = ((LocalDateTime) x).atZone(zoneId); Instant instant = zonedDateTime.toInstant(); - Timestamp timestamp = Timestamp.from(instant); - setTimestamp(parameterIndex, timestamp); + colOrderedMap.put(parameterIndex, new Column( instant, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); } } else if (x instanceof Instant) { - column.put(parameterIndex, new Column(Timestamp.from((Instant) x), TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column( x, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); } else if (x instanceof ZonedDateTime) { ZonedDateTime zonedDateTime = (ZonedDateTime) x; Instant instant = zonedDateTime.toInstant(); - column.put(parameterIndex, new Column(Timestamp.from(instant), TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(instant, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); } else if (x instanceof OffsetDateTime) { OffsetDateTime offsetDateTime = (OffsetDateTime) x; Instant instant = offsetDateTime.toInstant(); - column.put(parameterIndex, new Column(Timestamp.from(instant), TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(instant, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); } else { throw new SQLException("Unsupported data type: " + x.getClass().getName()); } } - @Override - public boolean execute() throws SQLException { - if (isClosed()) - throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED); - - List list = new ArrayList<>(); - if (!tag.isEmpty()) { - tag.keySet().stream().sorted().forEach(i -> { - Column col = this.tag.get(i); - list.add(col.data); - }); - } - if (!column.isEmpty()) { - column.keySet().stream().sorted().forEach(i -> { - Column col = this.column.get(i); - list.add(col.data); - }); + private void bindAllToTableInfo(){ + for (int index = 0; index < fields.size(); index++) { + if (fields.get(index).getBindType() == FeildBindType.TAOS_FIELD_TBNAME.getValue()) { + if (colOrderedMap.get(index + 1).data instanceof byte[]){ + tableInfo.setTableName(new String((byte[])colOrderedMap.get(index + 1).data, StandardCharsets.UTF_8)); + } + if (colOrderedMap.get(index + 1).data instanceof String){ + tableInfo.setTableName((String) colOrderedMap.get(index + 1).data); + } + } else if (fields.get(index).getBindType() == FeildBindType.TAOS_FIELD_TAG.getValue()) { + LinkedList list = new LinkedList<>(); + list.add(colOrderedMap.get(index + 1).data); + tableInfo.getTagInfo().add(new ColumnInfo(index + 1, list, fields.get(index).getFieldType())); + } else if (fields.get(index).getBindType() == FeildBindType.TAOS_FIELD_COL.getValue()) { + LinkedList list = new LinkedList<>(); + list.add(colOrderedMap.get(index + 1).data); + tableInfo.getDataList().add(new ColumnInfo(index + 1, list, fields.get(index).getFieldType())); + } } - Object[] parameters = list.toArray(new Object[0]); - this.clearParameters(); - final String sql = Utils.getNativeSql(this.rawSql, parameters); - return execute(sql); } - @Override - public void addBatch() throws SQLException { - List collect = column.keySet().stream().sorted().map(column::get).collect(Collectors.toList()); - if (data.isEmpty()) { - for (Column col : collect) { - data.add(new ColumnInfo(col.index, col.data, col.type)); - } - } else { - if (collect.size() != data.size()) { - throw new SQLException("batch add column size not match, expected: " + data.size() + ", actual: " + collect.size()); - } - for (int i = 0; i < collect.size(); i++) { - Column col = collect.get(i); - ColumnInfo columnInfo = data.get(i); - if (columnInfo.getIndex() != col.index) { - throw new SQLException("batch add column index not match, expected: " + columnInfo.getIndex() + ", actual: " + col.index); - } - if (columnInfo.getType() != col.type) { - throw new SQLException("batch add column type not match, expected type: " + columnInfo.getType() + ", actual type: " + col.type); + private void bindColToTableInfo(){ + for (ColumnInfo columnInfo: tableInfo.getDataList()){ + columnInfo.add(colOrderedMap.get(columnInfo.getIndex()).data); + } +// int j = 0; +// for (int index = 0; index < fields.size(); index++) { +// if (fields.get(index).getBindType() == FeildBindType.TAOS_FIELD_COL.getValue()) { +// tableInfo.getDataList().get(j).getDataList().add();add(new ColumnInfo(index, Collections.singletonList(colOrderedMap.get(index + 1).data), fields.get(index).getFieldType())); +// } +// } + } + + private void bindAllColWithStdApi() { + if (isTableInfoEmpty()) { + // first time, bind all + bindAllToTableInfo(); + } else { + if (toBeBindTableNameIndex >= 0) { + Object tbname = colOrderedMap.get(toBeBindTableNameIndex + 1).data; + if ((tbname instanceof String && tableInfo.getTableName().equals(tbname)) + || (tbname instanceof byte[] && tableInfo.getTableName().equals(new String((byte[]) tbname, StandardCharsets.UTF_8)))) { + // same table, only bind col + bindColToTableInfo(); + } else { + // different table, flush tableInfo and create a new one + tableInfoList.add(tableInfo); + tableInfo = TableInfo.getEmptyTableInfo(); + bindAllToTableInfo(); } - columnInfo.add(col.data); + } else { + // must same table + bindColToTableInfo(); } } } - @Override - public int[] executeBatch() throws SQLException { - if (column.isEmpty()) - throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_BATCH_IS_EMPTY); - - //set tag - if (!tag.isEmpty()) { - List collect = tag.keySet().stream().sorted().map(i -> { - Column col = this.tag.get(i); - return new ColumnInfo(i, col.data, col.type); - }).collect(Collectors.toList()); - byte[] tagBlock; - try { - tagBlock = SerializeBlock.getRawBlock(collect, precision); - } catch (IOException e) { - throw new SQLException("data serialize error!", e); + private void onlyBindCol() { + if (tableInfo.getDataList().isEmpty()){ + for (Map.Entry entry : colOrderedMap.entrySet()) { + Column col = entry.getValue(); + List list = new ArrayList<>(); + list.add(col.data); + tableInfo.getDataList().add(new ColumnInfo(entry.getKey(), list, col.type)); } - StmtResp bindResp = (StmtResp) transport.send(Action.SET_TAGS.getAction(), - reqId, stmtId, BindType.TAG.get(), tagBlock); - if (Code.SUCCESS.getCode() != bindResp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(bindResp.getCode()) + "):" + bindResp.getMessage()); + } else { + for (Map.Entry entry : colOrderedMap.entrySet()) { + Column col = entry.getValue(); + tableInfo.getDataList().get(col.index - 1).add(col.data); } } - // bind - byte[] rawBlock; - try { - rawBlock = SerializeBlock.getRawBlock(data, precision); - } catch (IOException e) { - throw new SQLException("data serialize error!", e); + } + private void onlyBindTag() { + if (!tableInfo.getTagInfo().isEmpty()){ + return; } - StmtResp bindResp = (StmtResp) transport.send(Action.BIND.getAction(), - reqId, stmtId, BindType.BIND.get(), rawBlock); - if (Code.SUCCESS.getCode() != bindResp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(bindResp.getCode()) + "):" + bindResp.getMessage()); + + while (!tag.isEmpty()) { + tableInfo.getTagInfo().add(tag.poll()); } - // add batch - Request batch = RequestFactory.generateBatch(stmtId, reqId); - Response send = transport.send(batch); - StmtResp batchResp = (StmtResp) send; - if (Code.SUCCESS.getCode() != batchResp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(batchResp.getCode()) + "):" + batchResp.getMessage()); + } + + @Override + // Only support batch insert + public void addBatch() throws SQLException { + if (colOrderedMap.size() == fields.size()){ + // jdbc standard bind api + bindAllColWithStdApi(); + return; } - this.clearParameters(); - // send - Request request = RequestFactory.generateExec(stmtId, reqId); - ExecResp resp = (ExecResp) transport.send(request); - if (Code.SUCCESS.getCode() != resp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(resp.getCode()) + "):" + resp.getMessage()); + // mixed standard api and extended api, only support one table + onlyBindTag(); + onlyBindCol(); + } + + private boolean isTableInfoEmpty(){ + return StringUtils.isEmpty(tableInfo.getTableName()) + && tableInfo.getTagInfo().isEmpty() + && tableInfo.getDataList().isEmpty(); + } + @Override + public int[] executeBatch() throws SQLException { + + if (!isTableInfoEmpty()){ + tableInfoList.add(tableInfo); } - int[] ints = new int[resp.getAffected()]; + + int affected = executeBatchImpl(); + int[] ints = new int[affected]; for (int i = 0, len = ints.length; i < len; i++) ints[i] = SUCCESS_NO_INFO; return ints; @@ -798,6 +742,10 @@ public void close() throws SQLException { super.close(); Request close = RequestFactory.generateClose(stmtId, reqId); transport.sendWithoutResponse(close); + Stmt2Resp resp = (Stmt2Resp) transport.send(close); + if (Code.SUCCESS.getCode() != resp.getCode()) { + throw new SQLException("(0x" + Integer.toHexString(resp.getCode()) + "):" + resp.getMessage()); + } } @Override @@ -812,15 +760,17 @@ public ParameterMetaData getParameterMetaData() throws SQLException { if (isClosed()) throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED); List list = new ArrayList<>(); - if (!tag.isEmpty()) { - tag.keySet().stream().sorted().forEach(i -> { - Column col = this.tag.get(i); - list.add(col.data); - }); + while (!tag.isEmpty()){ + ColumnInfo columnInfo = tag.poll(); + if (columnInfo.getDataList().size() != 1){ + throw new SQLException("tag size is not equal 1"); + } + + list.add(columnInfo.getDataList().get(0)); } - if (!column.isEmpty()) { - column.keySet().stream().sorted().forEach(i -> { - Column col = this.column.get(i); + if (!colOrderedMap.isEmpty()) { + colOrderedMap.keySet().stream().sorted().forEach(i -> { + Column col = this.colOrderedMap.get(i); list.add(col.data); }); } @@ -869,8 +819,8 @@ public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLExceptio @Override public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { - Timestamp timestamp = DateTimeUtils.toUTC(x, cal); - column.put(parameterIndex, new Column(timestamp, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); + Instant instant = DateTimeUtils.toInstant(x, cal); + colOrderedMap.put(parameterIndex, new Column(instant, TSDB_DATA_TYPE_TIMESTAMP, parameterIndex)); } @Override @@ -890,7 +840,7 @@ public void setRowId(int parameterIndex, RowId x) throws SQLException { @Override public void setNString(int parameterIndex, String value) throws SQLException { - column.put(parameterIndex, new Column(value, TSDB_DATA_TYPE_NCHAR, parameterIndex)); + colOrderedMap.put(parameterIndex, new Column(value, TSDB_DATA_TYPE_NCHAR, parameterIndex)); } @Override @@ -991,28 +941,27 @@ public Column(Object data, int type, int index) { } } + @Override public void setInt(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_INT, Integer.BYTES); } + @Override public void setFloat(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_FLOAT, Float.BYTES); } + @Override public void setTimestamp(int columnIndex, List list) throws SQLException { - List collect = list.stream().map(x -> { - if (x == null) { - return null; - } - return new Timestamp(x); - }).collect(Collectors.toList()); - setValueImpl(columnIndex, collect, TSDBConstants.TSDB_DATA_TYPE_TIMESTAMP, Long.BYTES); + setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_TIMESTAMP, Long.BYTES); } + @Override public void setLong(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_BIGINT, Long.BYTES); } + @Override public void setDouble(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_DOUBLE, Double.BYTES); } @@ -1021,14 +970,17 @@ public void setBoolean(int columnIndex, List list) throws SQLException setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_BOOL, Byte.BYTES); } + @Override public void setByte(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_TINYINT, Byte.BYTES); } + @Override public void setShort(int columnIndex, List list) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_SMALLINT, Short.BYTES); } + @Override public void setString(int columnIndex, List list, int size) throws SQLException { List collect = list.stream().map(x -> { if (x == null) { @@ -1038,81 +990,93 @@ public void setString(int columnIndex, List list, int size) throws SQLEx }).collect(Collectors.toList()); setValueImpl(columnIndex, collect, TSDBConstants.TSDB_DATA_TYPE_BINARY, size); } - + @Override public void setVarbinary(int columnIndex, List list, int size) throws SQLException { setValueImpl(columnIndex, list, TSDB_DATA_TYPE_VARBINARY, size); } + @Override public void setGeometry(int columnIndex, List list, int size) throws SQLException { setValueImpl(columnIndex, list, TSDB_DATA_TYPE_GEOMETRY, size); } // note: expand the required space for each NChar character + @Override public void setNString(int columnIndex, List list, int size) throws SQLException { setValueImpl(columnIndex, list, TSDBConstants.TSDB_DATA_TYPE_NCHAR, size * Integer.BYTES); } public void setValueImpl(int columnIndex, List list, int type, int bytes) throws SQLException { - List listObject = list.stream() - .map(Object.class::cast) - .collect(Collectors.toList()); - ColumnInfo p = new ColumnInfo(columnIndex, listObject, type, null); - queue.add(p); + List listObject = new ArrayList<>(list); + ColumnInfo p = new ColumnInfo(columnIndex, listObject, type); + colListQueue.add(p); } + @Override public void columnDataAddBatch() throws SQLException { - while (!queue.isEmpty()) { - data.add(queue.poll()); + if (!colOrderedMap.isEmpty()){ + throw new SQLException("column data is not empty"); + } + + while (!tag.isEmpty()){ + tableInfo.getTagInfo().add(tag.poll()); + } + while (!colListQueue.isEmpty()) { + tableInfo.getDataList().add(colListQueue.poll()); } + tableInfoList.add(tableInfo); + tableInfo = TableInfo.getEmptyTableInfo(); } - public void columnDataExecuteBatch() throws SQLException { - //set tag - if (!tag.isEmpty()) { - List collect = tag.keySet().stream().sorted().map(i -> { - Column col = this.tag.get(i); - return new ColumnInfo(i, col.data, col.type); - }).collect(Collectors.toList()); - byte[] tagBlock; - try { - tagBlock = SerializeBlock.getRawBlock(collect, precision); - } catch (IOException e) { - throw new SQLException("data serialize error!", e); - } - StmtResp bindResp = (StmtResp) transport.send(Action.SET_TAGS.getAction(), - reqId, stmtId, BindType.TAG.get(), tagBlock); - if (Code.SUCCESS.getCode() != bindResp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(bindResp.getCode()) + "):" + bindResp.getMessage()); - } + + private int executeBatchImpl() throws SQLException { + if (tableInfoList.isEmpty()) { + throw new SQLException("batch data is empty"); } - // bind + byte[] rawBlock; try { - rawBlock = SerializeBlock.getRawBlock(data, precision); + rawBlock = SerializeBlock.getStmt2BindBlock(reqId, stmtId, tableInfoList, toBeBindTableNameIndex, toBeBindTagCount, toBeBindColCount, precision); } catch (IOException e) { throw new SQLException("data serialize error!", e); + } finally { + this.clearParameters(); } - StmtResp bindResp = (StmtResp) transport.send(Action.BIND.getAction(), - reqId, stmtId, BindType.BIND.get(), rawBlock); + + // bind + Stmt2Resp bindResp = (Stmt2Resp) transport.send(Action.STMT2_BIND.getAction(), + reqId, rawBlock); if (Code.SUCCESS.getCode() != bindResp.getCode()) { throw new SQLException("(0x" + Integer.toHexString(bindResp.getCode()) + "):" + bindResp.getMessage()); } - // add batch - Request batch = RequestFactory.generateBatch(stmtId, reqId); - Response send = transport.send(batch); - StmtResp batchResp = (StmtResp) send; - if (Code.SUCCESS.getCode() != batchResp.getCode()) { - throw new SQLException("(0x" + Integer.toHexString(batchResp.getCode()) + "):" + batchResp.getMessage()); - } - this.clearParameters(); - // send + // execute Request request = RequestFactory.generateExec(stmtId, reqId); - ExecResp resp = (ExecResp) transport.send(request); + Stmt2ExecResp resp = (Stmt2ExecResp) transport.send(request); if (Code.SUCCESS.getCode() != resp.getCode()) { throw new SQLException("(0x" + Integer.toHexString(resp.getCode()) + "):" + resp.getMessage()); } + + this.affectedRows = resp.getAffected(); + return this.affectedRows; + } + @Override + public void columnDataExecuteBatch() throws SQLException { + executeBatchImpl(); } public void columnDataCloseBatch() throws SQLException { this.close(); } + + @Override + public void setTableName(String name) throws SQLException { + this.tableInfo.setTableName(name); + } + + private void ensureTagCapacity(int index) { + if (this.tableInfo.getTagInfo().size() < index + 1) { + int delta = index + 1 - this.tableInfo.getTagInfo().size(); + this.tableInfo.getTagInfo().addAll(Collections.nCopies(delta, null)); + } + } + } diff --git a/src/main/java/com/taosdata/jdbc/ws/Transport.java b/src/main/java/com/taosdata/jdbc/ws/Transport.java index a2efce29..dbdecbc7 100644 --- a/src/main/java/com/taosdata/jdbc/ws/Transport.java +++ b/src/main/java/com/taosdata/jdbc/ws/Transport.java @@ -196,7 +196,7 @@ public Response send(String action, long reqId, long resultId, long type, byte[] throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_CONNECTION_CLOSED, "Websocket Not Connected Exception"); } - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(24 + rawData.length + rawData2.length); try { buffer.write(SerializeBlock.longToBytes(reqId)); buffer.write(SerializeBlock.longToBytes(resultId)); @@ -239,6 +239,46 @@ public Response send(String action, long reqId, long resultId, long type, byte[] } return response; } + + public Response send(String action, long reqId, byte[] buffer) throws SQLException { + if (isClosed()){ + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_CONNECTION_CLOSED, "Websocket Not Connected Exception"); + } + + + Response response; + CompletableFuture completableFuture = new CompletableFuture<>(); + try { + inFlightRequest.put(new FutureResponse(action, reqId, completableFuture)); + } catch (InterruptedException | TimeoutException e) { + throw new SQLException(e); + } + + try { + clientArr.get(currentNodeIndex).send(buffer); + } catch (WebsocketNotConnectedException e) { + tmqRethrowConnectionCloseException(); + reconnect(); + try { + clientArr.get(currentNodeIndex).send(buffer); + }catch (Exception ex){ + inFlightRequest.remove(action, reqId); + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESTFul_Client_IOException, e.getMessage()); + } + } + + String reqString = "action:" + action + ", reqId:" + reqId; + CompletableFuture responseFuture = CompletableFutureTimeout.orTimeout(completableFuture, timeout, TimeUnit.MILLISECONDS, reqString); + try { + response = responseFuture.get(); + handleErrInMasterSlaveMode(response); + } catch (InterruptedException | ExecutionException e) { + inFlightRequest.remove(action, reqId); + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_QUERY_TIMEOUT, e.getMessage()); + } + return response; + } + private void handleErrInMasterSlaveMode(Response response) throws InterruptedException{ if (clientArr.size() > 1 && response instanceof CommonResp){ CommonResp commonResp = (CommonResp) response; diff --git a/src/main/java/com/taosdata/jdbc/ws/WSStatement.java b/src/main/java/com/taosdata/jdbc/ws/WSStatement.java index 0ac62d58..131a622e 100644 --- a/src/main/java/com/taosdata/jdbc/ws/WSStatement.java +++ b/src/main/java/com/taosdata/jdbc/ws/WSStatement.java @@ -16,11 +16,11 @@ public class WSStatement extends AbstractStatement { protected Transport transport; - private String database; + protected String database; private final AbstractConnection connection; private boolean closed; - private ResultSet resultSet; + protected ResultSet resultSet; private int queryTimeout = 0; diff --git a/src/main/java/com/taosdata/jdbc/ws/entity/Action.java b/src/main/java/com/taosdata/jdbc/ws/entity/Action.java index aadf82db..72d96381 100644 --- a/src/main/java/com/taosdata/jdbc/ws/entity/Action.java +++ b/src/main/java/com/taosdata/jdbc/ws/entity/Action.java @@ -1,8 +1,9 @@ package com.taosdata.jdbc.ws.entity; -import com.taosdata.jdbc.ws.stmt.entity.ExecResp; -import com.taosdata.jdbc.ws.stmt.entity.GetColFieldsResp; -import com.taosdata.jdbc.ws.stmt.entity.StmtResp; +import com.taosdata.jdbc.ws.stmt2.entity.ResultResp; +import com.taosdata.jdbc.ws.stmt2.entity.Stmt2ExecResp; +import com.taosdata.jdbc.ws.stmt2.entity.Stmt2PrepareResp; +import com.taosdata.jdbc.ws.stmt2.entity.Stmt2Resp; import com.taosdata.jdbc.ws.tmq.entity.FetchRawBlockResp; import java.util.HashMap; @@ -22,18 +23,14 @@ public enum Action { // free_result's class is meaningless FREE_RESULT("free_result", Response.class), - // stmt - INIT("init", StmtResp.class), - PREPARE("prepare", StmtResp.class), - SET_TABLE_NAME("set_table_name", StmtResp.class), - SET_TAGS("set_tags", StmtResp.class), - BIND("bind", StmtResp.class), - ADD_BATCH("add_batch", StmtResp.class), - EXEC("exec", ExecResp.class), + // stmt2 + STMT2_INIT("stmt2_init", Stmt2Resp.class), + STMT2_PREPARE("stmt2_prepare", Stmt2PrepareResp.class), + STMT2_BIND("stmt2_bind", Stmt2Resp.class), + STMT2_EXEC("stmt2_exec", Stmt2ExecResp.class), // response means nothing - GET_COL_FIELDS("get_col_fields", GetColFieldsResp.class), - - CLOSE("close", StmtResp.class), + STMT2_CLOSE("stmt2_close", Stmt2Resp.class), + STMT2_USE_RESULT("stmt2_result", ResultResp.class), //schemaless INSERT("insert", CommonResp.class), diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/BindReq.java b/src/main/java/com/taosdata/jdbc/ws/stmt/entity/BindReq.java deleted file mode 100644 index 0140a318..00000000 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/BindReq.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.taosdata.jdbc.ws.stmt.entity; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.taosdata.jdbc.utils.UInt64Serializer; -import com.taosdata.jdbc.ws.entity.Payload; - -public class BindReq extends Payload { - @JsonProperty("stmt_id") - @JsonSerialize(using = UInt64Serializer.class) - private long stmtId; - private Object[] columns; - - public long getStmtId() { - return stmtId; - } - - public void setStmtId(long stmtId) { - this.stmtId = stmtId; - } - - public Object[] getColumns() { - return columns; - } - - public void setColumns(Object[] columns) { - this.columns = columns; - } -} diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/GetColFieldsReq.java b/src/main/java/com/taosdata/jdbc/ws/stmt/entity/GetColFieldsReq.java deleted file mode 100644 index cf9d589f..00000000 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/GetColFieldsReq.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.taosdata.jdbc.ws.stmt.entity; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.taosdata.jdbc.utils.UInt64Serializer; -import com.taosdata.jdbc.ws.entity.Payload; - -public class GetColFieldsReq extends Payload { - @JsonProperty("stmt_id") - @JsonSerialize(using = UInt64Serializer.class) - - private long stmtId; - - public long getStmtId() { - return stmtId; - } - - public void setStmtId(long stmtId) { - this.stmtId = stmtId; - } -} diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/InitReq.java b/src/main/java/com/taosdata/jdbc/ws/stmt/entity/InitReq.java deleted file mode 100644 index 2e5bba56..00000000 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/InitReq.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.taosdata.jdbc.ws.stmt.entity; - -import com.taosdata.jdbc.ws.entity.Payload; - -public class InitReq extends Payload { -} diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/RequestFactory.java b/src/main/java/com/taosdata/jdbc/ws/stmt/entity/RequestFactory.java deleted file mode 100644 index 5ab3dd44..00000000 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/RequestFactory.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.taosdata.jdbc.ws.stmt.entity; - - -import com.taosdata.jdbc.ws.entity.Action; -import com.taosdata.jdbc.ws.entity.Request; - -/** - * generate id for request - */ -public class RequestFactory { - - private RequestFactory() { - } - - public static Request generateInit(long reqId) { - InitReq initReq = new InitReq(); - initReq.setReqId(reqId); - return new Request(Action.INIT.getAction(), initReq); - } - - public static Request generatePrepare(long stmtId, long reqId, String sql) { - PrepareReq prepareReq = new PrepareReq(); - prepareReq.setReqId(reqId); - prepareReq.setStmtId(stmtId); - prepareReq.setSql(sql); - return new Request(Action.PREPARE.getAction(), prepareReq); - } - - public static Request generateSetTableName(long stmtId, long reqId, String tableName) { - SetTableNameReq req = new SetTableNameReq(); - req.setReqId(reqId); - req.setStmtId(stmtId); - req.setName(tableName); - return new Request(Action.SET_TABLE_NAME.getAction(), req); - } - - public static Request generateSetTags(long stmtId, long reqId, Object[] tags) { - SetTagReq req = new SetTagReq(); - req.setReqId(reqId); - req.setStmtId(stmtId); - req.setTags(tags); - return new Request(Action.SET_TAGS.getAction(), req); - } - - public static Request generateBind(long stmtId, long reqId, Object[][] columns) { - BindReq req = new BindReq(); - req.setReqId(reqId); - req.setStmtId(stmtId); - req.setColumns(columns); - return new Request(Action.BIND.getAction(), req); - } - - public static Request generateBatch(long stmtId, long reqId) { - AddBatchReq req = new AddBatchReq(); - req.setReqId(reqId); - req.setStmtId(stmtId); - return new Request(Action.ADD_BATCH.getAction(), req); - } - - public static Request generateExec(long stmtId, long reqId) { - ExecReq req = new ExecReq(); - req.setReqId(reqId); - req.setStmtId(stmtId); - return new Request(Action.EXEC.getAction(), req); - } - public static Request generateGetColFields(long stmtId, long reqId) { - ExecReq req = new ExecReq(); - req.setReqId(reqId); - req.setStmtId(stmtId); - return new Request(Action.GET_COL_FIELDS.getAction(), req); - } - public static Request generateClose(long stmtId, long reqId) { - CloseReq req = new CloseReq(); - req.setReqId(reqId); - req.setStmtId(stmtId); - return new Request(Action.CLOSE.getAction(), req); - } - -} diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/SetTableNameReq.java b/src/main/java/com/taosdata/jdbc/ws/stmt/entity/SetTableNameReq.java deleted file mode 100644 index bfdfd242..00000000 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/SetTableNameReq.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.taosdata.jdbc.ws.stmt.entity; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.taosdata.jdbc.utils.UInt64Serializer; -import com.taosdata.jdbc.ws.entity.Payload; - -public class SetTableNameReq extends Payload { - @JsonProperty("stmt_id") - @JsonSerialize(using = UInt64Serializer.class) - private long stmtId; - private String name; - - public long getStmtId() { - return stmtId; - } - - public void setStmtId(long stmtId) { - this.stmtId = stmtId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/SetTagReq.java b/src/main/java/com/taosdata/jdbc/ws/stmt/entity/SetTagReq.java deleted file mode 100644 index 7bcdbe55..00000000 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/SetTagReq.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.taosdata.jdbc.ws.stmt.entity; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.taosdata.jdbc.utils.UInt64Serializer; -import com.taosdata.jdbc.ws.entity.Payload; - -public class SetTagReq extends Payload { - @JsonProperty("stmt_id") - @JsonSerialize(using = UInt64Serializer.class) - private long stmtId; - private Object[] tags; - - public long getStmtId() { - return stmtId; - } - - public void setStmtId(long stmtId) { - this.stmtId = stmtId; - } - - public Object[] getTags() { - return tags; - } - - public void setTags(Object[] tags) { - this.tags = tags; - } -} diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/CloseReq.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/CloseReq.java similarity index 92% rename from src/main/java/com/taosdata/jdbc/ws/stmt/entity/CloseReq.java rename to src/main/java/com/taosdata/jdbc/ws/stmt2/entity/CloseReq.java index 9f59912d..68c5d160 100644 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/CloseReq.java +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/CloseReq.java @@ -1,4 +1,4 @@ -package com.taosdata.jdbc.ws.stmt.entity; +package com.taosdata.jdbc.ws.stmt2.entity; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/ExecReq.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/ExecReq.java similarity index 92% rename from src/main/java/com/taosdata/jdbc/ws/stmt/entity/ExecReq.java rename to src/main/java/com/taosdata/jdbc/ws/stmt2/entity/ExecReq.java index f0d3d315..badd7ccc 100644 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/ExecReq.java +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/ExecReq.java @@ -1,4 +1,4 @@ -package com.taosdata.jdbc.ws.stmt.entity; +package com.taosdata.jdbc.ws.stmt2.entity; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/Field.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Field.java similarity index 60% rename from src/main/java/com/taosdata/jdbc/ws/stmt/entity/Field.java rename to src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Field.java index 9dafe929..6d62b41c 100644 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/Field.java +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Field.java @@ -1,13 +1,17 @@ -package com.taosdata.jdbc.ws.stmt.entity; +package com.taosdata.jdbc.ws.stmt2.entity; + +import com.fasterxml.jackson.annotation.JsonProperty; public class Field { private String name; - private byte field_type; + @JsonProperty("field_type") + private byte fieldType; private byte precision; private byte scale; private int bytes; - // getters and setters + @JsonProperty("bind_type") + private byte bindType; public String getName() { return name; } @@ -17,11 +21,11 @@ public void setName(String name) { } public byte getFieldType() { - return field_type; + return fieldType; } - public void setFieldType(byte field_type) { - this.field_type = field_type; + public void setFieldType(byte fieldType) { + this.fieldType = fieldType; } public byte getPrecision() { @@ -47,4 +51,12 @@ public int getBytes() { public void setBytes(int bytes) { this.bytes = bytes; } + + public byte getBindType() { + return bindType; + } + + public void setBindType(byte bindType) { + this.bindType = bindType; + } } \ No newline at end of file diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/InitReq.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/InitReq.java new file mode 100644 index 00000000..0f984c4e --- /dev/null +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/InitReq.java @@ -0,0 +1,27 @@ +package com.taosdata.jdbc.ws.stmt2.entity; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.taosdata.jdbc.ws.entity.Payload; + +public class InitReq extends Payload { + @JsonProperty("single_stb_insert") + private boolean singleStbInsert; + @JsonProperty("single_table_bind_once") + private boolean singleTableBindOnce; + + public boolean isSingleStbInsert() { + return singleStbInsert; + } + + public void setSingleStbInsert(boolean singleStbInsert) { + this.singleStbInsert = singleStbInsert; + } + + public boolean isSingleTableBindOnce() { + return singleTableBindOnce; + } + + public void setSingleTableBindOnce(boolean singleTableBindOnce) { + this.singleTableBindOnce = singleTableBindOnce; + } +} diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/PrepareReq.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/PrepareReq.java similarity index 70% rename from src/main/java/com/taosdata/jdbc/ws/stmt/entity/PrepareReq.java rename to src/main/java/com/taosdata/jdbc/ws/stmt2/entity/PrepareReq.java index ebcc2d10..5a91f155 100644 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/PrepareReq.java +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/PrepareReq.java @@ -1,4 +1,4 @@ -package com.taosdata.jdbc.ws.stmt.entity; +package com.taosdata.jdbc.ws.stmt2.entity; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -10,7 +10,8 @@ public class PrepareReq extends Payload { @JsonSerialize(using = UInt64Serializer.class) private long stmtId; private String sql; - + @JsonProperty("get_fields") + private boolean getFields = true; public long getStmtId() { return stmtId; } @@ -26,4 +27,13 @@ public String getSql() { public void setSql(String sql) { this.sql = sql; } + + + public boolean isGetFields() { + return getFields; + } + + public void setGetFields(boolean getFields) { + this.getFields = getFields; + } } diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/RequestFactory.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/RequestFactory.java new file mode 100644 index 00000000..64dc6aef --- /dev/null +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/RequestFactory.java @@ -0,0 +1,49 @@ +package com.taosdata.jdbc.ws.stmt2.entity; + + +import com.taosdata.jdbc.ws.entity.Action; +import com.taosdata.jdbc.ws.entity.Request; + +/** + * generate id for request + */ +public class RequestFactory { + + private RequestFactory() { + } + + public static Request generateInit(long reqId, boolean singleStbInsert, boolean singleTableBindOnce) { + InitReq initReq = new InitReq(); + initReq.setReqId(reqId); + initReq.setSingleStbInsert(singleStbInsert); + initReq.setSingleTableBindOnce(singleTableBindOnce); + return new Request(Action.STMT2_INIT.getAction(), initReq); + } + + public static Request generatePrepare(long stmtId, long reqId, String sql) { + PrepareReq prepareReq = new PrepareReq(); + prepareReq.setReqId(reqId); + prepareReq.setStmtId(stmtId); + prepareReq.setSql(sql); + return new Request(Action.STMT2_PREPARE.getAction(), prepareReq); + } + + public static Request generateExec(long stmtId, long reqId) { + ExecReq req = new ExecReq(); + req.setReqId(reqId); + req.setStmtId(stmtId); + return new Request(Action.STMT2_EXEC.getAction(), req); + } + public static Request generateClose(long stmtId, long reqId) { + CloseReq req = new CloseReq(); + req.setReqId(reqId); + req.setStmtId(stmtId); + return new Request(Action.STMT2_CLOSE.getAction(), req); + } + public static Request generateUseResult(long stmtId, long reqId) { + ResultReq req = new ResultReq(); + req.setReqId(reqId); + req.setStmtId(stmtId); + return new Request(Action.STMT2_USE_RESULT.getAction(), req); + } +} diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/AddBatchReq.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/ResultReq.java similarity index 84% rename from src/main/java/com/taosdata/jdbc/ws/stmt/entity/AddBatchReq.java rename to src/main/java/com/taosdata/jdbc/ws/stmt2/entity/ResultReq.java index 3ac916a0..52512421 100644 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/AddBatchReq.java +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/ResultReq.java @@ -1,11 +1,11 @@ -package com.taosdata.jdbc.ws.stmt.entity; +package com.taosdata.jdbc.ws.stmt2.entity; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.taosdata.jdbc.utils.UInt64Serializer; import com.taosdata.jdbc.ws.entity.Payload; -public class AddBatchReq extends Payload { +public class ResultReq extends Payload { @JsonProperty("stmt_id") @JsonSerialize(using = UInt64Serializer.class) private long stmtId; diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/GetColFieldsResp.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/ResultResp.java similarity index 60% rename from src/main/java/com/taosdata/jdbc/ws/stmt/entity/GetColFieldsResp.java rename to src/main/java/com/taosdata/jdbc/ws/stmt2/entity/ResultResp.java index 803d22ba..36d012fc 100644 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/GetColFieldsResp.java +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/ResultResp.java @@ -1,17 +1,19 @@ -package com.taosdata.jdbc.ws.stmt.entity; +package com.taosdata.jdbc.ws.stmt2.entity; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.taosdata.jdbc.utils.UInt64Deserializer; import com.taosdata.jdbc.ws.entity.CommonResp; +import com.taosdata.jdbc.ws.entity.QueryResp; -import java.util.List; - -public class GetColFieldsResp extends CommonResp { +/** + * query result pojo + */ +public class ResultResp extends QueryResp { @JsonProperty("stmt_id") @JsonDeserialize(using = UInt64Deserializer.class) private long stmtId; - private List fields; + public long getStmtId() { return stmtId; } @@ -19,12 +21,4 @@ public long getStmtId() { public void setStmtId(long stmtId) { this.stmtId = stmtId; } - - public List getFields() { - return fields; - } - - public void setFields(List fields) { - this.fields = fields; - } } diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/ExecResp.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Stmt2ExecResp.java similarity index 87% rename from src/main/java/com/taosdata/jdbc/ws/stmt/entity/ExecResp.java rename to src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Stmt2ExecResp.java index cc388dcd..e6942376 100644 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/ExecResp.java +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Stmt2ExecResp.java @@ -1,11 +1,11 @@ -package com.taosdata.jdbc.ws.stmt.entity; +package com.taosdata.jdbc.ws.stmt2.entity; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.taosdata.jdbc.utils.UInt64Deserializer; import com.taosdata.jdbc.ws.entity.CommonResp; -public class ExecResp extends CommonResp { +public class Stmt2ExecResp extends CommonResp { @JsonProperty("stmt_id") @JsonDeserialize(using = UInt64Deserializer.class) private long stmtId; diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Stmt2PrepareResp.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Stmt2PrepareResp.java new file mode 100644 index 00000000..2d0d36c5 --- /dev/null +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Stmt2PrepareResp.java @@ -0,0 +1,50 @@ +package com.taosdata.jdbc.ws.stmt2.entity; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.taosdata.jdbc.utils.UInt64Deserializer; +import com.taosdata.jdbc.ws.entity.CommonResp; + +import java.util.List; + +public class Stmt2PrepareResp extends CommonResp { + @JsonProperty("stmt_id") + @JsonDeserialize(using = UInt64Deserializer.class) + private long stmtId; + private List fields; + @JsonProperty("fields_count") + private int fieldsCount; + @JsonProperty("is_insert") + private boolean isInsert; + public long getStmtId() { + return stmtId; + } + + public void setStmtId(long stmtId) { + this.stmtId = stmtId; + } + + public List getFields() { + return fields; + } + + public void setFields(List fields) { + this.fields = fields; + } + + public int getFieldsCount() { + return fieldsCount; + } + + public void setFieldsCount(int fieldsCount) { + this.fieldsCount = fieldsCount; + } + public boolean isInsert() { + return isInsert; + } + + public void setInsert(boolean insert) { + isInsert = insert; + } + +} diff --git a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/StmtResp.java b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Stmt2Resp.java similarity index 82% rename from src/main/java/com/taosdata/jdbc/ws/stmt/entity/StmtResp.java rename to src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Stmt2Resp.java index f1a27a59..c1a3be08 100644 --- a/src/main/java/com/taosdata/jdbc/ws/stmt/entity/StmtResp.java +++ b/src/main/java/com/taosdata/jdbc/ws/stmt2/entity/Stmt2Resp.java @@ -1,12 +1,14 @@ -package com.taosdata.jdbc.ws.stmt.entity; +package com.taosdata.jdbc.ws.stmt2.entity; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.taosdata.jdbc.utils.UInt64Deserializer; import com.taosdata.jdbc.ws.entity.CommonResp; +import java.util.List; + // init | prepare | set_table_name | set_tags | bind | add_batch -public class StmtResp extends CommonResp { +public class Stmt2Resp extends CommonResp { @JsonProperty("stmt_id") @JsonDeserialize(using = UInt64Deserializer.class) private long stmtId; diff --git a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java index c9d79a2c..1c1a69f3 100644 --- a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java +++ b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java @@ -273,7 +273,8 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { if (value instanceof Timestamp) return (Timestamp) value; if (value instanceof Long) { - return DataTypeConverUtil.parseTimestampColumnDataWithZoneId((long) value, this.timestampPrecision, zoneId); + Instant instant = DateTimeUtils.parseTimestampColumnData((long) value, this.timestampPrecision); + return DateTimeUtils.getTimestamp(instant, zoneId); } Timestamp ret; try { @@ -474,7 +475,7 @@ public Object parseValue(int columnIndex) { return null; int type = fields.get(columnIndex - 1).getTaosType(); - return DataTypeConverUtil.parseValue(type, source, this.timestampPrecision, zoneId); + return DataTypeConverUtil.parseValue(type, source); } // ceil(numOfRows/8.0) diff --git a/src/test/java/com/taosdata/jdbc/TSDBPreparedStatementPerfTest.java b/src/test/java/com/taosdata/jdbc/TSDBPreparedStatementPerfTest.java index 5927f2e9..f11e9486 100644 --- a/src/test/java/com/taosdata/jdbc/TSDBPreparedStatementPerfTest.java +++ b/src/test/java/com/taosdata/jdbc/TSDBPreparedStatementPerfTest.java @@ -108,6 +108,7 @@ public static Connection genConnection() throws SQLException { @Test + @Ignore public void testStmtPerf() throws Exception { prepareMeta(); System.out.println("prepareMeta successfully"); diff --git a/src/test/java/com/taosdata/jdbc/TSDBResultSetTest.java b/src/test/java/com/taosdata/jdbc/TSDBResultSetTest.java index da168995..0adcab45 100644 --- a/src/test/java/com/taosdata/jdbc/TSDBResultSetTest.java +++ b/src/test/java/com/taosdata/jdbc/TSDBResultSetTest.java @@ -12,6 +12,7 @@ import java.sql.*; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.Instant; import java.util.Arrays; import java.util.Calendar; import java.util.Map; diff --git a/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java b/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java index 76e3896a..95a799c3 100644 --- a/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java +++ b/src/test/java/com/taosdata/jdbc/utils/DataTypeConverUtilTest.java @@ -48,8 +48,8 @@ public void testGetBoolean() throws SQLDataException { assertFalse(DataTypeConverUtil.getBoolean(TSDB_DATA_TYPE_BIGINT, 0L)); // Test TIMESTAMP - assertTrue(DataTypeConverUtil.getBoolean(TSDB_DATA_TYPE_TIMESTAMP, new Timestamp(1L))); - assertFalse(DataTypeConverUtil.getBoolean(TSDB_DATA_TYPE_TIMESTAMP, new Timestamp(0L))); + assertTrue(DataTypeConverUtil.getBoolean(TSDB_DATA_TYPE_TIMESTAMP, Instant.ofEpochMilli(1L))); + assertFalse(DataTypeConverUtil.getBoolean(TSDB_DATA_TYPE_TIMESTAMP, Instant.ofEpochMilli(0L))); // Test UBIGINT assertTrue(DataTypeConverUtil.getBoolean(TSDB_DATA_TYPE_UBIGINT, new BigDecimal(1))); @@ -481,9 +481,10 @@ public String toString() { @Test public void testGetDate() { // Test with Timestamp - Timestamp timestamp = new Timestamp(System.currentTimeMillis()); - Date expectedDate = new Date(timestamp.getTime()); - assertEquals(expectedDate, DataTypeConverUtil.getDate(timestamp, null)); + Instant now = Instant.now(); + Date expectedDate = DateTimeUtils.getDate(now, null); + Date actualDate = DataTypeConverUtil.getDate(now, null); + assertEquals(expectedDate, actualDate); // Test with byte array representing a date string String dateString = "2023-10-10 10:10:10.123"; @@ -505,9 +506,9 @@ public void testGetDate() { @Ignore public void testGetTime() { // Test with Timestamp - Timestamp timestamp = new Timestamp(Instant.now().toEpochMilli()); - Time expectedTime = new Time(timestamp.getTime()); - assertEquals(expectedTime, DataTypeConverUtil.getTime(timestamp, null)); + Instant now = Instant.now(); + Time expectedTime = DateTimeUtils.getTime(now, null); + assertEquals(expectedTime, DataTypeConverUtil.getTime(now, null)); // Test with byte array representing a valid time string String timeString = "12:34:56"; @@ -565,8 +566,8 @@ public void testGetBigDecimal() { assertEquals(BigDecimal.valueOf(3.141592653589793), DataTypeConverUtil.getBigDecimal(TSDB_DATA_TYPE_DOUBLE, 3.141592653589793)); // TIMESTAMP - Timestamp timestamp = new Timestamp(1627846261000L); - assertEquals(new BigDecimal(timestamp.getTime()), DataTypeConverUtil.getBigDecimal(TSDB_DATA_TYPE_TIMESTAMP, timestamp)); + Instant now = Instant.now(); + assertEquals(new BigDecimal(now.toEpochMilli()), DataTypeConverUtil.getBigDecimal(TSDB_DATA_TYPE_TIMESTAMP, now)); // NCHAR assertEquals(new BigDecimal("123.456"), DataTypeConverUtil.getBigDecimal(TSDB_DATA_TYPE_NCHAR, "123.456")); @@ -581,66 +582,61 @@ public void testGetBigDecimal() { @Test public void testParseValue() { // BOOL - assertEquals(Boolean.TRUE, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BOOL, (byte) 1, TimestampPrecision.MS, null)); - assertEquals(Boolean.FALSE, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BOOL, (byte) 0, TimestampPrecision.MS, null)); + assertEquals(Boolean.TRUE, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BOOL, (byte) 1)); + assertEquals(Boolean.FALSE, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BOOL, (byte) 0)); // UTINYINT - assertEquals((short)255, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UTINYINT, (byte) -1, TimestampPrecision.MS, null)); + assertEquals((short)255, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UTINYINT, (byte) -1)); // TINYINT, SMALLINT, INT, BIGINT, FLOAT, DOUBLE, BINARY, JSON, VARBINARY, GEOMETRY - assertEquals((byte) 127, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_TINYINT, (byte) 127, TimestampPrecision.MS, null)); - assertEquals((short) 32767, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_SMALLINT, (short) 32767, TimestampPrecision.MS, null)); - assertEquals(2147483647, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_INT, 2147483647, TimestampPrecision.MS, null)); - assertEquals(9223372036854775807L, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BIGINT, 9223372036854775807L, TimestampPrecision.MS, null)); - assertEquals(3.14f, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_FLOAT, 3.14f, TimestampPrecision.MS, null)); - assertEquals(3.141592653589793, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_DOUBLE, 3.141592653589793, TimestampPrecision.MS, null)); - assertEquals("binaryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BINARY, "binaryData", TimestampPrecision.MS, null)); - assertEquals("jsonData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_JSON, "jsonData", TimestampPrecision.MS, null)); - assertEquals("varbinaryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_VARBINARY, "varbinaryData", TimestampPrecision.MS, null)); - assertEquals("geometryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_GEOMETRY, "geometryData", TimestampPrecision.MS, null)); + assertEquals((byte) 127, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_TINYINT, (byte) 127)); + assertEquals((short) 32767, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_SMALLINT, (short) 32767)); + assertEquals(2147483647, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_INT, 2147483647)); + assertEquals(9223372036854775807L, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BIGINT, 9223372036854775807L)); + assertEquals(3.14f, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_FLOAT, 3.14f)); + assertEquals(3.141592653589793, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_DOUBLE, 3.141592653589793)); + assertEquals("binaryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_BINARY, "binaryData")); + assertEquals("jsonData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_JSON, "jsonData")); + assertEquals("varbinaryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_VARBINARY, "varbinaryData")); + assertEquals("geometryData", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_GEOMETRY, "geometryData")); // USMALLINT - assertEquals(65535, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_USMALLINT, (short) -1, TimestampPrecision.MS, null)); + assertEquals(65535, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_USMALLINT, (short) -1)); // UINT - assertEquals(4294967295L, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UINT, -1, TimestampPrecision.MS, null)); + assertEquals(4294967295L, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UINT, -1)); // TIMESTAMP - long currentTimeMillis = System.currentTimeMillis(); - Timestamp expectedTimestamp = new Timestamp(currentTimeMillis); - assertEquals(expectedTimestamp, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_TIMESTAMP, currentTimeMillis, TimestampPrecision.MS, null)); + Instant now = Instant.now(); + assertEquals(now, DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_TIMESTAMP, now)); // UBIGINT - assertEquals(new BigDecimal("18446744073709551615"), DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UBIGINT, -1L, TimestampPrecision.MS, null)); + assertEquals(new BigDecimal("18446744073709551615"), DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_UBIGINT, -1L)); // NCHAR int[] ncharData = {65, 66, 67}; // Corresponds to "ABC" - assertEquals("ABC", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_NCHAR, ncharData, TimestampPrecision.MS, null)); + assertEquals("ABC", DataTypeConverUtil.parseValue(TSDB_DATA_TYPE_NCHAR, ncharData)); // Default case - assertNull(DataTypeConverUtil.parseValue(-1, "unknownType", TimestampPrecision.MS, null)); + assertNull(DataTypeConverUtil.parseValue(-1, "unknownType")); } @Test public void testParseTimestampColumnData() { + // 纳秒时间戳 + long nanos = System.nanoTime(); + Instant instantFromMilli = Instant.ofEpochSecond(nanos / 1_000_000_000, (nanos % 1_000_000_000) / 1_000_000 * 1_000_000); + Instant instantFromMacro = Instant.ofEpochSecond(nanos / 1_000_000_000, (nanos % 1_000_000_000) / 1_000 * 1_000); + Instant instantFromNanos = Instant.ofEpochSecond(nanos / 1_000_000_000, nanos % 1_000_000_000); + // Test with millisecond precision - long currentTimeMillis = System.currentTimeMillis(); - Timestamp expectedTimestampMs = new Timestamp(currentTimeMillis); - assertEquals(expectedTimestampMs, DataTypeConverUtil.parseTimestampColumnData(currentTimeMillis, TimestampPrecision.MS)); + assertEquals(instantFromMilli, DateTimeUtils.parseTimestampColumnData(nanos / 1_000_000, TimestampPrecision.MS)); // Test with microsecond precision - long currentTimeMicros = currentTimeMillis * 1000L + 123; // Adding some microsecond part - long expectedEpochSecUs = currentTimeMicros / 1000_000L; - long expectedNanoAdjustmentUs = (currentTimeMicros % 1000_000L) * 1000L; - Timestamp expectedTimestampUs = Timestamp.from(Instant.ofEpochSecond(expectedEpochSecUs, expectedNanoAdjustmentUs)); - assertEquals(expectedTimestampUs, DataTypeConverUtil.parseTimestampColumnData(currentTimeMicros, TimestampPrecision.US)); + assertEquals(instantFromMacro, DateTimeUtils.parseTimestampColumnData(nanos / 1_000, TimestampPrecision.US)); // Test with nanosecond precision - long currentTimeNanos = currentTimeMillis * 1000_000L + 123456; // Adding some nanosecond part - long expectedEpochSecNs = currentTimeNanos / 1000_000_000L; - long expectedNanoAdjustmentNs = currentTimeNanos % 1000_000_000L; - Timestamp expectedTimestampNs = Timestamp.from(Instant.ofEpochSecond(expectedEpochSecNs, expectedNanoAdjustmentNs)); - assertEquals(expectedTimestampNs, DataTypeConverUtil.parseTimestampColumnData(currentTimeNanos, TimestampPrecision.NS)); + assertEquals(instantFromNanos, DateTimeUtils.parseTimestampColumnData(nanos, TimestampPrecision.NS)); } } diff --git a/src/test/java/com/taosdata/jdbc/utils/InsertSqlPatternTest.java b/src/test/java/com/taosdata/jdbc/utils/InsertSqlPatternTest.java index 19d50307..859145a2 100644 --- a/src/test/java/com/taosdata/jdbc/utils/InsertSqlPatternTest.java +++ b/src/test/java/com/taosdata/jdbc/utils/InsertSqlPatternTest.java @@ -18,29 +18,5 @@ public class InsertSqlPatternTest { "insert into st.sub_t using db.sup_t (t1, t2) tags(?,?) (c1, c2) values(?, ?)", }; - @Test - public void test() { - for (String sql : sqls) { - Matcher matcher = TSWSPreparedStatement.INSERT_PATTERN.matcher(sql); - String db = null; - if (matcher.find()) { - if (matcher.group(1).equals("?") && matcher.group(3) != null) { - String usingGroup = matcher.group(3); - if (usingGroup.contains(".")) { - String[] split = usingGroup.split("\\."); - db = split[0]; - } - } else { - String usingGroup = matcher.group(1); - if (usingGroup.contains(".")) { - String[] split = usingGroup.split("\\."); - db = split[0]; - } - } - Assert.assertEquals("st", db); - } else { - throw new RuntimeException("not match sql: " + sql); - } - } - } + } diff --git a/src/test/java/com/taosdata/jdbc/ws/TSWSPreparedStatementTest.java b/src/test/java/com/taosdata/jdbc/ws/TSWSPreparedStatementTest.java index 46e6a699..a92c5330 100644 --- a/src/test/java/com/taosdata/jdbc/ws/TSWSPreparedStatementTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/TSWSPreparedStatementTest.java @@ -148,9 +148,9 @@ public void executeTest() throws SQLException { stmt.execute("drop table if exists weather_test"); stmt.execute("create table weather_test(ts timestamp, f1 nchar(4), f2 float, f3 double, f4 timestamp, f5 int, f6 bool, f7 binary(10))"); - TSWSPreparedStatement s = (TSWSPreparedStatement) conn.prepareStatement("insert into ? values(?, ?, ?, ?, ?, ?, ?, ?)"); + TSWSPreparedStatement s = (TSWSPreparedStatement) conn.prepareStatement("insert into weather_test values(?, ?, ?, ?, ?, ?, ?, ?)"); Random r = new Random(); - s.setTableName("weather_test"); + //s.setTableName("weather_test"); ArrayList ts = new ArrayList<>(); for (int i = 0; i < numOfRows; i++) { @@ -260,9 +260,8 @@ public void bindDataSelectColumnTest() throws SQLException { stmt.execute("drop table if exists weather_test"); stmt.execute("create table weather_test(ts timestamp, f1 nchar(4), f2 float, f3 double, f4 timestamp, f5 int, f6 bool, f7 binary(10))"); - TSWSPreparedStatement s = (TSWSPreparedStatement) conn.prepareStatement("insert into ? (ts, f1, f7) values(?, ?, ?)"); + TSWSPreparedStatement s = (TSWSPreparedStatement) conn.prepareStatement("insert into weather_test (ts, f1, f7) values(?, ?, ?)"); Random r = new Random(); - s.setTableName("weather_test"); ArrayList ts = new ArrayList<>(); for (int i = 0; i < numOfRows; i++) { diff --git a/src/test/java/com/taosdata/jdbc/ws/TaosAdapterMock.java b/src/test/java/com/taosdata/jdbc/ws/TaosAdapterMock.java index 60f43e79..627d5228 100644 --- a/src/test/java/com/taosdata/jdbc/ws/TaosAdapterMock.java +++ b/src/test/java/com/taosdata/jdbc/ws/TaosAdapterMock.java @@ -170,7 +170,7 @@ public TargetWebSocketClient(WebSocket sourceConnection, URI serverUri) { public void onOpen(ServerHandshake handshakedata) { System.out.println("Connected to target server: " + getURI()); // Optionally, send an initial message to the target server - send("Hello from Proxy Server!"); + // send("Hello from Proxy Server!"); } diff --git a/src/test/java/com/taosdata/jdbc/ws/WSDriverBaseTest.java b/src/test/java/com/taosdata/jdbc/ws/WSDriverBaseTest.java index 53f49936..8a95f115 100644 --- a/src/test/java/com/taosdata/jdbc/ws/WSDriverBaseTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/WSDriverBaseTest.java @@ -32,6 +32,13 @@ public void queryBlock() throws SQLException { resultSet.next(); Assert.assertEquals(100, resultSet.getInt(2)); } + + + statement.execute("select 1 where 0"); + try (ResultSet resultSet = statement.executeQuery("select * from " + db_name + "." + tableName)) { + resultSet.next(); + Assert.assertEquals(100, resultSet.getInt(2)); + } } catch (SQLException e) { e.printStackTrace(); throw e; diff --git a/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java b/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java index 330cb009..1159d0a8 100644 --- a/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/WSTimeZoneTest.java @@ -4,16 +4,16 @@ import com.taosdata.jdbc.annotation.CatalogRunner; import com.taosdata.jdbc.annotation.Description; import com.taosdata.jdbc.annotation.TestTarget; +import com.taosdata.jdbc.utils.DateTimeUtils; import com.taosdata.jdbc.utils.SpecifyAddress; import org.junit.*; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.sql.*; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.sql.Date; +import java.time.*; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.stream.IntStream; @@ -33,8 +33,9 @@ public WSTimeZoneTest(String precision) { private static final String host = "127.0.0.1"; private static final int port = 6041; - private static final String db_name = "ws_query"; - private static final String tableName = "wq"; + private static final String db_name = "ws_timezone"; + private static final String tableName = "simple_t"; + private static final String fullTableName = "full_t"; private Connection connection; // 提供参数 @@ -68,12 +69,18 @@ public void TimeZoneQueryTest() throws SQLException, InterruptedException { } @Test - public void TimeZoneStmtTest() throws SQLException, InterruptedException { + public void TimeZoneStmtTimestampTest() throws SQLException, InterruptedException { String sql = "insert into " + db_name + "." + tableName + " values(?, ?)"; PreparedStatement statement = connection.prepareStatement(sql); - LocalDateTime localDateTime = LocalDateTime.of(2024, 1, 2, 0, 0, 0, 123456789); - statement.setTimestamp(1, Timestamp.valueOf(localDateTime)); + LocalDateTime localDateTime = LocalDateTime.of(2024, 1, 2, 1, 2, 3, 123456789); + ZoneId zoneId = ZoneId.of("Asia/Tokyo"); + ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + Instant instant = zonedDateTime.toInstant(); + + Timestamp timestamp = DateTimeUtils.getTimestamp(instant, zoneId); + + statement.setTimestamp(1, timestamp); statement.setInt(2, 2); int i = statement.executeUpdate(); Assert.assertEquals(1, i); @@ -84,19 +91,90 @@ public void TimeZoneStmtTest() throws SQLException, InterruptedException { while (resultSet.next()) { Timestamp ts = resultSet.getTimestamp("ts"); + Assert.assertEquals("2024-01-02 01:02:03." + precisionMap.get(this.precision), ts.toString()); - System.out.println("ts: " + ts); - System.out.println("ts: " + ts.getTime()); - Assert.assertEquals("2024-01-02 01:00:00." + precisionMap.get(this.precision), ts.toString()); + Date date = resultSet.getDate("ts"); + Assert.assertEquals("2024-01-02", date.toString()); + + Time time = resultSet.getTime("ts"); + Assert.assertEquals("01:02:03", time.toString()); + + Instant ins = resultSet.getObject("ts", Instant.class); + Assert.assertEquals(ins.toEpochMilli(), instant.toEpochMilli()); + + ZonedDateTime zdt = resultSet.getObject("ts", ZonedDateTime.class); + Assert.assertEquals(zdt.toInstant().toEpochMilli(), instant.toEpochMilli()); + + OffsetDateTime odt = resultSet.getObject("ts", OffsetDateTime.class); + Assert.assertEquals(odt.toInstant().toEpochMilli(), instant.toEpochMilli()); + + LocalDateTime ldt = resultSet.getObject("ts", LocalDateTime.class); + Assert.assertEquals(ldt.truncatedTo(ChronoUnit.MILLIS), instant.atZone(zoneId).toLocalDateTime().truncatedTo(ChronoUnit.MILLIS)); } } catch (SQLException e) { e.printStackTrace(); throw e; } + } + + @Test + public void TimeZoneStmtAllTimeTypeTest() throws SQLException, InterruptedException { + String sql = "insert into " + db_name + "." + fullTableName + " values(?, ?, ?, ?, ?, ?)"; + PreparedStatement statement = connection.prepareStatement(sql); + + // 创建一个 LocalDateTime 对象,表示特定的日期和时间 + LocalDateTime localDateTime = LocalDateTime.of(2024, 1, 2, 1, 2, 3, 123456789); + + // 指定时区 + ZoneId zoneId = ZoneId.of("Asia/Tokyo"); + + // 将 LocalDateTime 转换为 ZonedDateTime + ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + // 将 ZonedDateTime 转换为 Instant + Instant instant = zonedDateTime.toInstant(); + statement.setObject(1, instant); + statement.setObject(2, ZonedDateTime.ofInstant(instant, zoneId)); + statement.setObject(3, OffsetDateTime.ofInstant(instant, zoneId)); + statement.setObject(4, localDateTime); + + LocalDate localDate = localDateTime.toLocalDate(); + LocalTime localTime = localDateTime.toLocalTime(); + + statement.setObject(5, Date.valueOf(localDate)); + statement.setObject(6, Time.valueOf(localTime)); + + int i = statement.executeUpdate(); + Assert.assertEquals(1, i); + statement.close(); + + try (Statement qStmt = connection.createStatement(); + ResultSet resultSet = qStmt.executeQuery("select * from " + db_name + "." + fullTableName + " limit 1")) { + while (resultSet.next()) { + + Timestamp ts1 = resultSet.getTimestamp("ts1"); + Timestamp ts2 = resultSet.getTimestamp("ts2"); + Timestamp ts3 = resultSet.getTimestamp("ts3"); + Timestamp ts4 = resultSet.getTimestamp("ts4"); + Timestamp ts5 = resultSet.getTimestamp("ts5"); + Timestamp ts6 = resultSet.getTimestamp("ts6"); + + Assert.assertEquals("2024-01-02 01:02:03." + precisionMap.get(this.precision), ts1.toString()); + Assert.assertEquals("2024-01-02 01:02:03." + precisionMap.get(this.precision), ts2.toString()); + Assert.assertEquals("2024-01-02 01:02:03." + precisionMap.get(this.precision), ts3.toString()); + Assert.assertEquals("2024-01-02 01:02:03." + precisionMap.get(this.precision), ts4.toString()); + + Assert.assertEquals("2024-01-02", new Date(ts5.getTime()).toString()); + Assert.assertEquals("01:02:03", new Time(ts6.getTime()).toString()); + } + } catch (SQLException e) { + e.printStackTrace(); + throw e; + } } + @Test public void TimeZoneTest3() throws SQLException, InterruptedException { long value = 1704038400000L; // 示例时间戳 @@ -143,7 +221,8 @@ public void before() throws SQLException { statement.execute("drop database if exists " + db_name); statement.execute("create database " + db_name + " precision " + precision); statement.execute("use " + db_name); - statement.execute("create table if not exists " + db_name + "." + tableName + "(ts timestamp, f int)"); + statement.execute("create table if not exists " + db_name + "." + tableName + " (ts timestamp, f int)"); + statement.execute("create table if not exists " + db_name + "." + fullTableName + " (ts1 timestamp, ts2 timestamp, ts3 timestamp, ts4 timestamp, ts5 timestamp, ts6 timestamp)"); // Asia/Shanghai +08:00, 2024-01-01 00:00:00 statement.execute("insert into " + db_name + "." + tableName + " values (\"2024-01-01T00:00:00.123456789+08:00\", 1)"); diff --git a/src/test/java/com/taosdata/jdbc/ws/stmt/WsPStmtAllTypeNullTest.java b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPStmtAllTypeNullTest.java new file mode 100644 index 00000000..6628e8fc --- /dev/null +++ b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPStmtAllTypeNullTest.java @@ -0,0 +1,183 @@ +package com.taosdata.jdbc.ws.stmt; + +import com.taosdata.jdbc.utils.SpecifyAddress; +import com.taosdata.jdbc.ws.TSWSPreparedStatement; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.sql.*; +import java.util.Collections; +import java.util.Properties; + +import static com.taosdata.jdbc.TSDBConstants.*; + +public class WsPStmtAllTypeNullTest { + String host = "127.0.0.1"; + String db_name = "ws_prepare_type"; + String tableName = "wpt"; + String stableName = "swpt"; + Connection connection; + + @Test + public void testExecuteUpdate() throws SQLException { + String sql = "insert into " + db_name + "." + tableName + " values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + PreparedStatement statement = connection.prepareStatement(sql); + long current = System.currentTimeMillis(); + statement.setTimestamp(1, new Timestamp(current)); + statement.setNull(2, Types.TINYINT); + statement.setNull(3, Types.SMALLINT); + statement.setNull(4, Types.INTEGER); + statement.setNull(5, Types.BIGINT); + statement.setNull(6, Types.FLOAT); + statement.setNull(7, Types.DOUBLE); + statement.setNull(8, Types.BOOLEAN); + + statement.setString(9, null); + statement.setNString(10, null); + statement.setString(11, null); + statement.setNull(12, Types.VARBINARY); + statement.setNull(13, Types.VARBINARY); + statement.executeUpdate(); + + ResultSet resultSet = statement.executeQuery("select * from " + db_name + "." + tableName); + resultSet.next(); + Assert.assertEquals(resultSet.getTimestamp(1), new Timestamp(current)); + + resultSet.getByte(2); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getShort(3); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getInt(4); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getLong(5); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getFloat(6); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getDouble(7); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getBoolean(8); + Assert.assertTrue(resultSet.wasNull()); + + Assert.assertNull(resultSet.getString(9)); + Assert.assertNull(resultSet.getString(10)); + Assert.assertNull(resultSet.getString(11)); + + Assert.assertNull(resultSet.getBytes(12)); + Assert.assertNull(resultSet.getBytes(13)); + resultSet.close(); + statement.close(); + } + + + @Test + public void testExecuteUpdate2() throws SQLException { + String sql = "insert into stb_1 using " + db_name + "." + stableName + " tags (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) values (?, ?)"; + TSWSPreparedStatement statement = connection.prepareStatement(sql).unwrap(TSWSPreparedStatement.class); + long current = System.currentTimeMillis(); + + statement.setTagNull(0, TSDB_DATA_TYPE_TIMESTAMP); + statement.setTagNull(1, TSDB_DATA_TYPE_TINYINT); + statement.setTagNull(2, TSDB_DATA_TYPE_SMALLINT); + statement.setTagNull(3, TSDB_DATA_TYPE_INT); + statement.setTagNull(4, TSDB_DATA_TYPE_BIGINT); + statement.setTagNull(5, TSDB_DATA_TYPE_FLOAT); + statement.setTagNull(6, TSDB_DATA_TYPE_DOUBLE); + statement.setTagNull(7, TSDB_DATA_TYPE_BOOL); + + statement.setTagNull(8, TSDB_DATA_TYPE_BINARY); + statement.setTagNull(9, TSDB_DATA_TYPE_NCHAR); + statement.setTagNull(10, TSDB_DATA_TYPE_BINARY); + + statement.setTagNull(11, TSDB_DATA_TYPE_VARBINARY); + statement.setTagNull(12, TSDB_DATA_TYPE_GEOMETRY); + + statement.setTimestamp(0, Collections.singletonList(current)); + statement.setByte(1, Collections.singletonList(null)); + statement.columnDataAddBatch(); + statement.columnDataExecuteBatch(); + + ResultSet resultSet = statement.executeQuery("select * from " + db_name + "." + stableName); + resultSet.next(); + + + Assert.assertEquals(resultSet.getTimestamp(1), new Timestamp(current)); + + + resultSet.getByte(2); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getTimestamp(3); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getByte(4); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getShort(5); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getInt(6); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getLong(7); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getFloat(8); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getDouble(9); + Assert.assertTrue(resultSet.wasNull()); + + resultSet.getBoolean(10); + Assert.assertTrue(resultSet.wasNull()); + + Assert.assertNull(resultSet.getString(11)); + Assert.assertNull(resultSet.getString(12)); + Assert.assertNull(resultSet.getString(13)); + + Assert.assertNull(resultSet.getBytes(14)); + Assert.assertNull(resultSet.getBytes(15)); + resultSet.close(); + statement.close(); + } + + @Before + public void before() throws SQLException { + String url = SpecifyAddress.getInstance().getRestWithoutUrl(); + if (url == null) { + url = "jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata&batchfetch=true"; + } else { + url += "?user=root&password=taosdata&batchfetch=true"; + } + Properties properties = new Properties(); + connection = DriverManager.getConnection(url, properties); + Statement statement = connection.createStatement(); + statement.execute("drop database if exists " + db_name); + statement.execute("create database " + db_name + " keep 36500"); + statement.execute("use " + db_name); + statement.execute("create table if not exists " + db_name + "." + tableName + + "(ts timestamp, c1 tinyint, c2 smallint, c3 int, c4 bigint, " + + "c5 float, c6 double, c7 bool, c8 binary(10), c9 nchar(10), c10 varchar(20), c11 varbinary(100), c12 geometry(100))"); + + statement.execute("create stable if not exists " + db_name + "." + stableName + + "(ts timestamp, c1 tinyint) tags (t1 timestamp, t2 tinyint, t3 smallint, t4 int, t5 bigint, " + + "t6 float, t7 double, t8 bool, t9 binary(10), t10 nchar(10), t11 varchar(20), t12 varbinary(100), t13 geometry(100))"); + + statement.close(); + } + + @After + public void after() throws SQLException { + try (Statement statement = connection.createStatement()){ + statement.execute("drop database if exists " + db_name); + } + connection.close(); + } +} diff --git a/src/test/java/com/taosdata/jdbc/ws/WSPreparedStatementAllTypeTest.java b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtAllTypeTest.java similarity index 79% rename from src/test/java/com/taosdata/jdbc/ws/WSPreparedStatementAllTypeTest.java rename to src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtAllTypeTest.java index 6b99bebe..b1e45d49 100644 --- a/src/test/java/com/taosdata/jdbc/ws/WSPreparedStatementAllTypeTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtAllTypeTest.java @@ -1,24 +1,30 @@ -package com.taosdata.jdbc.ws; +package com.taosdata.jdbc.ws.stmt; import com.taosdata.jdbc.utils.SpecifyAddress; +import com.taosdata.jdbc.utils.StringUtils; +import com.taosdata.jdbc.ws.TSWSPreparedStatement; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.sql.*; +import java.util.Collections; import java.util.Properties; -public class WSPreparedStatementAllTypeTest { +public class WsPstmtAllTypeTest { String host = "127.0.0.1"; String db_name = "ws_prepare_type"; String tableName = "wpt"; String stableName = "swpt"; Connection connection; + static String testStr = "20160601"; + static byte[] expectedVarBinary = StringUtils.hexToBytes(testStr); + static byte[] expectedGeometry = StringUtils.hexToBytes("0101000000000000000000F03F0000000000000040"); @Test public void testExecuteUpdate() throws SQLException { - String sql = "insert into " + db_name + "." + tableName + " values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + String sql = "insert into " + db_name + "." + tableName + " values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; PreparedStatement statement = connection.prepareStatement(sql); long current = System.currentTimeMillis(); statement.setTimestamp(1, new Timestamp(current)); @@ -32,6 +38,8 @@ public void testExecuteUpdate() throws SQLException { statement.setString(9, "你好"); statement.setNString(10, "世界"); statement.setString(11, "hello world"); + statement.setBytes(12, expectedVarBinary); + statement.setBytes(13, expectedGeometry); statement.executeUpdate(); ResultSet resultSet = statement.executeQuery("select * from " + db_name + "." + tableName); @@ -47,7 +55,11 @@ public void testExecuteUpdate() throws SQLException { Assert.assertEquals(resultSet.getString(9), "你好"); Assert.assertEquals(resultSet.getString(10), "世界"); Assert.assertEquals(resultSet.getString(11), "hello world"); + Assert.assertArrayEquals(resultSet.getBytes(12), expectedVarBinary); + Assert.assertArrayEquals(resultSet.getBytes(13), expectedGeometry); + Date date = new Date(current); + Date date1 = resultSet.getDate(1); Assert.assertEquals(resultSet.getDate(1), new Date(current)); Assert.assertEquals(resultSet.getTime(1), new Time(current)); Assert.assertEquals(resultSet.getTimestamp(1), new Timestamp(current)); @@ -60,7 +72,7 @@ public void testExecuteUpdate() throws SQLException { @Test public void testExecuteUpdate2() throws SQLException { - String sql = "insert into stb_1 using " + db_name + "." + stableName + " tags (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) values (?, ?)"; + String sql = "insert into stb_1 using " + db_name + "." + stableName + " tags (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) values (?, ?)"; TSWSPreparedStatement statement = connection.prepareStatement(sql).unwrap(TSWSPreparedStatement.class); long current = System.currentTimeMillis(); statement.setTagTimestamp(0, new Timestamp(current)); @@ -74,10 +86,14 @@ public void testExecuteUpdate2() throws SQLException { statement.setTagString(8, "你好"); statement.setTagNString(9, "世界"); statement.setTagString(10, "hello world"); + statement.setTagVarbinary(11, expectedVarBinary); + statement.setTagGeometry(12, expectedGeometry); - statement.setTimestamp(1, new Timestamp(current)); - statement.setByte(2, (byte) 2); - statement.executeUpdate(); + + statement.setTimestamp(0, Collections.singletonList(current)); + statement.setByte(1, Collections.singletonList((byte) 2)); + statement.columnDataAddBatch(); + statement.columnDataExecuteBatch(); ResultSet resultSet = statement.executeQuery("select * from " + db_name + "." + stableName); resultSet.next(); @@ -96,13 +112,17 @@ public void testExecuteUpdate2() throws SQLException { Assert.assertEquals(resultSet.getString(12), "世界"); Assert.assertEquals(resultSet.getString(13), "hello world"); + Assert.assertArrayEquals(resultSet.getBytes(14), expectedVarBinary); + Assert.assertArrayEquals(resultSet.getBytes(15), expectedGeometry); + + resultSet.close(); statement.close(); } @Test public void testExecuteCriticalValue() throws SQLException { - String sql = "insert into " + db_name + "." + tableName + " values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + String sql = "insert into " + db_name + "." + tableName + " values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; PreparedStatement statement = connection.prepareStatement(sql); statement.setTimestamp(1, new Timestamp(0)); statement.setByte(2, (byte) 127); @@ -115,6 +135,9 @@ public void testExecuteCriticalValue() throws SQLException { statement.setString(9, "ABC"); statement.setNString(10, "涛思数据"); statement.setString(11, "陶"); + statement.setBytes(12, expectedVarBinary); + statement.setBytes(13, expectedGeometry); + statement.executeUpdate(); statement.close(); } @@ -135,11 +158,11 @@ public void before() throws SQLException { statement.execute("use " + db_name); statement.execute("create table if not exists " + db_name + "." + tableName + "(ts timestamp, c1 tinyint, c2 smallint, c3 int, c4 bigint, " + - "c5 float, c6 double, c7 bool, c8 binary(10), c9 nchar(10), c10 varchar(20))"); + "c5 float, c6 double, c7 bool, c8 binary(10), c9 nchar(10), c10 varchar(20), c11 varbinary(100), c12 geometry(100))"); statement.execute("create stable if not exists " + db_name + "." + stableName + "(ts timestamp, c1 tinyint) tags (t1 timestamp, t2 tinyint, t3 smallint, t4 int, t5 bigint, " + - "t6 float, t7 double, t8 bool, t9 binary(10), t10 nchar(10), t11 varchar(20))"); + "t6 float, t7 double, t8 bool, t9 binary(10), t10 nchar(10), t11 varchar(20), t12 varbinary(100), t13 geometry(100))"); statement.close(); } diff --git a/src/test/java/com/taosdata/jdbc/ws/WSPreparedStatementNsTest.java b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtNsTest.java similarity index 98% rename from src/test/java/com/taosdata/jdbc/ws/WSPreparedStatementNsTest.java rename to src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtNsTest.java index 1bb463a9..d14c1e3c 100644 --- a/src/test/java/com/taosdata/jdbc/ws/WSPreparedStatementNsTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtNsTest.java @@ -1,4 +1,4 @@ -package com.taosdata.jdbc.ws; +package com.taosdata.jdbc.ws.stmt; import com.taosdata.jdbc.utils.SpecifyAddress; import org.junit.*; @@ -12,7 +12,7 @@ import static com.taosdata.jdbc.TSDBConstants.TIMESTAMP_DATA_OUT_OF_RANGE; @FixMethodOrder -public class WSPreparedStatementNsTest { +public class WsPstmtNsTest { String host = "localhost"; String db_name = "ws_prepare_ns"; String tableName = "wpt"; diff --git a/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtStmt2Test.java b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtStmt2Test.java new file mode 100644 index 00000000..e01c19ff --- /dev/null +++ b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtStmt2Test.java @@ -0,0 +1,366 @@ +package com.taosdata.jdbc.ws.stmt; + +import com.taosdata.jdbc.utils.SpecifyAddress; +import com.taosdata.jdbc.ws.TSWSPreparedStatement; +import org.junit.*; + +import java.sql.*; +import java.util.*; +import java.util.Random; + +@FixMethodOrder +public class WsPstmtStmt2Test { + String host = "vm98"; + String db_name = "ws_prepare"; + String tableName = "wpt"; + String superTable = "wpt_st"; + Connection connection; + + int numOfSubTable = 10; + int numOfRow = 10; + private static final Random random = new Random(System.currentTimeMillis()); + + @Test + public void testStmt2Insert() throws SQLException { + String sql = "INSERT INTO ? USING " + db_name + "." + tableName + " TAGS(?,?) VALUES (?,?,?,?)"; + + try (TSWSPreparedStatement pstmt = connection.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { + + for (int i = 1; i <= numOfSubTable; i++) { + //set table name + pstmt.setTableName("d_bind_" + i); + + // set tags + pstmt.setTagInt(0, i); + pstmt.setTagString(1, "location_" + i); + + // set column ts + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + // set column current + ArrayList currentList = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + currentList.add(random.nextFloat() * 30); + pstmt.setFloat(1, currentList); + + // set column voltage + ArrayList voltageList = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + voltageList.add(random.nextInt(300)); + pstmt.setInt(2, voltageList); + + // set column phase + ArrayList phaseList = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + phaseList.add(random.nextFloat()); + pstmt.setFloat(3, phaseList); + // add column + pstmt.columnDataAddBatch(); + } + // execute column + pstmt.columnDataExecuteBatch(); + // you can check exeResult here + System.out.println("Successfully inserted " + (numOfSubTable * numOfRow) + " rows to power.meters."); + } catch (Exception ex) { + // please refer to the JDBC specifications for detailed exceptions info + System.out.printf("Failed to insert to table meters using stmt, %sErrMessage: %s%n", + ex instanceof SQLException ? "ErrCode: " + ((SQLException) ex).getErrorCode() + ", " : "", + ex.getMessage()); + // Print stack trace for context in examples. Use logging in production. + ex.printStackTrace(); + throw ex; + } + } + + @Test + public void testStmt2InsertExtend() throws SQLException { + String sql = "INSERT INTO " + db_name + "." + tableName + " (tbname, ts, current, voltage, phase) VALUES (?,?,?,?,?)"; + + try (TSWSPreparedStatement pstmt = connection.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { + + for (int i = 1; i <= numOfSubTable; i++) { + //set table name + pstmt.setTableName("d_bind_" + i); + + // set column ts + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + // set column current + ArrayList currentList = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + currentList.add(random.nextFloat() * 30); + pstmt.setFloat(1, currentList); + + // set column voltage + ArrayList voltageList = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + voltageList.add(random.nextInt(300)); + pstmt.setInt(2, voltageList); + + // set column phase + ArrayList phaseList = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + phaseList.add(random.nextFloat()); + pstmt.setFloat(3, phaseList); + // add column + pstmt.columnDataAddBatch(); + } + // execute column + pstmt.columnDataExecuteBatch(); + // you can check exeResult here + System.out.println("Successfully inserted " + (numOfSubTable * numOfRow) + " rows to power.meters."); + } catch (Exception ex) { + // please refer to the JDBC specifications for detailed exceptions info + System.out.printf("Failed to insert to table meters using stmt, %sErrMessage: %s%n", + ex instanceof SQLException ? "ErrCode: " + ((SQLException) ex).getErrorCode() + ", " : "", + ex.getMessage()); + // Print stack trace for context in examples. Use logging in production. + ex.printStackTrace(); + throw ex; + } + } + + @Test + public void testStmt2InsertMixedApi() throws SQLException { + String sql = "INSERT INTO ? USING " + db_name + "." + tableName + " TAGS(?,?) VALUES (?,?,?,?)"; + + try (TSWSPreparedStatement pstmt = connection.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { + + for (int i = 1; i <= numOfSubTable; i++) { + //set table name + pstmt.setTableName("d_bind_" + i); + + // set tags + pstmt.setTagInt(0, i); + pstmt.setTagString(1, "location_" + i); + + // set columns + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) { + pstmt.setTimestamp(1, new Timestamp(current + j)); + pstmt.setFloat(2, random.nextFloat() * 30); + pstmt.setInt(3, random.nextInt(300)); + pstmt.setFloat(4, random.nextFloat()); + pstmt.addBatch(); + } + int[] exeResult = pstmt.executeBatch(); + + for (int ele : exeResult){ + Assert.assertEquals(ele, Statement.SUCCESS_NO_INFO); + } + } + // you can check exeResult here + System.out.println("Successfully inserted " + (numOfSubTable * numOfRow) + " rows to power.meters."); + } catch (Exception ex) { + // please refer to the JDBC specifications for detailed exceptions info + System.out.printf("Failed to insert to table meters using stmt, %sErrMessage: %s%n", + ex instanceof SQLException ? "ErrCode: " + ((SQLException) ex).getErrorCode() + ", " : "", + ex.getMessage()); + // Print stack trace for context in examples. Use logging in production. + ex.printStackTrace(); + throw ex; + } + } + + @Test + public void testStmt2InsertSubTable() throws SQLException { + String sql = "INSERT INTO stb1 USING " + db_name + "." + tableName + " TAGS(?,?) VALUES (?,?,?,?)"; + + try (TSWSPreparedStatement pstmt = connection.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { + + for (int i = 1; i <= 10; i++) { + // set tags + pstmt.setTagInt(0, 1); + pstmt.setTagString(1, "location_" + 1); + + // set columns + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) { + pstmt.setTimestamp(1, new Timestamp(current + j)); + pstmt.setFloat(2, random.nextFloat() * 30); + pstmt.setInt(3, random.nextInt(300)); + pstmt.setFloat(4, random.nextFloat()); + pstmt.addBatch(); + } + int[] exeResult = pstmt.executeBatch(); + + for (int ele : exeResult){ + Assert.assertEquals(ele, Statement.SUCCESS_NO_INFO); + } + } + // you can check exeResult here + System.out.println("Successfully inserted " + (numOfSubTable * numOfRow) + " rows to power.meters."); + } catch (Exception ex) { + // please refer to the JDBC specifications for detailed exceptions info + System.out.printf("Failed to insert to table meters using stmt, %sErrMessage: %s%n", + ex instanceof SQLException ? "ErrCode: " + ((SQLException) ex).getErrorCode() + ", " : "", + ex.getMessage()); + // Print stack trace for context in examples. Use logging in production. + ex.printStackTrace(); + throw ex; + } + } + + @Test + public void testStmt2InsertStdApi() throws SQLException { + String sql = "INSERT INTO " + db_name + "." + tableName + "(tbname, groupId, location, ts, current, voltage, phase) VALUES (?,?,?,?,?,?,?)"; + + try (PreparedStatement pstmt = connection.prepareStatement(sql)) { + + for (int i = 1; i <= numOfSubTable; i++) { + // set columns + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) { + pstmt.setString(1, "d_bind_" + i); + pstmt.setInt(2, i); + pstmt.setString(3, "location_" + i); + + pstmt.setTimestamp(4, new Timestamp(current + j)); + pstmt.setFloat(5, random.nextFloat() * 30); + pstmt.setInt(6, random.nextInt(300)); + pstmt.setFloat(7, random.nextFloat()); + pstmt.addBatch(); + } + int[] exeResult = pstmt.executeBatch(); + + for (int ele : exeResult){ + Assert.assertEquals(ele, Statement.SUCCESS_NO_INFO); + } + } + // you can check exeResult here + System.out.println("Successfully inserted " + (numOfSubTable * numOfRow) + " rows to power.meters."); + } catch (Exception ex) { + // please refer to the JDBC specifications for detailed exceptions info + System.out.printf("Failed to insert to table meters using stmt, %sErrMessage: %s%n", + ex instanceof SQLException ? "ErrCode: " + ((SQLException) ex).getErrorCode() + ", " : "", + ex.getMessage()); + // Print stack trace for context in examples. Use logging in production. + ex.printStackTrace(); + throw ex; + } + } + + @Test + public void testStmt2InsertStdApiWithEscapeChar() throws SQLException { + String sql = "INSERT INTO `" + db_name + "`.`" + tableName + "` (tbname, groupId, location, ts, current, voltage, phase) VALUES (?,?,?,?,?,?,?)"; + + try (PreparedStatement pstmt = connection.prepareStatement(sql)) { + + for (int i = 1; i <= numOfSubTable; i++) { + // set columns + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) { + pstmt.setString(1, "d_bind_" + i); + pstmt.setInt(2, i); + pstmt.setString(3, "location_" + i); + + pstmt.setTimestamp(4, new Timestamp(current + j)); + pstmt.setFloat(5, random.nextFloat() * 30); + pstmt.setInt(6, random.nextInt(300)); + pstmt.setFloat(7, random.nextFloat()); + pstmt.addBatch(); + } + int[] exeResult = pstmt.executeBatch(); + + for (int ele : exeResult){ + Assert.assertEquals(ele, Statement.SUCCESS_NO_INFO); + } + } + // you can check exeResult here + System.out.println("Successfully inserted " + (numOfSubTable * numOfRow) + " rows to power.meters."); + } catch (Exception ex) { + // please refer to the JDBC specifications for detailed exceptions info + System.out.printf("Failed to insert to table meters using stmt, %sErrMessage: %s%n", + ex instanceof SQLException ? "ErrCode: " + ((SQLException) ex).getErrorCode() + ", " : "", + ex.getMessage()); + // Print stack trace for context in examples. Use logging in production. + ex.printStackTrace(); + throw ex; + } + } + @Test + public void testStmt2InsertStdApiNoTag() throws SQLException { + // create sub table first + String createSql = "create table"; + for (int i = 1; i <= numOfSubTable; i++) { + createSql += " if not exists d_bind_" + i + " using " + db_name + "." + tableName + " tags(" + i + ", \"location_" + i + "\")"; + } + try (Statement stmt = connection.createStatement()) { + stmt.execute(createSql); + } catch (Exception ex) { + System.out.printf("Failed to create sub table, %sErrMessage: %s%n", + ex instanceof SQLException ? "ErrCode: " + ((SQLException) ex).getErrorCode() + ", " : "", + ex.getMessage()); + ex.printStackTrace(); + throw ex; + } + + String sql = "INSERT INTO " + db_name + "." + tableName + "(tbname, ts, current, voltage, phase) VALUES (?,?,?,?,?)"; + + try (TSWSPreparedStatement pstmt = connection.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)) { + + for (int i = 1; i <= numOfSubTable; i++) { + // set columns + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) { + pstmt.setString(1, "d_bind_" + i); + + pstmt.setTimestamp(2, new Timestamp(current + j)); + pstmt.setFloat(3, random.nextFloat() * 30); + pstmt.setInt(4, random.nextInt(300)); + pstmt.setFloat(5, random.nextFloat()); + pstmt.addBatch(); + } + int[] exeResult = pstmt.executeBatch(); + + for (int ele : exeResult){ + Assert.assertEquals(ele, Statement.SUCCESS_NO_INFO); + } + } + // you can check exeResult here + System.out.println("Successfully inserted " + (numOfSubTable * numOfRow) + " rows to power.meters."); + } catch (Exception ex) { + // please refer to the JDBC specifications for detailed exceptions info + System.out.printf("Failed to insert to table meters using stmt, %sErrMessage: %s%n", + ex instanceof SQLException ? "ErrCode: " + ((SQLException) ex).getErrorCode() + ", " : "", + ex.getMessage()); + // Print stack trace for context in examples. Use logging in production. + ex.printStackTrace(); + throw ex; + } + } + + @Before + public void before() throws SQLException { + String url = SpecifyAddress.getInstance().getWebSocketWithoutUrl(); + if (url == null) { + url = "jdbc:TAOS-WS://" + host + ":6041/?user=root&password=taosdata"; + } else { + url += "?user=root&password=taosdata&batchfetch=true"; + } + Properties properties = new Properties(); + connection = DriverManager.getConnection(url, properties); + Statement statement = connection.createStatement(); + statement.execute("drop database if exists " + db_name); + statement.execute("create database " + db_name); + statement.execute("use " + db_name); + statement.execute("create stable if not exists " + db_name + "." + tableName + " (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); + statement.close(); + } + + @After + public void after() throws SQLException { + try (Statement statement = connection.createStatement()) { + statement.execute("drop database if exists " + db_name); + } + connection.close(); + } +} diff --git a/src/test/java/com/taosdata/jdbc/ws/TaosPrepareStatementTest.java b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtSubTableTest.java similarity index 97% rename from src/test/java/com/taosdata/jdbc/ws/TaosPrepareStatementTest.java rename to src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtSubTableTest.java index 2c5c2bf8..081242ee 100644 --- a/src/test/java/com/taosdata/jdbc/ws/TaosPrepareStatementTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtSubTableTest.java @@ -1,6 +1,7 @@ -package com.taosdata.jdbc.ws; +package com.taosdata.jdbc.ws.stmt; import com.taosdata.jdbc.utils.SpecifyAddress; +import com.taosdata.jdbc.ws.TSWSPreparedStatement; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -9,7 +10,7 @@ import java.sql.*; import java.util.Properties; -public class TaosPrepareStatementTest { +public class WsPstmtSubTableTest { String host = "127.0.0.1"; String db_name = "ws_prepare_taos"; String superTable = "wpt_st"; @@ -90,7 +91,7 @@ public void testInsertSubTable() throws SQLException { " \"t10\": \"2021-01-01 00:00:00.000\",\n" + " \"t11\": \"taosdata\"\n" + " }"; // json string - statement.setTagJson(1, tagJson); + statement.setTagJson(0, tagJson); statement.setTimestamp(1, new Timestamp(System.currentTimeMillis())); statement.setInt(2, 100); statement.executeUpdate(); diff --git a/src/test/java/com/taosdata/jdbc/ws/WSPreparedStatementTest.java b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtTest.java similarity index 98% rename from src/test/java/com/taosdata/jdbc/ws/WSPreparedStatementTest.java rename to src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtTest.java index 29892678..7fdc7360 100644 --- a/src/test/java/com/taosdata/jdbc/ws/WSPreparedStatementTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/stmt/WsPstmtTest.java @@ -1,6 +1,7 @@ -package com.taosdata.jdbc.ws; +package com.taosdata.jdbc.ws.stmt; import com.taosdata.jdbc.utils.SpecifyAddress; +import com.taosdata.jdbc.ws.TSWSPreparedStatement; import org.junit.*; import java.io.InputStream; @@ -13,10 +14,8 @@ import java.util.HashSet; import java.util.Properties; -import static com.taosdata.jdbc.TSDBConstants.*; - @FixMethodOrder -public class WSPreparedStatementTest { +public class WsPstmtTest { String host = "127.0.0.1"; String db_name = "ws_prepare"; String tableName = "wpt"; From 738f3a0d91e8895c5ff600982eaebd4adacc49b7 Mon Sep 17 00:00:00 2001 From: sheyanjie-qq <249478495@qq.com> Date: Fri, 27 Dec 2024 13:33:04 +0800 Subject: [PATCH 4/4] remove tmq timezone support --- .../jdbc/utils/DataTypeConverUtil.java | 31 ------------------- .../taosdata/jdbc/utils/DateTimeUtils.java | 26 ---------------- .../com/taosdata/jdbc/ws/tmq/WSConsumer.java | 5 +-- .../jdbc/ws/tmq/WSConsumerResultSet.java | 7 ++--- .../jdbc/ws/WSConsumerResultSetTest.java | 2 +- 5 files changed, 4 insertions(+), 67 deletions(-) diff --git a/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java b/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java index 42895128..90553ce2 100644 --- a/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java +++ b/src/main/java/com/taosdata/jdbc/utils/DataTypeConverUtil.java @@ -590,35 +590,4 @@ static public Object parseValue(int type, Object source){ return null; } } - - - -// public static Timestamp parseTimestampColumnDataWithZoneId(long value, int timestampPrecision, ZoneId zoneId) { -// if (zoneId == null){ -// return parseTimestampColumnData(value, timestampPrecision); -// } -// -// if (TimestampPrecision.MS == timestampPrecision) { -// Instant instant = Instant.ofEpochMilli(value); -// LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); -// return Timestamp.valueOf(localDateTime); -// } -// -// if (TimestampPrecision.US == timestampPrecision) { -// long epochSec = value / 1000_000L; -// long nanoAdjustment = value % 1000_000L * 1000L; -// Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment); -// LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); -// return Timestamp.valueOf(localDateTime); -// } -// if (TimestampPrecision.NS == timestampPrecision) { -// long epochSec = value / 1000_000_000L; -// long nanoAdjustment = value % 1000_000_000L; -// Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment); -// LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); -// return Timestamp.valueOf(localDateTime); -// } -// return null; -// } - } diff --git a/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java b/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java index ad294cd3..18c94729 100644 --- a/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java +++ b/src/main/java/com/taosdata/jdbc/utils/DateTimeUtils.java @@ -115,32 +115,6 @@ public static Long toLong(Instant instant, int precision) { return v; } -// public static ZonedDateTime getZonedDateTime(Timestamp timestamp, ZoneId zoneId) { -// if (zoneId == null) { -// return ; -// } -// -// TimeZone calTimeZone = cal.getTimeZone(); -// ZoneId zoneId = calTimeZone.toZoneId(); -// -// return toUTC(timestamp, zoneId); -// } -// public static OffsetDateTime getOffsetDateTime(Timestamp timestamp) { -// if (zoneId == null) { -// return timestamp; -// } -// -// Instant instant = timestamp.toInstant(); -// -// ZoneOffset systemOffset = systemZoneId.getRules().getOffset(instant); -// -// // 将 Instant 转换为 OffsetDateTime -// OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(instant, systemOffset); -// return offsetDateTime; -// } - - - public static Instant parseTimestampColumnData(long value, int timestampPrecision) { if (TimestampPrecision.MS == timestampPrecision) return Instant.ofEpochMilli(value); diff --git a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumer.java b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumer.java index a5c20eb7..cf129716 100644 --- a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumer.java +++ b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumer.java @@ -36,13 +36,10 @@ public class WSConsumer implements Consumer { private long messageId = 0L; private Collection topics; - private ZoneId zoneId = null; - @Override public void create(Properties properties) throws SQLException { factory = new TMQRequestFactory(); param = new ConsumerParam(properties); - this.zoneId = param.getConnectionParam().getZoneId(); InFlightRequest inFlightRequest = new InFlightRequest(param.getConnectionParam().getRequestTimeout() , param.getConnectionParam().getMaxRequest()); transport = new Transport(WSFunction.TMQ, param.getConnectionParam(), inFlightRequest); @@ -149,7 +146,7 @@ private ConsumerRecords doPoll(Duration timeout, Deserializer deserializer ConsumerRecords records = new ConsumerRecords<>(); - try (WSConsumerResultSet rs = new WSConsumerResultSet(transport, factory, pollResp.getMessageId(), pollResp.getDatabase(), zoneId)) { + try (WSConsumerResultSet rs = new WSConsumerResultSet(transport, factory, pollResp.getMessageId(), pollResp.getDatabase())) { while (rs.next()) { String topic = pollResp.getTopic(); String dbName = pollResp.getDatabase(); diff --git a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java index 1c1a69f3..daa87ea0 100644 --- a/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java +++ b/src/main/java/com/taosdata/jdbc/ws/tmq/WSConsumerResultSet.java @@ -48,14 +48,11 @@ public class WSConsumerResultSet extends AbstractResultSet { protected int numOfRows = 0; protected int rowIndex = 0; - private final ZoneId zoneId; - - public WSConsumerResultSet(Transport transport, TMQRequestFactory factory, long messageId, String database, ZoneId zoneId) { + public WSConsumerResultSet(Transport transport, TMQRequestFactory factory, long messageId, String database) { this.transport = transport; this.factory = factory; this.messageId = messageId; this.database = database; - this.zoneId = zoneId; } private boolean forward() { @@ -274,7 +271,7 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { return (Timestamp) value; if (value instanceof Long) { Instant instant = DateTimeUtils.parseTimestampColumnData((long) value, this.timestampPrecision); - return DateTimeUtils.getTimestamp(instant, zoneId); + return DateTimeUtils.getTimestamp(instant, null); } Timestamp ret; try { diff --git a/src/test/java/com/taosdata/jdbc/ws/WSConsumerResultSetTest.java b/src/test/java/com/taosdata/jdbc/ws/WSConsumerResultSetTest.java index 6b246edd..1435360c 100644 --- a/src/test/java/com/taosdata/jdbc/ws/WSConsumerResultSetTest.java +++ b/src/test/java/com/taosdata/jdbc/ws/WSConsumerResultSetTest.java @@ -16,7 +16,7 @@ public class WSConsumerResultSetTest { @Before public void setUp() { - wsConsumerResultSet = new WSConsumerResultSet(null, null, 0, null, null); + wsConsumerResultSet = new WSConsumerResultSet(null, null, 0, null); } @Test