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 06e9221515530..ceed84624851b 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 @@ -68,6 +68,9 @@ public class IoTDBConnection implements Connection { private static final Logger logger = LoggerFactory.getLogger(IoTDBConnection.class); private static final TSProtocolVersion protocolVersion = TSProtocolVersion.IOTDB_SERVICE_PROTOCOL_V3; + // Match Thrift's fixed protocol text independently of the SQLException message. + private static final String MISSING_CLIENT_PROTOCOL_DIAGNOSTIC = + "Required field 'client_protocol' was not present!"; private static final String NOT_SUPPORT_PREPARE_CALL = "Does not support prepareCall"; private static final String NOT_SUPPORT_PREPARE_STATEMENT = "Does not support prepareStatement"; private IClientRPCService.Iface client = null; @@ -521,18 +524,18 @@ private void openSession() throws SQLException { } catch (TException e) { transport.close(); - if (e.getMessage().contains("Required field 'client_protocol' was not present!")) { + String message = e.getMessage(); + if (message != null && message.contains(MISSING_CLIENT_PROTOCOL_DIAGNOSTIC)) { // the server is an old version (less than 0.10) throw new SQLException( String.format( "Can not establish connection with %s : You may try to connect an old version IoTDB instance using a client with new version: %s. ", - params.getJdbcUriString(), e.getMessage()), + params.getJdbcUriString(), message), e); } throw new SQLException( String.format( - "Can not establish connection with %s : %s. ", - params.getJdbcUriString(), e.getMessage()), + "Can not establish connection with %s : %s. ", params.getJdbcUriString(), message), e); } catch (StatementExecutionException e) { // failed to connect, disconnect from the server diff --git a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBConnectionTest.java b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBConnectionTest.java index 2919e51d265a5..5c51d01b14238 100644 --- a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBConnectionTest.java +++ b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBConnectionTest.java @@ -24,9 +24,12 @@ import org.apache.iotdb.service.rpc.thrift.IClientRPCService; import org.apache.iotdb.service.rpc.thrift.ServerProperties; import org.apache.iotdb.service.rpc.thrift.TSGetTimeZoneResp; +import org.apache.iotdb.service.rpc.thrift.TSOpenSessionReq; import org.apache.iotdb.service.rpc.thrift.TSSetTimeZoneReq; import org.apache.thrift.TException; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -34,21 +37,31 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.time.ZoneId; import java.util.ArrayList; import java.util.List; +import java.util.Properties; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class IoTDBConnectionTest { @Mock private IClientRPCService.Iface client; + @Mock private TTransport transport; + private static final String JDBC_URL = "jdbc:iotdb://127.0.0.1:6667/"; private IoTDBConnection connection = new IoTDBConnection(); private TSStatus successStatus = RpcUtils.SUCCESS_STATUS; private long sessionId; @@ -119,4 +132,70 @@ public void setTimeoutTest() throws SQLException { connection.setQueryTimeout(60); Assert.assertEquals(60, connection.getQueryTimeout()); } + + @Test + public void testOpenSessionRecognizesLegacyProtocolDiagnostic() throws Exception { + TException cause = + new TException( + "Required field 'client_protocol' was not present! Struct: TSOpenSessionReq(...)"); + + SQLException exception = failOpenSession(cause); + + assertEquals( + String.format( + "Can not establish connection with %s : You may try to connect an old version IoTDB instance using a client with new version: %s. ", + JDBC_URL, cause.getMessage()), + exception.getMessage()); + } + + @Test + public void testOpenSessionPreservesOtherMissingFieldDiagnostic() throws Exception { + TException cause = + new TException("Required field 'username' was not present! Struct: TSOpenSessionReq(...)"); + + SQLException exception = failOpenSession(cause); + + assertEquals( + String.format("Can not establish connection with %s : %s. ", JDBC_URL, cause.getMessage()), + exception.getMessage()); + } + + @Test + public void testOpenSessionPreservesMessageLessTransportException() throws Exception { + TException cause = new TTransportException(TTransportException.END_OF_FILE); + + SQLException exception = failOpenSession(cause); + + assertEquals( + String.format("Can not establish connection with %s : %s. ", JDBC_URL, cause.getMessage()), + exception.getMessage()); + } + + private SQLException failOpenSession(TException cause) throws Exception { + Method openSession = prepareOpenSession(); + when(client.openSession(any(TSOpenSessionReq.class))).thenThrow(cause); + + InvocationTargetException invocationException = + assertThrows(InvocationTargetException.class, () -> openSession.invoke(connection)); + + assertTrue(invocationException.getCause() instanceof SQLException); + SQLException exception = (SQLException) invocationException.getCause(); + assertSame(cause, exception.getCause()); + verify(transport).close(); + return exception; + } + + private Method prepareOpenSession() throws Exception { + IoTDBConnectionParams params = Utils.parseUrl(JDBC_URL, new Properties()); + Field paramsField = IoTDBConnection.class.getDeclaredField("params"); + paramsField.setAccessible(true); + paramsField.set(connection, params); + Field transportField = IoTDBConnection.class.getDeclaredField("transport"); + transportField.setAccessible(true); + transportField.set(connection, transport); + connection.setClient(client); + Method openSession = IoTDBConnection.class.getDeclaredMethod("openSession"); + openSession.setAccessible(true); + return openSession; + } }