From 992ec97ed12f8ed54cfc5488c35122e034ddc053 Mon Sep 17 00:00:00 2001 From: huolibo Date: Mon, 21 Feb 2022 16:18:27 +0800 Subject: [PATCH] [TD-11946]: implement jdbc with websocket and get/parse JSON resultSet (#10221) * [TD-11946]: implement the JDBC framwork * [TD-11946]: Get the resultset and parse the resultset add fetch file * remove useless file * [TD-11946]: add close method release resource * [TD-11946]: remove redundant code * [TD-11946]: complete websocket parse block data, remove json format * [TD-11946]: ignore websocket test --- .../com/taosdata/jdbc/rs/RestfulDriver.java | 13 +- .../taosdata/jdbc/rs/RestfulResultSet.java | 3 + .../taosdata/jdbc/ws/AbstractWSResultSet.java | 146 ++ .../com/taosdata/jdbc/ws/BlockResultSet.java | 626 ++++++++ .../com/taosdata/jdbc/ws/InFlightRequest.java | 59 +- .../com/taosdata/jdbc/ws/ResponseFuture.java | 22 +- .../java/com/taosdata/jdbc/ws/Transport.java | 8 +- .../java/com/taosdata/jdbc/ws/WSClient.java | 62 +- .../com/taosdata/jdbc/ws/WSConnection.java | 10 +- .../com/taosdata/jdbc/ws/WSStatement.java | 112 ++ .../com/taosdata/jdbc/ws/entity/Action.java | 6 +- .../com/taosdata/jdbc/ws/entity/Code.java | 1 - .../jdbc/ws/entity/FetchBlockResp.java | 19 +- .../jdbc/ws/entity/FetchJsonResp.java | 8 +- .../com/taosdata/jdbc/ws/entity/FetchReq.java | 18 + .../taosdata/jdbc/ws/entity/FetchResp.java | 6 +- .../com/taosdata/jdbc/ws/entity/IdUtil.java | 20 - .../com/taosdata/jdbc/ws/entity/Payload.java | 16 + .../com/taosdata/jdbc/ws/entity/QueryReq.java | 18 + .../com/taosdata/jdbc/ws/entity/Request.java | 123 +- .../jdbc/ws/entity/RequestFactory.java | 48 + .../com/taosdata/jdbc/ws/entity/Response.java | 4 - .../taosdata/jdbc/ws/WSConnectionTest.java | 28 +- .../com/taosdata/jdbc/ws/WSJsonTagTest.java | 1283 +++++++++++++++++ .../com/taosdata/jdbc/ws/WSQueryTest.java | 62 + .../com/taosdata/jdbc/ws/WSSelectTest.java | 83 ++ 26 files changed, 2600 insertions(+), 204 deletions(-) create mode 100644 src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/AbstractWSResultSet.java create mode 100644 src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java create mode 100644 src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSStatement.java create mode 100644 src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchReq.java delete mode 100644 src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/IdUtil.java create mode 100644 src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Payload.java create mode 100644 src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/QueryReq.java create mode 100644 src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/RequestFactory.java create mode 100644 src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSJsonTagTest.java create mode 100644 src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSQueryTest.java create mode 100644 src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSSelectTest.java diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulDriver.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulDriver.java index eb51da8aff..888f58856a 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulDriver.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulDriver.java @@ -3,6 +3,7 @@ package com.taosdata.jdbc.rs; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.taosdata.jdbc.*; +import com.taosdata.jdbc.enums.TimestampFormat; import com.taosdata.jdbc.utils.HttpClientPoolUtil; import com.taosdata.jdbc.ws.InFlightRequest; import com.taosdata.jdbc.ws.Transport; @@ -77,18 +78,20 @@ public class RestfulDriver extends AbstractDriver { int maxRequest = props.containsKey(TSDBDriver.PROPERTY_KEY_MAX_CONCURRENT_REQUEST) ? Integer.parseInt(props.getProperty(TSDBDriver.PROPERTY_KEY_MAX_CONCURRENT_REQUEST)) : Transport.DEFAULT_MAX_REQUEST; + InFlightRequest inFlightRequest = new InFlightRequest(timeout, maxRequest); CountDownLatch latch = new CountDownLatch(1); Map httpHeaders = new HashMap<>(); - client = new WSClient(new URI(loginUrl), user, password, database, inFlightRequest, httpHeaders, latch, maxRequest); + client = new WSClient(new URI(loginUrl), user, password, database, + inFlightRequest, httpHeaders, latch, maxRequest); transport = new Transport(client, inFlightRequest); - if (!client.connectBlocking()) { + if (!client.connectBlocking(timeout, TimeUnit.MILLISECONDS)) { throw new SQLException("can't create connection with server"); } if (!latch.await(timeout, TimeUnit.MILLISECONDS)) { throw new SQLException("auth timeout"); } - if (client.isAuth()) { + if (!client.isAuth()) { throw new SQLException("auth failure"); } } catch (URISyntaxException e) { @@ -96,7 +99,9 @@ public class RestfulDriver extends AbstractDriver { } catch (InterruptedException e) { throw new SQLException("creat websocket connection has been Interrupted ", e); } - return new WSConnection(url, props, transport, database, true); + // TODO fetch Type from config + props.setProperty(TSDBDriver.PROPERTY_KEY_TIMESTAMP_FORMAT, String.valueOf(TimestampFormat.TIMESTAMP)); + return new WSConnection(url, props, transport, database); } loginUrl = "http://" + props.getProperty(TSDBDriver.PROPERTY_KEY_HOST) + ":" + props.getProperty(TSDBDriver.PROPERTY_KEY_PORT) + "/rest/login/" + user + "/" + password + ""; int poolSize = Integer.parseInt(props.getProperty("httpPoolSize", HttpClientPoolUtil.DEFAULT_MAX_PER_ROUTE)); diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java index 47c5232d11..599ee85a6b 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/rs/RestfulResultSet.java @@ -302,6 +302,9 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet { this.taos_type = taos_type; } + public int getTaosType() { + return taos_type; + } } @Override diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/AbstractWSResultSet.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/AbstractWSResultSet.java new file mode 100644 index 0000000000..2325161d68 --- /dev/null +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/AbstractWSResultSet.java @@ -0,0 +1,146 @@ +package com.taosdata.jdbc.ws; + +import com.taosdata.jdbc.*; +import com.taosdata.jdbc.rs.RestfulResultSet; +import com.taosdata.jdbc.rs.RestfulResultSetMetaData; +import com.taosdata.jdbc.ws.entity.*; + +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.time.chrono.IsoChronology; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.ResolverStyle; +import java.time.temporal.ChronoField; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public abstract class AbstractWSResultSet extends AbstractResultSet { + public static DateTimeFormatter rfc3339Parser = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .appendValue(ChronoField.YEAR, 4) + .appendLiteral('-') + .appendValue(ChronoField.MONTH_OF_YEAR, 2) + .appendLiteral('-') + .appendValue(ChronoField.DAY_OF_MONTH, 2) + .appendLiteral('T') + .appendValue(ChronoField.HOUR_OF_DAY, 2) + .appendLiteral(':') + .appendValue(ChronoField.MINUTE_OF_HOUR, 2) + .appendLiteral(':') + .appendValue(ChronoField.SECOND_OF_MINUTE, 2) + .optionalStart() + .appendFraction(ChronoField.NANO_OF_SECOND, 2, 9, true) + .optionalEnd() + .appendOffset("+HH:MM", "Z").toFormatter() + .withResolverStyle(ResolverStyle.STRICT) + .withChronology(IsoChronology.INSTANCE); + + protected final Statement statement; + protected final Transport transport; + protected final RequestFactory factory; + protected final long queryId; + + protected boolean isClosed; + // meta + protected final ResultSetMetaData metaData; + protected final List fields = new ArrayList<>(); + protected final List columnNames; + protected List fieldLength; + // data + protected List> result = new ArrayList<>(); + + protected int numOfRows = 0; + protected int rowIndex = 0; + private boolean isCompleted; + + public AbstractWSResultSet(Statement statement, Transport transport, RequestFactory factory, + QueryResp response, String database) throws SQLException { + this.statement = statement; + this.transport = transport; + this.factory = factory; + this.queryId = response.getId(); + columnNames = Arrays.asList(response.getFieldsNames()); + for (int i = 0; i < response.getFieldsCount(); i++) { + String colName = response.getFieldsNames()[i]; + int taosType = response.getFieldsTypes()[i]; + int jdbcType = TSDBConstants.taosType2JdbcType(taosType); + int length = response.getFieldsLengths()[i]; + fields.add(new RestfulResultSet.Field(colName, jdbcType, length, "", taosType)); + } + this.metaData = new RestfulResultSetMetaData(database, fields, null); + this.timestampPrecision = response.getPrecision(); + } + + private boolean forward() { + if (this.rowIndex > this.numOfRows) { + return false; + } + + return ((++this.rowIndex) < this.numOfRows); + } + + public void reset() { + this.rowIndex = 0; + } + + @Override + public boolean next() throws SQLException { + if (isClosed()) { + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + } + + if (this.forward()) { + return true; + } + + Request request = factory.generateFetch(queryId); + CompletableFuture send = transport.send(request); + try { + Response response = send.get(); + FetchResp fetchResp = (FetchResp) response; + if (Code.SUCCESS.getCode() != fetchResp.getCode()) { +// TODO reWrite error type + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNKNOWN, fetchResp.getMessage()); + } + this.reset(); + if (fetchResp.isCompleted()) { + this.isCompleted = true; + return false; + } + fieldLength = Arrays.asList(fetchResp.getLengths()); + this.numOfRows = fetchResp.getRows(); + this.result = fetchJsonData(); + return true; + } catch (InterruptedException | ExecutionException e) { + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESTFul_Client_IOException, e.getMessage()); + } + } + + public abstract List> fetchJsonData() throws SQLException, ExecutionException, InterruptedException; + + @Override + public void close() throws SQLException { + this.isClosed = true; + if (result != null && !result.isEmpty() && !isCompleted) { + FetchReq fetchReq = new FetchReq(queryId, queryId); + transport.sendWithoutRep(new Request(Action.FREE_RESULT.getAction(), fetchReq)); + } + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + return this.metaData; + } + + @Override + public boolean isClosed() throws SQLException { + return isClosed; + } +} diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java new file mode 100644 index 0000000000..8371b9e7c4 --- /dev/null +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/BlockResultSet.java @@ -0,0 +1,626 @@ +package com.taosdata.jdbc.ws; + +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import com.google.common.primitives.Shorts; +import com.taosdata.jdbc.TSDBConstants; +import com.taosdata.jdbc.TSDBDriver; +import com.taosdata.jdbc.TSDBError; +import com.taosdata.jdbc.TSDBErrorNumbers; +import com.taosdata.jdbc.enums.TimestampFormat; +import com.taosdata.jdbc.enums.TimestampPrecision; +import com.taosdata.jdbc.utils.Utils; +import com.taosdata.jdbc.ws.entity.*; + +import java.math.BigDecimal; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.sql.*; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static com.taosdata.jdbc.TSDBConstants.*; + +public class BlockResultSet extends AbstractWSResultSet { + + public BlockResultSet(Statement statement, Transport transport, RequestFactory factory, + QueryResp response, String database) throws SQLException { + super(statement, transport, factory, response, database); + } + + @Override + public List> fetchJsonData() throws SQLException, ExecutionException, InterruptedException { + Request blockRequest = factory.generateFetchBlock(queryId); + CompletableFuture fetchFuture = transport.send(blockRequest); + FetchBlockResp resp = (FetchBlockResp) fetchFuture.get(); + ByteBuffer buffer = resp.getBuffer(); + List> list = new ArrayList<>(); + if (resp.getBuffer() != null) { + for (int i = 0; i < fields.size(); i++) { + List col = new ArrayList<>(numOfRows); + int type = fields.get(i).getTaosType(); + switch (type) { + case TSDB_DATA_TYPE_BOOL: + for (int j = 0; j < numOfRows; j++) { + col.add(buffer.get() == 1); + } + break; + case TSDB_DATA_TYPE_UTINYINT: + case TSDB_DATA_TYPE_TINYINT: + for (int j = 0; j < numOfRows; j++) { + col.add(buffer.get()); + } + break; + case TSDB_DATA_TYPE_USMALLINT: + case TSDB_DATA_TYPE_SMALLINT: + for (int j = 0; j < numOfRows; j++) { + col.add(buffer.getShort()); + } + break; + case TSDB_DATA_TYPE_UINT: + case TSDB_DATA_TYPE_INT: + for (int j = 0; j < numOfRows; j++) { + col.add(buffer.getInt()); + } + break; + case TSDB_DATA_TYPE_UBIGINT: + case TSDB_DATA_TYPE_BIGINT: + for (int j = 0; j < numOfRows; j++) { + col.add(buffer.getLong()); + } + break; + case TSDB_DATA_TYPE_FLOAT: + for (int j = 0; j < numOfRows; j++) { + col.add(buffer.getFloat()); + } + break; + case TSDB_DATA_TYPE_DOUBLE: + for (int j = 0; j < numOfRows; j++) { + col.add(buffer.getDouble()); + } + break; + case TSDB_DATA_TYPE_BINARY: { + byte[] bytes = new byte[fieldLength.get(i) - 2]; + for (int j = 0; j < numOfRows; j++) { + short s = buffer.getShort(); + buffer.get(bytes); + col.add(Arrays.copyOf(bytes, s)); + } + break; + } + case TSDB_DATA_TYPE_NCHAR: + case TSDB_DATA_TYPE_JSON: { + byte[] bytes = new byte[fieldLength.get(i) - 2]; + for (int j = 0; j < numOfRows; j++) { + short s = buffer.getShort(); + buffer.get(bytes); + col.add(new String(Arrays.copyOf(bytes, s), StandardCharsets.UTF_8)); + } + break; + } + case TSDB_DATA_TYPE_TIMESTAMP: { + byte[] bytes = new byte[fieldLength.get(i)]; + for (int j = 0; j < numOfRows; j++) { + buffer.get(bytes); + col.add(parseTimestampColumnData(bytes)); + } + break; + } + default: + break; + } + list.add(col); + } + } + return list; + } + + public static long bytesToLong(byte[] bytes) { + ByteBuffer buffer = ByteBuffer.allocate(8); + buffer.put(bytes, 0, bytes.length); + buffer.flip();//need flip + buffer.order(ByteOrder.LITTLE_ENDIAN); + return buffer.getLong(); + } + + private Timestamp parseTimestampColumnData(byte[] bytes) throws SQLException { + if (bytes == null || bytes.length < 1) + return null; + String tsFormatUpperCase = this.statement.getConnection().getClientInfo(TSDBDriver.PROPERTY_KEY_TIMESTAMP_FORMAT).toUpperCase(); + TimestampFormat timestampFormat = TimestampFormat.valueOf(tsFormatUpperCase); + switch (timestampFormat) { + case TIMESTAMP: { + long value = bytesToLong(bytes); + if (TimestampPrecision.MS == this.timestampPrecision) + return new Timestamp(value); + + if (TimestampPrecision.US == this.timestampPrecision) { + long epochSec = value / 1000_000L; + long nanoAdjustment = value % 1000_000L * 1000L; + return Timestamp.from(Instant.ofEpochSecond(epochSec, nanoAdjustment)); + } + if (TimestampPrecision.NS == this.timestampPrecision) { + long epochSec = value / 1000_000_000L; + long nanoAdjustment = value % 1000_000_000L; + return Timestamp.from(Instant.ofEpochSecond(epochSec, nanoAdjustment)); + } + } + case UTC: { + String value = new String(bytes); + if (value.lastIndexOf(":") > 19) { + ZonedDateTime parse = ZonedDateTime.parse(value, rfc3339Parser); + return Timestamp.from(parse.toInstant()); + } else { + long epochSec = Timestamp.valueOf(value.substring(0, 19).replace("T", " ")).getTime() / 1000; + int fractionalSec = Integer.parseInt(value.substring(20, value.length() - 5)); + long nanoAdjustment; + if (TimestampPrecision.NS == this.timestampPrecision) { + // ns timestamp: yyyy-MM-ddTHH:mm:ss.SSSSSSSSS+0x00 + nanoAdjustment = fractionalSec; + } else if (TimestampPrecision.US == this.timestampPrecision) { + // ms timestamp: yyyy-MM-ddTHH:mm:ss.SSSSSS+0x00 + nanoAdjustment = fractionalSec * 1000L; + } else { + // ms timestamp: yyyy-MM-ddTHH:mm:ss.SSS+0x00 + nanoAdjustment = fractionalSec * 1000_000L; + } + ZoneOffset zoneOffset = ZoneOffset.of(value.substring(value.length() - 5)); + Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment).atOffset(zoneOffset).toInstant(); + return Timestamp.from(instant); + } + } + case STRING: + default: { + String value = new String(bytes, StandardCharsets.UTF_8); + if (TimestampPrecision.MS == this.timestampPrecision) { + // ms timestamp: yyyy-MM-dd HH:mm:ss.SSS + return Timestamp.valueOf(value); + } + if (TimestampPrecision.US == this.timestampPrecision) { + // us timestamp: yyyy-MM-dd HH:mm:ss.SSSSSS + long epochSec = Timestamp.valueOf(value.substring(0, 19)).getTime() / 1000; + long nanoAdjustment = Integer.parseInt(value.substring(20)) * 1000L; + return Timestamp.from(Instant.ofEpochSecond(epochSec, nanoAdjustment)); + } + if (TimestampPrecision.NS == this.timestampPrecision) { + // ms timestamp: yyyy-MM-dd HH:mm:ss.SSSSSSSSS + long epochSec = Timestamp.valueOf(value.substring(0, 19)).getTime() / 1000; + long nanoAdjustment = Integer.parseInt(value.substring(20)); + return Timestamp.from(Instant.ofEpochSecond(epochSec, nanoAdjustment)); + } + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNKNOWN_TIMESTAMP_PRECISION); + } + } + } + + @Override + public String getString(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return null; + if (value instanceof String) + return (String) value; + if (value instanceof byte[]) + return new String((byte[]) value); + return value.toString(); + } + + @Override + public boolean getBoolean(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return false; + if (value instanceof Boolean) + return (boolean) value; + return Boolean.parseBoolean(value.toString()); + } + + @Override + public byte getByte(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return 0; + if (value instanceof Byte) + return (byte) value; + long valueAsLong = Long.parseLong(value.toString()); + if (valueAsLong == Byte.MIN_VALUE) + return 0; + if (valueAsLong < Byte.MIN_VALUE || valueAsLong > Byte.MAX_VALUE) + throwRangeException(value.toString(), columnIndex, Types.TINYINT); + + return (byte) valueAsLong; + } + + private void throwRangeException(String valueAsString, int columnIndex, int jdbcType) throws SQLException { + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_NUMERIC_VALUE_OUT_OF_RANGE, + "'" + valueAsString + "' in column '" + columnIndex + "' is outside valid range for the jdbcType " + TSDBConstants.jdbcType2TaosTypeName(jdbcType)); + } + + @Override + public short getShort(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return 0; + if (value instanceof Short) + return (short) value; + long valueAsLong = Long.parseLong(value.toString()); + if (valueAsLong == Short.MIN_VALUE) + return 0; + if (valueAsLong < Short.MIN_VALUE || valueAsLong > Short.MAX_VALUE) + throwRangeException(value.toString(), columnIndex, Types.SMALLINT); + return (short) valueAsLong; + } + + @Override + public int getInt(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return 0; + if (value instanceof Integer) + return (int) value; + long valueAsLong = Long.parseLong(value.toString()); + if (valueAsLong == Integer.MIN_VALUE) + return 0; + if (valueAsLong < Integer.MIN_VALUE || valueAsLong > Integer.MAX_VALUE) + throwRangeException(value.toString(), columnIndex, Types.INTEGER); + return (int) valueAsLong; + } + + @Override + public long getLong(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return 0; + if (value instanceof Long) + return (long) value; + if (value instanceof Timestamp) { + Timestamp ts = (Timestamp) value; + switch (this.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; + } + } + long valueAsLong = 0; + try { + valueAsLong = Long.parseLong(value.toString()); + if (valueAsLong == Long.MIN_VALUE) + return 0; + } catch (NumberFormatException e) { + throwRangeException(value.toString(), columnIndex, Types.BIGINT); + } + return valueAsLong; + } + + @Override + public float getFloat(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return 0; + if (value instanceof Float) + return (float) value; + if (value instanceof Double) + return new Float((Double) value); + return Float.parseFloat(value.toString()); + } + + @Override + public double getDouble(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) { + return 0; + } + if (value instanceof Double || value instanceof Float) + return (double) value; + return Double.parseDouble(value.toString()); + } + + @Override + public byte[] getBytes(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return null; + if (value instanceof byte[]) + return (byte[]) value; + if (value instanceof String) + return ((String) value).getBytes(); + if (value instanceof Long) + return Longs.toByteArray((long) value); + if (value instanceof Integer) + return Ints.toByteArray((int) value); + if (value instanceof Short) + return Shorts.toByteArray((short) value); + if (value instanceof Byte) + return new byte[]{(byte) value}; + if (value instanceof Timestamp) { + return Utils.formatTimestamp((Timestamp) value).getBytes(); + } + + return value.toString().getBytes(); + } + + @Override + public Date getDate(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return null; + if (value instanceof Timestamp) + return new Date(((Timestamp) value).getTime()); + return Utils.parseDate(value.toString()); + } + + @Override + public Time getTime(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return null; + if (value instanceof Timestamp) + return new Time(((Timestamp) value).getTime()); + Time time = null; + try { + time = Utils.parseTime(value.toString()); + } catch (DateTimeParseException ignored) { + } + return time; + } + + @Override + public Timestamp getTimestamp(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return null; + if (value instanceof Timestamp) + return (Timestamp) value; + if (value instanceof Long) { + if (1_0000_0000_0000_0L > (long) value) + return Timestamp.from(Instant.ofEpochMilli((long) value)); + long epochSec = (long) value / 1000_000L; + long nanoAdjustment = (long) value % 1000_000L * 1000; + return Timestamp.from(Instant.ofEpochSecond(epochSec, nanoAdjustment)); + } + Timestamp ret; + try { + ret = Utils.parseTimestamp(value.toString()); + } catch (Exception e) { + ret = null; + wasNull = true; + } + return ret; + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + return this.metaData; + } + + @Override + public Object getObject(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + return value; + } + + @Override + public int findColumn(String columnLabel) throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + + int columnIndex = columnNames.indexOf(columnLabel); + if (columnIndex == -1) + throw new SQLException("cannot find Column in result"); + return columnIndex + 1; + } + + @Override + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + checkAvailability(columnIndex, fields.size()); + + Object value = result.get(columnIndex - 1).get(rowIndex); + wasNull = value == null; + if (value == null) + return null; + if (value instanceof Long || value instanceof Integer || value instanceof Short || value instanceof Byte) + return new BigDecimal(Long.parseLong(value.toString())); + if (value instanceof Double || value instanceof Float) + return BigDecimal.valueOf(Double.parseDouble(value.toString())); + if (value instanceof Timestamp) + return new BigDecimal(((Timestamp) value).getTime()); + BigDecimal ret; + try { + ret = new BigDecimal(value.toString()); + } catch (Exception e) { + ret = null; + } + return ret; + } + + @Override + public boolean isBeforeFirst() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + return this.rowIndex == -1 && this.numOfRows != 0; + } + + @Override + public boolean isAfterLast() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + + return this.rowIndex >= numOfRows && this.numOfRows != 0; + } + + @Override + public boolean isFirst() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + return this.rowIndex == 0; + } + + @Override + public boolean isLast() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + if (this.numOfRows == 0) + return false; + return this.rowIndex == (this.numOfRows - 1); + } + + @Override + public void beforeFirst() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + + synchronized (this) { + if (this.numOfRows > 0) { + this.rowIndex = -1; + } + } + } + + @Override + public void afterLast() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + synchronized (this) { + if (this.numOfRows > 0) { + this.rowIndex = this.numOfRows; + } + } + } + + @Override + public boolean first() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + + if (this.numOfRows == 0) + return false; + + synchronized (this) { + this.rowIndex = 0; + } + return true; + } + + @Override + public boolean last() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + if (this.numOfRows == 0) + return false; + synchronized (this) { + this.rowIndex = this.numOfRows - 1; + } + return true; + } + + @Override + public int getRow() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + int row; + synchronized (this) { + if (this.rowIndex < 0 || this.rowIndex >= this.numOfRows) + return 0; + row = this.rowIndex + 1; + } + return row; + } + + @Override + public boolean absolute(int row) throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD); + } + + @Override + public boolean relative(int rows) throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD); + } + + @Override + public boolean previous() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_UNSUPPORTED_METHOD); + } + + @Override + public String getNString(int columnIndex) throws SQLException { + return getString(columnIndex); + } + + @Override + public Statement getStatement() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_RESULTSET_CLOSED); + + return this.statement; + } + + @Override + public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { + //TODO:did not use the specified timezone in cal + return getTimestamp(columnIndex); + } +} diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/InFlightRequest.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/InFlightRequest.java index 773bb38a8e..37349c4332 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/InFlightRequest.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/InFlightRequest.java @@ -1,58 +1,73 @@ package com.taosdata.jdbc.ws; +import com.taosdata.jdbc.ws.entity.Action; + +import java.util.HashMap; import java.util.Map; import java.util.concurrent.*; /** * Unfinished execution */ -public class InFlightRequest implements AutoCloseable { +public class InFlightRequest { private final int timeoutSec; private final Semaphore semaphore; - private final Map futureMap = new ConcurrentHashMap<>(); - private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); - private final ScheduledFuture scheduledFuture; + private final Map> futureMap = new HashMap<>(); + private final Map> expireMap = new HashMap<>(); + private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = new Thread(r); + t.setName("timer-" + t.getId()); + return t; + }); public InFlightRequest(int timeoutSec, int concurrentNum) { this.timeoutSec = timeoutSec; this.semaphore = new Semaphore(concurrentNum); - this.scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(this::removeTimeoutFuture, timeoutSec, timeoutSec, TimeUnit.MILLISECONDS); + scheduledExecutorService.scheduleWithFixedDelay(this::removeTimeoutFuture, + timeoutSec, timeoutSec, TimeUnit.MILLISECONDS); + Runtime.getRuntime().addShutdownHook(new Thread(scheduledExecutorService::shutdown)); + for (Action value : Action.values()) { + String action = value.getAction(); + if (Action.CONN.getAction().equals(action)) + continue; + futureMap.put(action, new ConcurrentHashMap<>()); + expireMap.put(action, new PriorityBlockingQueue<>()); + } } - public void put(ResponseFuture responseFuture) throws InterruptedException, TimeoutException { + public void put(ResponseFuture rf) throws InterruptedException, TimeoutException { if (semaphore.tryAcquire(timeoutSec, TimeUnit.MILLISECONDS)) { - futureMap.put(responseFuture.getId(), responseFuture); + futureMap.get(rf.getAction()).put(rf.getId(), rf); + expireMap.get(rf.getAction()).put(rf); } else { throw new TimeoutException(); } } - public ResponseFuture remove(String id) { - ResponseFuture future = futureMap.remove(id); + public ResponseFuture remove(String action, Long id) { + ResponseFuture future = futureMap.get(action).remove(id); if (null != future) { + expireMap.get(action).remove(future); semaphore.release(); } return future; } private void removeTimeoutFuture() { - futureMap.entrySet().removeIf(entry -> { - if (System.nanoTime() - entry.getValue().getTimestamp() > timeoutSec * 1_000_000L) { + expireMap.forEach((k, v) -> { + while (true) { + ResponseFuture response = v.peek(); + if (null == response || (System.nanoTime() - response.getTimestamp()) < timeoutSec * 1_000_000L) + break; + try { - entry.getValue().getFuture().completeExceptionally(new TimeoutException()); - }finally { + v.poll(); + futureMap.get(k).remove(response.getId()); + response.getFuture().completeExceptionally(new TimeoutException()); + } finally { semaphore.release(); } - return true; - } else { - return false; } }); } - - @Override - public void close() { - scheduledFuture.cancel(true); - scheduledExecutorService.shutdown(); - } } diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/ResponseFuture.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/ResponseFuture.java index f2525c30bf..5ce7e86572 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/ResponseFuture.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/ResponseFuture.java @@ -4,18 +4,24 @@ import com.taosdata.jdbc.ws.entity.Response; import java.util.concurrent.CompletableFuture; -public class ResponseFuture { - private final String id; +public class ResponseFuture implements Comparable { + private final String action; + private final Long id; private final CompletableFuture future; private final long timestamp; - public ResponseFuture(String id, CompletableFuture future) { + public ResponseFuture(String action, Long id, CompletableFuture future) { + this.action = action; this.id = id; this.future = future; timestamp = System.nanoTime(); } - public String getId() { + public String getAction() { + return action; + } + + public Long getId() { return id; } @@ -26,4 +32,12 @@ public class ResponseFuture { long getTimestamp() { return timestamp; } + + @Override + public int compareTo(ResponseFuture rf) { + long r = this.timestamp - rf.timestamp; + if (r > 0) return 1; + if (r < 0) return -1; + return 0; + } } diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/Transport.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/Transport.java index 9431e26585..94b5d9b6c8 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/Transport.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/Transport.java @@ -25,15 +25,19 @@ public class Transport implements AutoCloseable { public CompletableFuture send(Request request) { CompletableFuture completableFuture = new CompletableFuture<>(); try { - inFlightRequest.put(new ResponseFuture(request.id(), completableFuture)); + inFlightRequest.put(new ResponseFuture(request.getAction(), request.id(), completableFuture)); client.send(request.toString()); } catch (Throwable t) { - inFlightRequest.remove(request.id()); + inFlightRequest.remove(request.getAction(), request.id()); completableFuture.completeExceptionally(t); } return completableFuture; } + public void sendWithoutRep(Request request) { + client.send(request.toString()); + } + public boolean isClosed() throws SQLException { return client.isClosed(); } diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSClient.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSClient.java index d04ef1aba3..f66bbbe6b3 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSClient.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSClient.java @@ -7,6 +7,8 @@ import org.java_websocket.handshake.ServerHandshake; import java.net.URI; import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.concurrent.*; @@ -20,6 +22,7 @@ public class WSClient extends WebSocketClient implements AutoCloseable { ThreadPoolExecutor executor; private boolean auth; + private int reqId; public boolean isAuth() { return auth; @@ -54,8 +57,8 @@ public class WSClient extends WebSocketClient implements AutoCloseable { @Override public void onOpen(ServerHandshake serverHandshake) { // certification - Request request = Request.generateConnect(user, password, database); - this.send(request.toString()); + ConnectReq connectReq = new ConnectReq(++reqId, user, password, database); + this.send(new Request(Action.CONN.getAction(), connectReq).toString()); } @Override @@ -64,14 +67,15 @@ public class WSClient extends WebSocketClient implements AutoCloseable { executor.submit(() -> { JSONObject jsonObject = JSONObject.parseObject(message); if (Action.CONN.getAction().equals(jsonObject.getString("action"))) { - latch.countDown(); if (Code.SUCCESS.getCode() != jsonObject.getInteger("code")) { - auth = false; this.close(); + } else { + auth = true; } + latch.countDown(); } else { Response response = parseMessage(jsonObject); - ResponseFuture remove = inFlightRequest.remove(response.id()); + ResponseFuture remove = inFlightRequest.remove(response.getAction(), response.getReqId()); if (null != remove) { remove.getFuture().complete(response); } @@ -87,7 +91,14 @@ public class WSClient extends WebSocketClient implements AutoCloseable { @Override public void onMessage(ByteBuffer bytes) { - super.onMessage(bytes); + bytes.order(ByteOrder.LITTLE_ENDIAN); + long id = bytes.getLong(); + ResponseFuture remove = inFlightRequest.remove(Action.FETCH_BLOCK.getAction(), id); + if (null != remove) { +// FetchBlockResp fetchBlockResp = new FetchBlockResp(id, bytes.slice()); + FetchBlockResp fetchBlockResp = new FetchBlockResp(id, bytes); + remove.getFuture().complete(fetchBlockResp); + } } @Override @@ -97,7 +108,6 @@ public class WSClient extends WebSocketClient implements AutoCloseable { } else { throw new RuntimeException("close connection: " + reason); } - } @Override @@ -109,6 +119,42 @@ public class WSClient extends WebSocketClient implements AutoCloseable { public void close() { super.close(); executor.shutdown(); - inFlightRequest.close(); + } + + static class ConnectReq extends Payload { + private String user; + private String password; + private String db; + + public ConnectReq(long reqId, String user, String password, String db) { + super(reqId); + this.user = user; + this.password = password; + this.db = db; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getDb() { + return db; + } + + public void setDb(String db) { + this.db = db; + } } } diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSConnection.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSConnection.java index 5e2195093d..bdd56c03ce 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSConnection.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSConnection.java @@ -5,6 +5,7 @@ import com.taosdata.jdbc.TSDBDriver; import com.taosdata.jdbc.TSDBError; import com.taosdata.jdbc.TSDBErrorNumbers; import com.taosdata.jdbc.rs.RestfulDatabaseMetaData; +import com.taosdata.jdbc.ws.entity.RequestFactory; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; @@ -16,14 +17,14 @@ public class WSConnection extends AbstractConnection { private final Transport transport; private final DatabaseMetaData metaData; private final String database; - private boolean fetchType; + private final RequestFactory factory; - public WSConnection(String url, Properties properties, Transport transport, String database, boolean fetchType) { + public WSConnection(String url, Properties properties, Transport transport, String database) { super(properties); this.transport = transport; this.database = database; - this.fetchType = fetchType; this.metaData = new RestfulDatabaseMetaData(url, properties.getProperty(TSDBDriver.PROPERTY_KEY_USER), this); + this.factory = new RequestFactory(); } @Override @@ -31,8 +32,7 @@ public class WSConnection extends AbstractConnection { if (isClosed()) throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_CONNECTION_CLOSED); -// return new WSStatement(transport, database , fetchType); - return null; + return new WSStatement(transport, database, this, factory); } @Override diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSStatement.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSStatement.java new file mode 100644 index 0000000000..58e6ad3193 --- /dev/null +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/WSStatement.java @@ -0,0 +1,112 @@ +package com.taosdata.jdbc.ws; + +import com.taosdata.jdbc.AbstractStatement; +import com.taosdata.jdbc.TSDBError; +import com.taosdata.jdbc.TSDBErrorNumbers; +import com.taosdata.jdbc.utils.SqlSyntaxValidator; +import com.taosdata.jdbc.ws.entity.*; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class WSStatement extends AbstractStatement { + private final Transport transport; + private final String database; + private final Connection connection; + private final RequestFactory factory; + + private boolean closed; + private ResultSet resultSet; + + public WSStatement(Transport transport, String database, Connection connection, RequestFactory factory) { + this.transport = transport; + this.database = database; + this.connection = connection; + this.factory = factory; + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED); + if (!SqlSyntaxValidator.isValidForExecuteQuery(sql)) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_FOR_EXECUTE_QUERY, "not a valid sql for executeQuery: " + sql); + + this.execute(sql); + return this.resultSet; + } + + @Override + public int executeUpdate(String sql) throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED); + if (!SqlSyntaxValidator.isValidForExecuteUpdate(sql)) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_FOR_EXECUTE_UPDATE, "not a valid sql for executeUpdate: " + sql); + + this.execute(sql); + return affectedRows; + } + + @Override + public void close() throws SQLException { + if (!isClosed()) + this.closed = true; + } + + @Override + public boolean execute(String sql) throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED); + + Request request = factory.generateQuery(sql); + CompletableFuture send = transport.send(request); + + Response response; + try { + response = send.get(); + QueryResp queryResp = (QueryResp) response; + if (Code.SUCCESS.getCode() != queryResp.getCode()) { + throw TSDBError.createSQLException(queryResp.getCode(), queryResp.getMessage()); + } + if (queryResp.isUpdate()) { + this.resultSet = null; + this.affectedRows = queryResp.getAffectedRows(); + return false; + } else { + this.resultSet = new BlockResultSet(this, this.transport, this.factory, queryResp, this.database); + this.affectedRows = -1; + return true; + } + } catch (InterruptedException | ExecutionException e) { + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_WITH_EXECUTEQUERY, e.getMessage()); + } + } + + @Override + public ResultSet getResultSet() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED); + return this.resultSet; + } + + @Override + public int getUpdateCount() throws SQLException { + if (isClosed()) + throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED); + + return affectedRows; + } + + @Override + public Connection getConnection() throws SQLException { + return this.connection; + } + + @Override + public boolean isClosed() throws SQLException { + return closed; + } +} diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Action.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Action.java index 8d5d8272d7..9d44282df7 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Action.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Action.java @@ -11,8 +11,9 @@ public enum Action { QUERY("query", QueryResp.class), FETCH("fetch", FetchResp.class), FETCH_JSON("fetch_json", FetchJsonResp.class), - // fetch_block's class is meaningless - FETCH_BLOCK("fetch_block", Response.class), + FETCH_BLOCK("fetch_block", FetchBlockResp.class), + // free_result's class is meaningless + FREE_RESULT("free_result", Response.class), ; private final String action; private final Class clazz; @@ -35,7 +36,6 @@ public enum Action { static { for (Action value : Action.values()) { actions.put(value.action, value); - IdUtil.init(value.action); } } diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Code.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Code.java index 6b6d60858d..13a2b852e0 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Code.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Code.java @@ -5,7 +5,6 @@ package com.taosdata.jdbc.ws.entity; */ public enum Code { SUCCESS(0, "success"), - ; private final int code; diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchBlockResp.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchBlockResp.java index 40052f68e9..2dbcffb40f 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchBlockResp.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchBlockResp.java @@ -1,4 +1,21 @@ package com.taosdata.jdbc.ws.entity; -public class FetchBlockResp { +import java.nio.ByteBuffer; + +public class FetchBlockResp extends Response { + private ByteBuffer buffer; + + public FetchBlockResp(long id, ByteBuffer buffer) { + this.setAction(Action.FETCH_BLOCK.getAction()); + this.setReqId(id); + this.buffer = buffer; + } + + public ByteBuffer getBuffer() { + return buffer; + } + + public void setBuffer(ByteBuffer buffer) { + this.buffer = buffer; + } } diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchJsonResp.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchJsonResp.java index bdf6d51232..74c2f46d54 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchJsonResp.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchJsonResp.java @@ -1,14 +1,16 @@ package com.taosdata.jdbc.ws.entity; +import com.alibaba.fastjson.JSONArray; + public class FetchJsonResp extends Response{ private long id; - private Object[][] data; + private JSONArray data; - public Object[][] getData() { + public JSONArray getData() { return data; } - public void setData(Object[][] data) { + public void setData(JSONArray data) { this.data = data; } diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchReq.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchReq.java new file mode 100644 index 0000000000..25cd9dc472 --- /dev/null +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchReq.java @@ -0,0 +1,18 @@ +package com.taosdata.jdbc.ws.entity; + +public class FetchReq extends Payload { + private long id; + + public FetchReq(long reqId, long id) { + super(reqId); + this.id = id; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } +} diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchResp.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchResp.java index 45f5452007..08229c00b1 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchResp.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/FetchResp.java @@ -8,7 +8,7 @@ public class FetchResp extends Response{ private String message; private long id; private boolean completed; - private int[] lengths; + private Integer[] lengths; private int rows; public int getCode() { @@ -43,11 +43,11 @@ public class FetchResp extends Response{ this.completed = completed; } - public int[] getLengths() { + public Integer[] getLengths() { return lengths; } - public void setLengths(int[] lengths) { + public void setLengths(Integer[] lengths) { this.lengths = lengths; } diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/IdUtil.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/IdUtil.java deleted file mode 100644 index fb2aab51c6..0000000000 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/IdUtil.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.taosdata.jdbc.ws.entity; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; - -/** - * generate id for request - */ -public class IdUtil { - private static final Map ids = new HashMap<>(); - - public static long getId(String action) { - return ids.get(action).incrementAndGet(); - } - - public static void init(String action) { - ids.put(action, new AtomicLong(0)); - } -} diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Payload.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Payload.java new file mode 100644 index 0000000000..1821a5fc1f --- /dev/null +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Payload.java @@ -0,0 +1,16 @@ +package com.taosdata.jdbc.ws.entity; + +import com.alibaba.fastjson.annotation.JSONField; + +public class Payload { + @JSONField(name = "req_id") + private final long reqId; + + public Payload(long reqId) { + this.reqId = reqId; + } + + public long getReqId() { + return reqId; + } +} \ No newline at end of file diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/QueryReq.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/QueryReq.java new file mode 100644 index 0000000000..8e6d197bc6 --- /dev/null +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/QueryReq.java @@ -0,0 +1,18 @@ +package com.taosdata.jdbc.ws.entity; + +public class QueryReq extends Payload { + private String sql; + + public QueryReq(long reqId, String sql) { + super(reqId); + this.sql = sql; + } + + public String getSql() { + return sql; + } + + public void setSql(String sql) { + this.sql = sql; + } +} diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Request.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Request.java index ca0fdf427d..6462664309 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Request.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Request.java @@ -1,7 +1,6 @@ package com.taosdata.jdbc.ws.entity; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.annotation.JSONField; /** * send to taosadapter @@ -15,14 +14,14 @@ public class Request { this.args = args; } - public String id() { - return action + "_" + args.getReqId(); - } - public String getAction() { return action; } + public Long id(){ + return args.getReqId(); + } + public void setAction(String action) { this.action = action; } @@ -39,118 +38,4 @@ public class Request { public String toString() { return JSON.toJSONString(this); } - - public static Request generateConnect(String user, String password, String db) { - long reqId = IdUtil.getId(Action.CONN.getAction()); - ConnectReq connectReq = new ConnectReq(reqId, user, password, db); - return new Request(Action.CONN.getAction(), connectReq); - } - - public static Request generateQuery(String sql) { - long reqId = IdUtil.getId(Action.QUERY.getAction()); - QueryReq queryReq = new QueryReq(reqId, sql); - return new Request(Action.QUERY.getAction(), queryReq); - } - - public static Request generateFetch(long id) { - long reqId = IdUtil.getId(Action.FETCH.getAction()); - FetchReq fetchReq = new FetchReq(reqId, id); - return new Request(Action.FETCH.getAction(), fetchReq); - } - - public static Request generateFetchJson(long id) { - long reqId = IdUtil.getId(Action.FETCH_JSON.getAction()); - FetchReq fetchReq = new FetchReq(reqId, id); - return new Request(Action.FETCH_JSON.getAction(), fetchReq); - } - - public static Request generateFetchBlock(long id) { - long reqId = IdUtil.getId(Action.FETCH_BLOCK.getAction()); - FetchReq fetchReq = new FetchReq(reqId, id); - return new Request(Action.FETCH_BLOCK.getAction(), fetchReq); - } -} - -class Payload { - @JSONField(name = "req_id") - private final long reqId; - - public Payload(long reqId) { - this.reqId = reqId; - } - - public long getReqId() { - return reqId; - } -} - -class ConnectReq extends Payload { - private String user; - private String password; - private String db; - - public ConnectReq(long reqId, String user, String password, String db) { - super(reqId); - this.user = user; - this.password = password; - this.db = db; - } - - public String getUser() { - return user; - } - - public void setUser(String user) { - this.user = user; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getDb() { - return db; - } - - public void setDb(String db) { - this.db = db; - } -} - -class QueryReq extends Payload { - private String sql; - - public QueryReq(long reqId, String sql) { - super(reqId); - this.sql = sql; - } - - public String getSql() { - return sql; - } - - public void setSql(String sql) { - this.sql = sql; - } -} - -class FetchReq extends Payload { - private long id; - - public FetchReq(long reqId, long id) { - super(reqId); - this.id = id; - } - - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } } \ No newline at end of file diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/RequestFactory.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/RequestFactory.java new file mode 100644 index 0000000000..f033d0d8ba --- /dev/null +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/RequestFactory.java @@ -0,0 +1,48 @@ +package com.taosdata.jdbc.ws.entity; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +/** + * generate id for request + */ +public class RequestFactory { + private final Map ids = new HashMap<>(); + + public long getId(String action) { + return ids.get(action).incrementAndGet(); + } + + public RequestFactory() { + for (Action value : Action.values()) { + String action = value.getAction(); + if (Action.CONN.getAction().equals(action) || Action.FETCH_BLOCK.getAction().equals(action)) + continue; + ids.put(action, new AtomicLong(0)); + } + } + + public Request generateQuery(String sql) { + long reqId = this.getId(Action.QUERY.getAction()); + QueryReq queryReq = new QueryReq(reqId, sql); + return new Request(Action.QUERY.getAction(), queryReq); + } + + public Request generateFetch(long id) { + long reqId = this.getId(Action.FETCH.getAction()); + FetchReq fetchReq = new FetchReq(reqId, id); + return new Request(Action.FETCH.getAction(), fetchReq); + } + + public Request generateFetchJson(long id) { + long reqId = this.getId(Action.FETCH_JSON.getAction()); + FetchReq fetchReq = new FetchReq(reqId, id); + return new Request(Action.FETCH_JSON.getAction(), fetchReq); + } + + public Request generateFetchBlock(long id) { + FetchReq fetchReq = new FetchReq(id, id); + return new Request(Action.FETCH_BLOCK.getAction(), fetchReq); + } +} diff --git a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Response.java b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Response.java index 780e30067f..604317acaf 100644 --- a/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Response.java +++ b/src/connector/jdbc/src/main/java/com/taosdata/jdbc/ws/entity/Response.java @@ -11,10 +11,6 @@ public class Response { @JSONField(name = "req_id") private long reqId; - public String id() { - return action + "_" + reqId; - } - public String getAction() { return action; } diff --git a/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSConnectionTest.java b/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSConnectionTest.java index 0719a5094c..916f8287de 100644 --- a/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSConnectionTest.java +++ b/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSConnectionTest.java @@ -10,15 +10,17 @@ import org.junit.runner.RunWith; import java.sql.*; import java.util.Properties; +import java.util.concurrent.TimeUnit; /** * You need to start taosadapter before testing this method */ @Ignore @RunWith(CatalogRunner.class) -@TestTarget(alias = "test connection with server", author = "huolibo",version = "2.0.37") +@TestTarget(alias = "test connection with server", author = "huolibo", version = "2.0.37") public class WSConnectionTest { - private static final String host = "192.168.1.98"; +// private static final String host = "192.168.1.98"; + private static final String host = "127.0.0.1"; private static final int port = 6041; private Connection connection; @@ -46,13 +48,12 @@ public class WSConnectionTest { String url = "jdbc:TAOS-RS://" + host + ":" + port + "/"; Properties properties = new Properties(); properties.setProperty(TSDBDriver.PROPERTY_KEY_USER, "root"); - properties.setProperty(TSDBDriver.PROPERTY_KEY_PASSWORD,"taosdata"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_PASSWORD, "taosdata"); properties.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); connection = DriverManager.getConnection(url, properties); } - @Test -// @Test(expected = SQLException.class) + @Test(expected = SQLException.class) @Description("wrong password or user") public void wrongUserOrPasswordConection() throws SQLException { String url = "jdbc:TAOS-RS://" + host + ":" + port + "/test?user=abc&password=taosdata"; @@ -60,4 +61,21 @@ public class WSConnectionTest { properties.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); connection = DriverManager.getConnection(url, properties); } + + @Test + @Description("sleep keep connection") + public void keepConnection() throws SQLException, InterruptedException { + String url = "jdbc:TAOS-RS://" + host + ":" + port + "/?user=root&password=taosdata"; + Properties properties = new Properties(); + properties.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); + connection = DriverManager.getConnection(url, properties); + TimeUnit.MINUTES.sleep(1); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("show databases"); + TimeUnit.MINUTES.sleep(1); + resultSet.next(); + System.out.println(resultSet.getTimestamp(1)); + resultSet.close(); + statement.close(); + } } diff --git a/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSJsonTagTest.java b/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSJsonTagTest.java new file mode 100644 index 0000000000..a106e57fbf --- /dev/null +++ b/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSJsonTagTest.java @@ -0,0 +1,1283 @@ +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 org.junit.*; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +import java.sql.*; +import java.util.Properties; + +/** + * Most of the functionality is consistent with {@link com.taosdata.jdbc.JsonTagTest}, + * Except for batchInsert, which is not supported by restful API. + * Restful could not distinguish between empty and nonexistent of json value, the result is always null. + * The order of json results may change due to serialization and deserialization + */ +@Ignore +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(CatalogRunner.class) +@TestTarget(alias = "JsonTag", author = "huolibo", version = "2.0.38") +public class WSJsonTagTest { + private static final String dbName = "json_tag_test"; + private static Connection connection; + private static Statement statement; + private static final String superSql = "create table if not exists jsons1(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json)"; + private static final String[] sql = { + "insert into jsons1_1 using jsons1 tags('{\"tag1\":\"fff\",\"tag2\":5, \"tag3\":true}') values(now, 1, false, 'json1', '你是') (1591060608000, 23, true, '等等', 'json')", + "insert into jsons1_2 using jsons1 tags('{\"tag1\":5,\"tag2\":\"beijing\"}') values (1591060628000, 2, true, 'json2', 'sss')", + "insert into jsons1_3 using jsons1 tags('{\"tag1\":false,\"tag2\":\"beijing\"}') values (1591060668000, 3, false, 'json3', 'efwe')", + "insert into jsons1_4 using jsons1 tags('{\"tag1\":null,\"tag2\":\"shanghai\",\"tag3\":\"hello\"}') values (1591060728000, 4, true, 'json4', '323sd')", + "insert into jsons1_5 using jsons1 tags('{\"tag1\":1.232, \"tag2\":null}') values(1591060928000, 1, false, '你就会', 'ewe')", + "insert into jsons1_6 using jsons1 tags('{\"tag1\":11,\"tag2\":\"\",\"tag2\":null}') values(1591061628000, 11, false, '你就会','')", + "insert into jsons1_7 using jsons1 tags('{\"tag1\":\"收到货\",\"tag2\":\"\",\"tag3\":null}') values(1591062628000, 2, NULL, '你就会', 'dws')", + // test duplicate key using the first one. + "CREATE TABLE if not exists jsons1_8 using jsons1 tags('{\"tag1\":null, \"tag1\":true, \"tag1\":45, \"1tag$\":2, \" \":90}')", + + }; + + private static final String[] invalidJsonInsertSql = { + // test empty json string, save as tag is NULL + "insert into jsons1_9 using jsons1 tags('\t') values (1591062328000, 24, NULL, '你就会', '2sdw')", + }; + + private static final String[] invalidJsonCreateSql = { + "CREATE TABLE if not exists jsons1_10 using jsons1 tags('')", + "CREATE TABLE if not exists jsons1_11 using jsons1 tags(' ')", + "CREATE TABLE if not exists jsons1_12 using jsons1 tags('{}')", + "CREATE TABLE if not exists jsons1_13 using jsons1 tags('null')", + }; + + // test invalidate json + private static final String[] errorJsonInsertSql = { + "CREATE TABLE if not exists jsons1_14 using jsons1 tags('\"efwewf\"')", + "CREATE TABLE if not exists jsons1_14 using jsons1 tags('3333')", + "CREATE TABLE if not exists jsons1_14 using jsons1 tags('33.33')", + "CREATE TABLE if not exists jsons1_14 using jsons1 tags('false')", + "CREATE TABLE if not exists jsons1_14 using jsons1 tags('[1,true]')", + "CREATE TABLE if not exists jsons1_14 using jsons1 tags('{222}')", + "CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"fe\"}')", + }; + + private static final String[] errorSelectSql = { + "select * from jsons1 where jtag->tag1='beijing'", + "select * from jsons1 where jtag->'location'", + "select * from jsons1 where jtag->''", + "select * from jsons1 where jtag->''=9", + "select -> from jsons1", + "select ? from jsons1", + "select * from jsons1 where contains", + "select * from jsons1 where jtag->", + "select jtag->location from jsons1", + "select jtag contains location from jsons1", + "select * from jsons1 where jtag contains location", + "select * from jsons1 where jtag contains ''", + "select * from jsons1 where jtag contains 'location'='beijing'", + // test where with json tag + "select * from jsons1_1 where jtag is not null", + "select * from jsons1 where jtag='{\"tag1\":11,\"tag2\":\"\"}'", + "select * from jsons1 where jtag->'tag1'={}" + }; + + @Test + @Description("insert json tag") + public void case01_InsertTest() throws SQLException { + for (String sql : sql) { + statement.execute(sql); + } + for (String sql : invalidJsonInsertSql) { + statement.execute(sql); + } + for (String sql : invalidJsonCreateSql) { + statement.execute(sql); + } + } + + @Test + @Description("error json tag insert") + public void case02_ErrorJsonInsertTest() { + int count = 0; + for (String sql : errorJsonInsertSql) { + try { + statement.execute(sql); + } catch (SQLException e) { + count++; + } + } + Assert.assertEquals(errorJsonInsertSql.length, count); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when json value is array") + public void case02_ArrayErrorTest() throws SQLException { + statement.execute("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"tag1\":[1,true]}')"); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when json value is empty") + public void case02_EmptyValueErrorTest() throws SQLException { + statement.execute("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"tag1\":{}}')"); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when json key is not ASCII") + public void case02_AbnormalKeyErrorTest1() throws SQLException { + statement.execute("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"。loc\":\"fff\"}')"); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when json key is '\\t'") + public void case02_AbnormalKeyErrorTest2() throws SQLException { + statement.execute("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"\t\":\"fff\"}')"); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when json key is chinese") + public void case02_AbnormalKeyErrorTest3() throws SQLException { + statement.execute("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"试试\":\"fff\"}')"); + } + + @Test + @Description("alter json tag") + public void case03_AlterTag() throws SQLException { + statement.execute("ALTER TABLE jsons1_1 SET TAG jtag='{\"tag1\":\"femail\",\"tag2\":35,\"tag3\":true}'"); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when add json tag") + public void case03_AddTagErrorTest() throws SQLException { + statement.execute("ALTER STABLE jsons1 add tag tag2 nchar(20)"); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when delete json tag") + public void case03_dropTagErrorTest() throws SQLException { + statement.execute("ALTER STABLE jsons1 drop tag jtag"); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when set some json tag value") + public void case03_AlterTagErrorTest() throws SQLException { + statement.execute("ALTER TABLE jsons1_1 SET TAG jtag=4"); + } + + @Test + @Description("exception will throw when select syntax error") + public void case04_SelectErrorTest() { + int count = 0; + for (String sql : errorSelectSql) { + try { + statement.execute(sql); + } catch (SQLException e) { + count++; + } + } + Assert.assertEquals(errorSelectSql.length, count); + } + + @Test + @Description("normal select stable") + public void case04_select01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select dataint from jsons1"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(sql.length + invalidJsonInsertSql.length, count); + close(resultSet); + } + + @Test + @Description("select all column from stable") + public void case04_select02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(sql.length + invalidJsonInsertSql.length, count); + close(resultSet); + } + + @Test + @Description("select json tag from stable") + public void case04_select03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag from jsons1"); + ResultSetMetaData metaData = resultSet.getMetaData(); + metaData.getColumnTypeName(1); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(sql.length + invalidJsonInsertSql.length + invalidJsonCreateSql.length, count); + close(resultSet); + } + + @Test + @Description("where condition tag is null") + public void case04_select04() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag from jsons1 where jtag is null"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(invalidJsonInsertSql.length + invalidJsonCreateSql.length, count); + close(resultSet); + } + + @Test + @Description("where condition tag is not null") + public void case04_select05() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag from jsons1 where jtag is not null"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(sql.length, count); + close(resultSet); + } + + @Test + @Description("select json tag") + public void case04_select06() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag from jsons1_8"); + resultSet.next(); + String result = resultSet.getString(1); + Assert.assertEquals("{\" \":90,\"tag1\":null,\"1tag$\":2}", result); + close(resultSet); + } + + @Test + @Description("select json tag") + public void case04_select07() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag from jsons1_1"); + resultSet.next(); + String result = resultSet.getString(1); + Assert.assertEquals("{\"tag1\":\"femail\",\"tag2\":35,\"tag3\":true}", result); + close(resultSet); + } + + @Test + @Description("select not exist json tag") + public void case04_select08() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag from jsons1_9"); + resultSet.next(); + String result = resultSet.getString(1); + Assert.assertNull(result); + close(resultSet); + } + + @Test + @Description("select a json tag") + public void case04_select09() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag->'tag1' from jsons1_1"); + resultSet.next(); + String result = resultSet.getString(1); + Assert.assertEquals("\"femail\"", result); + close(resultSet); + } + + @Test + @Description(value = "select a normal value", version = "2.0.37") + public void case04_selectNormal() throws SQLException { + ResultSet resultSet = statement.executeQuery("select datastr from jsons1_1"); + resultSet.next(); + String result = resultSet.getString(1); + Assert.assertEquals("等等", result); + close(resultSet); + } + + @Test + @Description("select a json tag, the value is empty") + public void case04_select10() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag->'tag2' from jsons1_6"); + resultSet.next(); + String result = resultSet.getString(1); + Assert.assertEquals("\"\"", result); + close(resultSet); + } + + @Test + @Description("select a json tag, the value is int") + public void case04_select11() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag->'tag2' from jsons1_1"); + resultSet.next(); + String string = resultSet.getString(1); + Assert.assertEquals("35", string); + close(resultSet); + } + + @Test + @Description("select a json tag, the value is boolean") + public void case04_select12() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag->'tag3' from jsons1_1"); + resultSet.next(); + String string = resultSet.getString(1); + Assert.assertEquals("true", string); + close(resultSet); + } + +// @Test +// @Description("select a json tag, the value is null") +// public void case04_select13() throws SQLException { +// ResultSet resultSet = statement.executeQuery("select jtag->'tag1' from jsons1_4"); +// resultSet.next(); +// String string = resultSet.getString(1); +// Assert.assertEquals("null", string); +// close(resultSet); +// } + + @Test + @Description("select a json tag, the value is double") + public void case04_select14() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag->'tag1' from jsons1_5"); + resultSet.next(); + String string = resultSet.getString(1); + Assert.assertEquals("1.232000000", string); + close(resultSet); + } + + @Test + @Description("select a json tag, the key is not exist") + public void case04_select15() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag->'tag10' from jsons1_4"); + resultSet.next(); + String string = resultSet.getString(1); + Assert.assertNull(string); + close(resultSet); + } + + @Test + @Description("select a json tag, the result number equals tables number") + public void case04_select16() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag->'tag1' from jsons1"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(sql.length + invalidJsonCreateSql.length + invalidJsonInsertSql.length, count); + close(resultSet); + } + + @Test + @Description("where condition '=' for string") + public void case04_select19() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag2'='beijing'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("select and where conditon '=' for string") + public void case04_select20() throws SQLException { + ResultSet resultSet = statement.executeQuery("select dataint,tbname,jtag->'tag1',jtag from jsons1 where jtag->'tag2'='beijing'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("where condition result is null") + public void case04_select21() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'='beijing'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("where condition equation has chinese") + public void case04_select23() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'='收到货'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("where condition support '>' for character") + public void case05_symbolOperation01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag2'>'beijing'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("where condition support '>=' for character") + public void case05_symbolOperation02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag2'>='beijing'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(3, count); + close(resultSet); + } + + @Test + @Description("where condition support '<' for character") + public void case05_symbolOperation03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag2'<'beijing'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("where condition support '<=' in character") + public void case05_symbolOperation04() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag2'<='beijing'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(4, count); + close(resultSet); + } + + @Test + @Description("where condition support '!=' in character") + public void case05_symbolOperation05() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag2'!='beijing'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(3, count); + close(resultSet); + } + + @Test + @Description("where condition support '=' empty") + public void case05_symbolOperation06() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag2'=''"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + // where json value is int + @Test + @Description("where condition support '=' for int") + public void case06_selectValue01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'=5"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("where conditional support '<' for int") + public void case06_selectValue02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'<54"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(3, count); + close(resultSet); + } + + @Test + @Description("where condition support '<=' for int") + public void case06_selectValue03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'<=11"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(3, count); + close(resultSet); + } + + @Test + @Description("where conditional support '>' for int") + public void case06_selectValue04() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'>4"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("where condition support '>=' for int") + public void case06_selectValue05() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'>=5"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("where conditional support '!=' for int") + public void case06_selectValue06() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'!=5"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("where conditional support '!=' for int") + public void case06_selectValue07() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'!=55"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(3, count); + close(resultSet); + } + + @Test + @Description("where conditional support '!=' for int and result is nothing") + public void case06_selectValue08() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'=10"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("where condition support '=' for double") + public void case07_selectValue01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'=1.232"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("where condition support '<' for double") + public void case07_doubleOperation01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'<1.232"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("where condition support '<=' for double") + public void case07_doubleOperation02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'<=1.232"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("where condition support '>' for double") + public void case07_doubleOperation03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'>1.23"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(3, count); + close(resultSet); + } + + @Test + @Description("where condition support '>=' for double") + public void case07_doubleOperation04() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'>=1.232"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(3, count); + close(resultSet); + } + + @Test + @Description("where condition support '!=' for double") + public void case07_doubleOperation05() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'!=1.232"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("where condition support '!=' for double") + public void case07_doubleOperation06() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'!=3.232"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(3, count); + close(resultSet); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when denominator is zero") + public void case07_doubleOperation07() throws SQLException { + statement.executeQuery("select * from jsons1 where jtag->'tag1'/0=3"); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when invalid operation") + public void case07_doubleOperation08() throws SQLException { + statement.executeQuery("select * from jsons1 where jtag->'tag1'/5=1"); + } + + @Test + @Description("where condition support '=' for boolean") + public void case08_boolOperation01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'=true"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("where condition support '=' for boolean") + public void case08_boolOperation02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'=false"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("where condition support '!=' for boolean") + public void case08_boolOperation03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'!=false"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test(expected = SQLException.class) + @Description("exception will throw when '>' operation for boolean") + public void case08_boolOperation04() throws SQLException { + statement.executeQuery("select * from jsons1 where jtag->'tag1'>false"); + } + + @Test + @Description("where conditional support '=null'") + public void case09_select01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'=null"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("where conditional support 'is null'") + public void case09_select02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag is null"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("where condition support 'is not null'") + public void case09_select03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag is not null"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(8, count); + close(resultSet); + } + + @Test + @Description("where condition support one tag '='") + public void case09_select04() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag_no_exist'=3"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("where condition support one tag 'is null'") + public void case09_select05() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1' is null"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(invalidJsonInsertSql.length, count); + close(resultSet); + } + + @Test + @Description("where condition support one tag 'is null'") + public void case09_select06() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag4' is null"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(sql.length + invalidJsonInsertSql.length, count); + close(resultSet); + } + + @Test + @Description("where condition support one tag 'is not null'") + public void case09_select07() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag3' is not null"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(4, count); + close(resultSet); + } + + @Test + @Description("contains") + public void case09_select10() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag contains 'tag1'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(8, count); + close(resultSet); + } + + @Test + @Description("contains") + public void case09_select11() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag contains 'tag3'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(4, count); + close(resultSet); + } + + @Test + @Description("contains with no exist tag") + public void case09_select12() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag contains 'tag_no_exist'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("where condition with and") + public void case10_selectAndOr01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'=false and jtag->'tag2'='beijing'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("where condition with 'or'") + public void case10_selectAndOr02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'=false or jtag->'tag2'='beijing'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("where condition with 'and'") + public void case10_selectAndOr03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'=false and jtag->'tag2'='shanghai'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("where condition with 'or'") + public void case10_selectAndOr04() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'=13 or jtag->'tag2'>35"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("where condition with 'or' and contains") + public void case10_selectAndOr05() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1' is not null and jtag contains 'tag3'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(4, count); + close(resultSet); + } + + @Test + @Description("where condition with 'and' and contains") + public void case10_selectAndOr06() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1'='femail' and jtag contains 'tag3'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("test with tbname/normal column") + public void case11_selectTbName01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where tbname = 'jsons1_1'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("test with tbname/normal column") + public void case11_selectTbName02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where tbname = 'jsons1_1' and jtag contains 'tag3'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("test with tbname/normal column") + public void case11_selectTbName03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where tbname = 'jsons1_1' and jtag contains 'tag3' and dataint=3"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("test with tbname/normal column") + public void case11_selectTbName04() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where tbname = 'jsons1_1' and jtag contains 'tag3' and dataint=23"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("where condition like") + public void case12_selectWhere01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select *,tbname from jsons1 where jtag->'tag2' like 'bei%'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("where condition like") + public void case12_selectWhere02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select *,tbname from jsons1 where jtag->'tag1' like 'fe%' and jtag->'tag2' is not null"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test(expected = SQLException.class) + @Description("where condition in no support in") + public void case12_selectWhere03() throws SQLException { + statement.executeQuery("select * from jsons1 where jtag->'tag1' in ('beijing')"); + } + + @Test + @Description("where condition match") + public void case12_selectWhere04() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1' match 'ma'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("where condition match") + public void case12_selectWhere05() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1' match 'ma$'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("where condition match") + public void case12_selectWhere06() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag2' match 'jing$'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(2, count); + close(resultSet); + } + + @Test + @Description("where condition match") + public void case12_selectWhere07() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from jsons1 where jtag->'tag1' match '收到'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("insert distinct") + public void case13_selectDistinct01() throws SQLException { + statement.execute("insert into jsons1_14 using jsons1 tags('{\"tag1\":\"收到货\",\"tag2\":\"\",\"tag3\":null}') values(1591062628000, 2, NULL, '你就会', 'dws')"); + } + + @Test + @Description("distinct json tag") + public void case13_selectDistinct02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select distinct jtag->'tag1' from jsons1"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(8, count); + close(resultSet); + } + + @Test + @Description("distinct json tag") + public void case13_selectDistinct03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select distinct jtag from jsons1"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(9, count); + close(resultSet); + } + + @Test + @Description("insert json tag") + public void case14_selectDump01() throws SQLException { + statement.execute("INSERT INTO jsons1_15 using jsons1 tags('{\"tbname\":\"tt\",\"databool\":true,\"datastr\":\"是是是\"}') values(1591060828000, 4, false, 'jjsf', \"你就会\")"); + } + + @Test + @Description("test duplicate key with normal column") + public void case14_selectDump02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select *,tbname,jtag from jsons1 where jtag->'datastr' match '是' and datastr match 'js'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(1, count); + close(resultSet); + } + + @Test + @Description("test duplicate key with normal column") + public void case14_selectDump03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select tbname,jtag->'tbname' from jsons1 where jtag->'tbname'='tt' and tbname='jsons1_14'"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(0, count); + close(resultSet); + } + + @Test + @Description("insert json tag for join test") + public void case15_selectJoin01() throws SQLException { + statement.execute("create table if not exists jsons2(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json)"); + statement.execute("insert into jsons2_1 using jsons2 tags('{\"tag1\":\"fff\",\"tag2\":5, \"tag3\":true}') values(1591060618000, 2, false, 'json2', '你是2')"); + statement.execute("insert into jsons2_2 using jsons2 tags('{\"tag1\":5,\"tag2\":null}') values (1591060628000, 2, true, 'json2', 'sss')"); + + statement.execute("create table if not exists jsons3(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json)"); + statement.execute("insert into jsons3_1 using jsons3 tags('{\"tag1\":\"fff\",\"tag2\":5, \"tag3\":true}') values(1591060618000, 3, false, 'json3', '你是3')"); + statement.execute("insert into jsons3_2 using jsons3 tags('{\"tag1\":5,\"tag2\":\"beijing\"}') values (1591060638000, 2, true, 'json3', 'sss')"); + } + + @Test + @Description("select json tag from join") + public void case15_selectJoin02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select 'sss',33,a.jtag->'tag3' from jsons2 a,jsons3 b where a.ts=b.ts and a.jtag->'tag1'=b.jtag->'tag1'"); + resultSet.next(); + Assert.assertEquals("sss", resultSet.getString(1)); + close(resultSet); + } + + @Test + @Description("group by and order by json tag desc") + public void case16_selectGroupOrder01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select count(*) from jsons1 group by jtag->'tag1' order by jtag->'tag1' desc"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(8, count); + close(resultSet); + } + + @Test + @Description("group by and order by json tag asc") + public void case16_selectGroupOrder02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select count(*) from jsons1 group by jtag->'tag1' order by jtag->'tag1' asc"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(8, count); + close(resultSet); + } + + @Test + @Description("stddev with group by json tag") + public void case17_selectStddev01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select stddev(dataint) from jsons1 group by jtag->'tag1'"); + String s = ""; + int count = 0; + while (resultSet.next()) { + count++; + s = resultSet.getString(2); + + } + Assert.assertEquals(8, count); + Assert.assertEquals("\"femail\"", s); + close(resultSet); + } + + @Test + @Description("subquery json tag") + public void case18_selectSubquery01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select * from (select jtag, dataint from jsons1)"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(11, count); + close(resultSet); + } + + @Test + @Description("subquery some json tags") + public void case18_selectSubquery02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag->'tag1' from (select jtag->'tag1', dataint from jsons1)"); + + ResultSetMetaData metaData = resultSet.getMetaData(); + String columnName = metaData.getColumnName(1); + Assert.assertEquals("jtag->'tag1'", columnName); + + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(11, count); + close(resultSet); + } + + @Test + @Description("query some json tags from subquery") + public void case18_selectSubquery04() throws SQLException { + ResultSet resultSet = statement.executeQuery("select ts,tbname,jtag->'tag1' from (select jtag->'tag1',tbname,ts from jsons1 order by ts)"); + int count = 0; + while (resultSet.next()) { + count++; + } + Assert.assertEquals(11, count); + close(resultSet); + } + + @Test + @Description(value = "query metadata for json", version = "2.0.37") + public void case19_selectMetadata01() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag from jsons1"); + ResultSetMetaData metaData = resultSet.getMetaData(); + int columnType = metaData.getColumnType(1); + String columnTypeName = metaData.getColumnTypeName(1); + Assert.assertEquals(Types.OTHER, columnType); + Assert.assertEquals("JSON", columnTypeName); + close(resultSet); + } + + @Test + @Description(value = "query metadata for json", version = "2.0.37") + public void case19_selectMetadata02() throws SQLException { + ResultSet resultSet = statement.executeQuery("select *,jtag from jsons1"); + ResultSetMetaData metaData = resultSet.getMetaData(); + int columnType = metaData.getColumnType(6); + String columnTypeName = metaData.getColumnTypeName(6); + Assert.assertEquals(Types.OTHER, columnType); + Assert.assertEquals("JSON", columnTypeName); + close(resultSet); + } + + @Test + @Description(value = "query metadata for one json result", version = "2.0.37") + public void case19_selectMetadata03() throws SQLException { + ResultSet resultSet = statement.executeQuery("select jtag->'tag1' from jsons1_6"); + ResultSetMetaData metaData = resultSet.getMetaData(); + int columnType = metaData.getColumnType(1); + String columnTypeName = metaData.getColumnTypeName(1); + Assert.assertEquals(Types.OTHER, columnType); + Assert.assertEquals("JSON", columnTypeName); + resultSet.next(); + String string = resultSet.getString(1); + Assert.assertEquals("11", string); + close(resultSet); + } + + private void close(ResultSet resultSet) { + try { + if (null != resultSet) { + resultSet.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @BeforeClass + public static void beforeClass() { +// String host = "192.168.1.98"; + String host = "127.0.0.1"; + final String url = "jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata"; + try { + Properties properties = new Properties(); + properties.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); + connection = DriverManager.getConnection(url, properties); + statement = connection.createStatement(); + statement.execute("drop database if exists " + dbName); + statement.execute("create database if not exists " + dbName); + statement.execute("use " + dbName); + statement.execute(superSql); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void afterClass() { + try { + if (null != statement) { +// statement.execute("drop database " + dbName); + statement.close(); + } + if (null != connection) { + connection.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSQueryTest.java b/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSQueryTest.java new file mode 100644 index 0000000000..70ea3c4d88 --- /dev/null +++ b/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSQueryTest.java @@ -0,0 +1,62 @@ +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 org.junit.*; +import org.junit.runner.RunWith; + +import java.sql.*; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +@Ignore +@RunWith(CatalogRunner.class) +@TestTarget(alias = "query test", author = "huolibo", version = "2.0.38") +@FixMethodOrder +public class WSQueryTest { + private static final String host = "192.168.1.98"; + private static final int port = 6041; + private static final String databaseName = "ws_query"; + private static final String tableName = "wq"; + private Connection connection; + private long now; + + @Description("query") + @Test + public void queryBlock() throws SQLException, InterruptedException { + IntStream.range(1, 100).limit(1000).parallel().forEach(x -> { + try { + Statement statement = connection.createStatement(); + + statement.execute("insert into " + databaseName + "." + tableName + " values(now+100s, 100)"); + + ResultSet resultSet = statement.executeQuery("select * from " + databaseName + "." + tableName); + resultSet.next(); + Assert.assertEquals(100, resultSet.getInt(2)); + statement.close(); + TimeUnit.SECONDS.sleep(10); + } catch (SQLException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + } + + @Before + public void before() throws SQLException { + String url = "jdbc:TAOS-RS://" + host + ":" + port + "/test?user=root&password=taosdata"; + Properties properties = new Properties(); + properties.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); + connection = DriverManager.getConnection(url, properties); + Statement statement = connection.createStatement(); + statement.execute("drop database if exists " + databaseName); + statement.execute("create database " + databaseName); + statement.execute("use " + databaseName); + statement.execute("create table if not exists " + databaseName + "." + tableName + "(ts timestamp, f int)"); + statement.close(); + } +} diff --git a/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSSelectTest.java b/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSSelectTest.java new file mode 100644 index 0000000000..fa48480592 --- /dev/null +++ b/src/connector/jdbc/src/test/java/com/taosdata/jdbc/ws/WSSelectTest.java @@ -0,0 +1,83 @@ +package com.taosdata.jdbc.ws; + +import com.taosdata.jdbc.TSDBDriver; +import com.taosdata.jdbc.enums.TimestampFormat; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +@Ignore +public class WSSelectTest { + // private static final String host = "192.168.1.98"; + private static final String host = "127.0.0.1"; + private static final int port = 6041; + private static Connection connection; + private static final String databaseName = "driver"; + + private static void testInsert() throws SQLException { + Statement statement = connection.createStatement(); + long cur = System.currentTimeMillis(); + List timeList = new ArrayList<>(); + for (long i = 0L; i < 3000; i++) { + long t = cur + i; + timeList.add("insert into " + databaseName + ".alltype_query values(" + t + ",1,1,1,1,1,1,1,1,1,1,1,'test_binary','test_nchar')"); + } + for (int i = 0; i < 3000; i++) { + statement.execute(timeList.get(i)); + } + statement.close(); + } + + @Test + public void testWSSelect() throws SQLException { + Statement statement = connection.createStatement(); + int count = 0; + long start = System.nanoTime(); + for (int i = 0; i < 1000; i++) { + ResultSet resultSet = statement.executeQuery("select ts,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13 from " + databaseName + ".alltype_query limit 3000"); + while (resultSet.next()) { + count++; + resultSet.getTimestamp(1); + resultSet.getBoolean(2); + resultSet.getInt(3); + resultSet.getInt(4); + resultSet.getInt(5); + resultSet.getLong(6); + resultSet.getInt(7); + resultSet.getInt(8); + resultSet.getLong(9); + resultSet.getLong(10); + resultSet.getFloat(11); + resultSet.getDouble(12); + resultSet.getString(13); + resultSet.getString(14); + } + } + long d = System.nanoTime() - start; + System.out.println(d / 1000); + System.out.println(count); + statement.close(); + } + + @BeforeClass + public static void beforeClass() throws SQLException { + String url = "jdbc:TAOS-RS://" + host + ":" + port + "/?user=root&password=taosdata"; + Properties properties = new Properties(); + properties.setProperty(TSDBDriver.PROPERTY_KEY_TIMESTAMP_FORMAT, String.valueOf(TimestampFormat.UTC)); + properties.setProperty(TSDBDriver.PROPERTY_KEY_MESSAGE_WAIT_TIMEOUT, "100000"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); + connection = DriverManager.getConnection(url, properties); + Statement statement = connection.createStatement(); + statement.execute("drop database if exists " + databaseName); + statement.execute("create database " + databaseName); + statement.execute("create table " + databaseName + ".alltype_query(ts timestamp, c1 bool,c2 tinyint, c3 smallint, c4 int, c5 bigint, c6 tinyint unsigned, c7 smallint unsigned, c8 int unsigned, c9 bigint unsigned, c10 float, c11 double, c12 binary(20), c13 nchar(30) )"); + statement.close(); + testInsert(); + } +} -- GitLab