This is an automated email from the ASF dual-hosted git repository. rong pushed a commit to branch jdbc-charset in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 2bc39499c8bd3104a192e6cd0e1691fb58e5273d Author: Steve Yurong Su <[email protected]> AuthorDate: Fri May 31 18:00:45 2024 +0800 JDBC: SUPPORT DIFFERENT CHARSETS --- .../main/java/org/apache/iotdb/jdbc/Config.java | 3 + .../org/apache/iotdb/jdbc/IoTDBConnection.java | 10 ++- .../apache/iotdb/jdbc/IoTDBConnectionParams.java | 10 +++ .../org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java | 54 +++++++++++++- .../apache/iotdb/jdbc/IoTDBPreparedStatement.java | 16 ++++- .../java/org/apache/iotdb/jdbc/IoTDBStatement.java | 83 +++++++++++++++++----- .../src/main/java/org/apache/iotdb/jdbc/Utils.java | 16 +++-- .../apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java | 36 +++++----- .../org/apache/iotdb/jdbc/IoTDBStatementTest.java | 8 ++- 9 files changed, 188 insertions(+), 48 deletions(-) diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Config.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Config.java index 407f07ef30d..4e2e0f38300 100644 --- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Config.java +++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Config.java @@ -73,6 +73,9 @@ public class Config { /** Key of connection's time zone. */ public static final String TIME_ZONE = "time_zone"; + /** Key of connection's charset. */ + public static final String CHARSET = "charset"; + public static final String USE_SSL = "use_ssl"; public static final String TRUST_STORE = "trust_store"; diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java index 8b3bef19cb9..06e92215155 100644 --- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java +++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java @@ -39,6 +39,7 @@ import org.apache.thrift.transport.TTransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.nio.charset.Charset; import java.sql.Array; import java.sql.Blob; import java.sql.CallableStatement; @@ -89,6 +90,8 @@ public class IoTDBConnection implements Connection { private int networkTimeout = Config.DEFAULT_CONNECTION_TIMEOUT_MS; private ZoneId zoneId; + private Charset charset; + private boolean autoCommit; private String url; @@ -111,6 +114,7 @@ public class IoTDBConnection implements Connection { this.userName = info.get("user").toString(); this.networkTimeout = params.getNetworkTimeout(); this.zoneId = ZoneId.of(params.getTimeZone()); + this.charset = params.getCharset(); openTransport(); if (Config.rpcThriftCompressionEnable) { setClient(new IClientRPCService.Client(new TCompactProtocol(transport))); @@ -200,7 +204,7 @@ public class IoTDBConnection implements Connection { if (isClosed) { throw new SQLException("Cannot create statement because connection is closed"); } - return new IoTDBStatement(this, getClient(), sessionId, zoneId, queryTimeout); + return new IoTDBStatement(this, getClient(), sessionId, zoneId, charset, queryTimeout); } @Override @@ -215,7 +219,7 @@ public class IoTDBConnection implements Connection { throw new SQLException( String.format("Statements with ResultSet type %d are not supported", resultSetType)); } - return new IoTDBStatement(this, getClient(), sessionId, zoneId, queryTimeout); + return new IoTDBStatement(this, getClient(), sessionId, zoneId, charset, queryTimeout); } @Override @@ -366,7 +370,7 @@ public class IoTDBConnection implements Connection { @Override public PreparedStatement prepareStatement(String sql) throws SQLException { - return new IoTDBPreparedStatement(this, getClient(), sessionId, sql, zoneId); + return new IoTDBPreparedStatement(this, getClient(), sessionId, sql, zoneId, charset); } @Override diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java index d1523691f79..be51a8fcf07 100644 --- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java +++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java @@ -21,6 +21,7 @@ package org.apache.iotdb.jdbc; import org.apache.iotdb.rpc.RpcUtils; +import java.nio.charset.Charset; import java.time.ZoneId; public class IoTDBConnectionParams { @@ -40,6 +41,7 @@ public class IoTDBConnectionParams { private int networkTimeout = Config.DEFAULT_CONNECTION_TIMEOUT_MS; private String timeZone = ZoneId.systemDefault().toString(); + private Charset charset = Charset.defaultCharset(); private boolean useSSL = false; private String trustStore; @@ -141,6 +143,14 @@ public class IoTDBConnectionParams { return this.timeZone; } + public void setCharset(String charsetName) { + this.charset = Charset.forName(charsetName); + } + + public Charset getCharset() { + return charset; + } + public boolean isUseSSL() { return useSSL; } diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java index df558b18809..17288335015 100644 --- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java +++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java @@ -37,6 +37,7 @@ import java.math.BigDecimal; import java.math.MathContext; import java.net.URL; import java.nio.ByteBuffer; +import java.nio.charset.Charset; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; @@ -70,6 +71,7 @@ public class IoTDBJDBCResultSet implements ResultSet { private String operationType = ""; private List<String> columns = null; private List<String> sgColumns = null; + private Charset charset = Charset.defaultCharset(); private String timeFormat = RpcUtils.DEFAULT_TIME_FORMAT; @SuppressWarnings("squid:S107") // ignore Methods should not have too many parameters @@ -91,7 +93,8 @@ public class IoTDBJDBCResultSet implements ResultSet { List<String> sgColumns, BitSet aliasColumnMap, boolean moreData, - ZoneId zoneId) + ZoneId zoneId, + Charset charset) throws SQLException { this.ioTDBRpcDataSet = new IoTDBRpcDataSet( @@ -121,6 +124,51 @@ public class IoTDBJDBCResultSet implements ResultSet { this.operationType = operationType; this.columns = columns; this.sgColumns = sgColumns; + this.charset = charset; + } + + @SuppressWarnings("squid:S107") // ignore Methods should not have too many parameters + public IoTDBJDBCResultSet( + Statement statement, + List<String> columnNameList, + List<String> columnTypeList, + Map<String, Integer> columnNameIndex, + boolean ignoreTimeStamp, + IClientRPCService.Iface client, + String sql, + long queryId, + long sessionId, + List<ByteBuffer> dataSet, + TSTracingInfo tracingInfo, + long timeout, + boolean moreData, + ZoneId zoneId, + Charset charset) + throws SQLException { + this.ioTDBRpcDataSet = + new IoTDBRpcDataSet( + sql, + columnNameList, + columnTypeList, + columnNameIndex, + ignoreTimeStamp, + moreData, + queryId, + ((IoTDBStatement) statement).getStmtId(), + client, + sessionId, + dataSet, + statement.getFetchSize(), + timeout, + zoneId, + timeFormat); + this.statement = statement; + this.columnTypeList = columnTypeList; + if (tracingInfo != null) { + ioTDBRpcTracingInfo = new IoTDBTracingInfo(); + ioTDBRpcTracingInfo.setTsTracingInfo(tracingInfo); + } + this.charset = charset; } @SuppressWarnings("squid:S107") // ignore Methods should not have too many parameters @@ -327,7 +375,7 @@ public class IoTDBJDBCResultSet implements ResultSet { @Override public byte[] getBytes(int columnIndex) throws SQLException { try { - return ioTDBRpcDataSet.getBinary(columnIndex).getValues(); + return ioTDBRpcDataSet.getString(columnIndex).getBytes(charset); } catch (StatementExecutionException e) { throw new SQLException(e.getMessage()); } @@ -336,7 +384,7 @@ public class IoTDBJDBCResultSet implements ResultSet { @Override public byte[] getBytes(String columnName) throws SQLException { try { - return ioTDBRpcDataSet.getBinary(columnName).getValues(); + return ioTDBRpcDataSet.getString(columnName).getBytes(charset); } catch (StatementExecutionException e) { throw new SQLException(e.getMessage()); } diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBPreparedStatement.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBPreparedStatement.java index 6cc0db318de..708450b05cb 100644 --- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBPreparedStatement.java +++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBPreparedStatement.java @@ -32,6 +32,7 @@ import java.io.Reader; import java.io.StringReader; import java.math.BigDecimal; import java.net.URL; +import java.nio.charset.Charset; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; @@ -71,10 +72,23 @@ public class IoTDBPreparedStatement extends IoTDBStatement implements PreparedSt /** save the SQL parameters as (paramLoc,paramValue) pairs. */ private final Map<Integer, String> parameters = new HashMap<>(); + IoTDBPreparedStatement( + IoTDBConnection connection, + Iface client, + Long sessionId, + String sql, + ZoneId zoneId, + Charset charset) + throws SQLException { + super(connection, client, sessionId, zoneId, charset); + this.sql = sql; + } + + // Only for tests IoTDBPreparedStatement( IoTDBConnection connection, Iface client, Long sessionId, String sql, ZoneId zoneId) throws SQLException { - super(connection, client, sessionId, zoneId); + super(connection, client, sessionId, zoneId, Charset.defaultCharset()); this.sql = sql; } diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java index 6d93c932d0d..f321bd9d800 100644 --- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java +++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java @@ -32,6 +32,7 @@ import org.apache.iotdb.service.rpc.thrift.TSExecuteStatementResp; import org.apache.thrift.TException; +import java.nio.charset.Charset; import java.sql.BatchUpdateException; import java.sql.Connection; import java.sql.ResultSet; @@ -45,12 +46,15 @@ import java.util.List; public class IoTDBStatement implements Statement { - ZoneId zoneId; + private final IoTDBConnection connection; + private ResultSet resultSet = null; - private IoTDBConnection connection; private int fetchSize; private int maxRows = 0; + protected final ZoneId zoneId; + protected final Charset charset; + /** * Timeout of query can be set by users. Unit: s. A negative number means using the default * configuration of server. And value 0 will disable the function of query timeout. @@ -82,6 +86,7 @@ public class IoTDBStatement implements Statement { long sessionId, int fetchSize, ZoneId zoneId, + Charset charset, int seconds) throws SQLException { this.connection = connection; @@ -90,11 +95,44 @@ public class IoTDBStatement implements Statement { this.fetchSize = fetchSize; this.batchSQLList = new ArrayList<>(); this.zoneId = zoneId; + this.charset = charset; this.queryTimeout = seconds; requestStmtId(); } - // only for test + IoTDBStatement( + IoTDBConnection connection, + IClientRPCService.Iface client, + long sessionId, + ZoneId zoneId, + Charset charset, + int seconds) + throws SQLException { + this(connection, client, sessionId, Config.DEFAULT_FETCH_SIZE, zoneId, charset, seconds); + } + + IoTDBStatement( + IoTDBConnection connection, + IClientRPCService.Iface client, + long sessionId, + ZoneId zoneId, + Charset charset) + throws SQLException { + this(connection, client, sessionId, Config.DEFAULT_FETCH_SIZE, zoneId, charset, 0); + } + + // Only for tests + IoTDBStatement( + IoTDBConnection connection, + IClientRPCService.Iface client, + long sessionId, + ZoneId zoneId, + int seconds) + throws SQLException { + this(connection, client, sessionId, Config.DEFAULT_FETCH_SIZE, zoneId, Charset.defaultCharset(), seconds); + } + + // Only for tests IoTDBStatement( IoTDBConnection connection, IClientRPCService.Iface client, @@ -108,24 +146,23 @@ public class IoTDBStatement implements Statement { this.fetchSize = Config.DEFAULT_FETCH_SIZE; this.batchSQLList = new ArrayList<>(); this.zoneId = zoneId; + this.charset = Charset.defaultCharset(); this.queryTimeout = seconds; this.stmtId = statementId; } + // Only for tests IoTDBStatement( IoTDBConnection connection, IClientRPCService.Iface client, long sessionId, ZoneId zoneId) throws SQLException { - this(connection, client, sessionId, Config.DEFAULT_FETCH_SIZE, zoneId, 0); - } - - IoTDBStatement( - IoTDBConnection connection, - IClientRPCService.Iface client, - long sessionId, - ZoneId zoneId, - int seconds) - throws SQLException { - this(connection, client, sessionId, Config.DEFAULT_FETCH_SIZE, zoneId, seconds); + this( + connection, + client, + sessionId, + Config.DEFAULT_FETCH_SIZE, + zoneId, + Charset.defaultCharset(), + 0); } @Override @@ -206,6 +243,18 @@ public class IoTDBStatement implements Statement { throw new SQLException("Not support closeOnCompletion"); } + /** + * Execute a SQL encoded in bytes with {@link IoTDBStatement#charset}. + * + * @param sql raw sql in bytes encoded with {@link IoTDBStatement#charset} + * @return true if the first result is a {@link ResultSet} object; false if the first result is an + * update count or there is no result + * @throws SQLException if a database access error occurs + */ + public boolean execute(byte[] sql) throws SQLException { + return execute(new String(sql, charset)); + } + @Override public boolean execute(String sql) throws SQLException { checkConnection("execute"); @@ -302,7 +351,8 @@ public class IoTDBStatement implements Statement { execResp.tracingInfo, execReq.timeout, execResp.moreData, - zoneId); + zoneId, + charset); } return true; } @@ -457,7 +507,8 @@ public class IoTDBStatement implements Statement { execResp.sgColumns, aliasColumn, execResp.moreData, - zoneId); + zoneId, + charset); } return resultSet; } diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java index af884d3e781..dcda9e3071e 100644 --- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java +++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java @@ -19,6 +19,7 @@ package org.apache.iotdb.jdbc; +import java.nio.charset.Charset; import java.time.DateTimeException; import java.time.ZoneId; import java.util.Properties; @@ -104,19 +105,18 @@ public class Utils { if (info.containsKey(Config.TIME_ZONE)) { params.setTimeZone(info.getProperty(Config.TIME_ZONE)); } - + if (info.containsKey(Config.CHARSET)) { + params.setCharset(info.getProperty(Config.CHARSET)); + } if (info.containsKey(Config.USE_SSL)) { params.setUseSSL(Boolean.parseBoolean(info.getProperty(Config.USE_SSL))); } - if (info.containsKey(Config.TRUST_STORE)) { params.setTrustStore(info.getProperty(Config.TRUST_STORE)); } - if (info.containsKey(Config.TRUST_STORE_PWD)) { params.setTrustStorePwd(info.getProperty(Config.TRUST_STORE_PWD)); } - return params; } @@ -165,6 +165,14 @@ public class Utils { } info.put(key, value); break; + case Config.CHARSET: + try { + Charset.forName(value); + } catch (Exception e) { + return false; + } + info.put(key, value); + break; default: return false; } diff --git a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java index e69c7673d66..46595d3d79c 100644 --- a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java +++ b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java @@ -19,6 +19,23 @@ package org.apache.iotdb.jdbc; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.Statement; +import java.sql.Types; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.service.rpc.thrift.IClientRPCService; @@ -29,7 +46,6 @@ import org.apache.iotdb.service.rpc.thrift.TSFetchMetadataReq; import org.apache.iotdb.service.rpc.thrift.TSFetchMetadataResp; import org.apache.iotdb.service.rpc.thrift.TSFetchResultsReq; import org.apache.iotdb.service.rpc.thrift.TSFetchResultsResp; - import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.read.common.block.column.TsBlockSerde; @@ -39,24 +55,6 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.Statement; -import java.sql.Types; -import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - /* This class is designed to test the function of TsfileQueryResultSet. This class also sheds light on the complete execution process of a query sql from the jdbc perspective. diff --git a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBStatementTest.java b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBStatementTest.java index 2699c442d39..b18a7068b5b 100644 --- a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBStatementTest.java +++ b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBStatementTest.java @@ -31,6 +31,7 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.nio.charset.Charset; import java.sql.SQLException; import java.time.ZoneId; @@ -84,7 +85,9 @@ public class IoTDBStatementTest { @Test public void testSetFetchSize3() throws SQLException { final int fetchSize = 10000; - IoTDBStatement stmt = new IoTDBStatement(connection, client, sessionId, fetchSize, zoneID, 0); + IoTDBStatement stmt = + new IoTDBStatement( + connection, client, sessionId, fetchSize, zoneID, Charset.defaultCharset(), 0); assertEquals(fetchSize, stmt.getFetchSize()); } @@ -97,7 +100,8 @@ public class IoTDBStatementTest { @Test public void setTimeoutTest() throws SQLException { - IoTDBStatement statement = new IoTDBStatement(connection, client, sessionId, zoneID, 60); + IoTDBStatement statement = + new IoTDBStatement(connection, client, sessionId, zoneID, Charset.defaultCharset(), 60); Assert.assertEquals(60, statement.getQueryTimeout()); statement.setQueryTimeout(100); Assert.assertEquals(100, statement.getQueryTimeout());
