提交 dd9228e0 编写于 作者: Z zyyang

[TD-1859]<feature>: restful implementation for taos-jdbc

上级 4c11e460
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<groupId>com.taosdata.jdbc</groupId> <groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId> <artifactId>taos-jdbcdriver</artifactId>
<version>2.0.10</version> <version>2.0.13</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>JDBCDriver</name> <name>JDBCDriver</name>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.taosdata.jdbc</groupId> <groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId> <artifactId>taos-jdbcdriver</artifactId>
<version>2.0.8</version> <version>2.0.13</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>JDBCDriver</name> <name>JDBCDriver</name>
<url>https://github.com/taosdata/TDengine/tree/master/src/connector/jdbc</url> <url>https://github.com/taosdata/TDengine/tree/master/src/connector/jdbc</url>
...@@ -112,6 +112,13 @@ ...@@ -112,6 +112,13 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version> <version>2.12.4</version>
<configuration> <configuration>
<includes>
<include>**/*Test.java</include>
</includes>
<excludes>
<exclude>**/BatchInsertTest.java</exclude>
<exclude>**/FailOverTest.java</exclude>
</excludes>
<testFailureIgnore>true</testFailureIgnore> <testFailureIgnore>true</testFailureIgnore>
</configuration> </configuration>
</plugin> </plugin>
......
...@@ -15,7 +15,6 @@ public class RestfulConnection implements Connection { ...@@ -15,7 +15,6 @@ public class RestfulConnection implements Connection {
private final String database; private final String database;
private final String url; private final String url;
public RestfulConnection(String host, String port, Properties props, String database, String url) { public RestfulConnection(String host, String port, Properties props, String database, String url) {
this.host = host; this.host = host;
this.port = Integer.parseInt(port); this.port = Integer.parseInt(port);
...@@ -28,7 +27,7 @@ public class RestfulConnection implements Connection { ...@@ -28,7 +27,7 @@ public class RestfulConnection implements Connection {
public Statement createStatement() throws SQLException { public Statement createStatement() throws SQLException {
if (isClosed()) if (isClosed())
throw new SQLException(TSDBConstants.WrapErrMsg("restful TDengine connection is closed.")); throw new SQLException(TSDBConstants.WrapErrMsg("restful TDengine connection is closed."));
return new RestfulStatement(this, this.database); return new RestfulStatement(this, database);
} }
@Override @Override
...@@ -104,22 +103,28 @@ public class RestfulConnection implements Connection { ...@@ -104,22 +103,28 @@ public class RestfulConnection implements Connection {
@Override @Override
public void setTransactionIsolation(int level) throws SQLException { public void setTransactionIsolation(int level) throws SQLException {
//transaction is not supported
throw new SQLFeatureNotSupportedException("transactions are not supported");
} }
/**
*
*/
@Override @Override
public int getTransactionIsolation() throws SQLException { public int getTransactionIsolation() throws SQLException {
return 0; //Connection.TRANSACTION_NONE specifies that transactions are not supported.
return Connection.TRANSACTION_NONE;
} }
@Override @Override
public SQLWarning getWarnings() throws SQLException { public SQLWarning getWarnings() throws SQLException {
//TODO: getWarnings not implemented
return null; return null;
} }
@Override @Override
public void clearWarnings() throws SQLException { public void clearWarnings() throws SQLException {
throw new SQLFeatureNotSupportedException("clearWarnings not supported.");
} }
@Override @Override
...@@ -209,22 +214,26 @@ public class RestfulConnection implements Connection { ...@@ -209,22 +214,26 @@ public class RestfulConnection implements Connection {
@Override @Override
public Clob createClob() throws SQLException { public Clob createClob() throws SQLException {
return null; //TODO: not supported
throw new SQLFeatureNotSupportedException();
} }
@Override @Override
public Blob createBlob() throws SQLException { public Blob createBlob() throws SQLException {
return null; //TODO: not supported
throw new SQLFeatureNotSupportedException();
} }
@Override @Override
public NClob createNClob() throws SQLException { public NClob createNClob() throws SQLException {
return null; //TODO: not supported
throw new SQLFeatureNotSupportedException();
} }
@Override @Override
public SQLXML createSQLXML() throws SQLException { public SQLXML createSQLXML() throws SQLException {
return null; //TODO: not supported
throw new SQLFeatureNotSupportedException();
} }
@Override @Override
...@@ -254,12 +263,14 @@ public class RestfulConnection implements Connection { ...@@ -254,12 +263,14 @@ public class RestfulConnection implements Connection {
@Override @Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException { public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return null; //TODO: not supported
throw new SQLFeatureNotSupportedException();
} }
@Override @Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException { public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return null; //TODO: not supported
throw new SQLFeatureNotSupportedException();
} }
@Override @Override
...@@ -289,12 +300,16 @@ public class RestfulConnection implements Connection { ...@@ -289,12 +300,16 @@ public class RestfulConnection implements Connection {
@Override @Override
public <T> T unwrap(Class<T> iface) throws SQLException { public <T> T unwrap(Class<T> iface) throws SQLException {
return null; try {
return iface.cast(this);
} catch (ClassCastException cce) {
throw new SQLException("Unable to unwrap to " + iface.toString());
}
} }
@Override @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException { public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false; return iface.isInstance(this);
} }
public String getHost() { public String getHost() {
......
...@@ -35,7 +35,7 @@ public class RestfulDriver extends AbstractTaosDriver { ...@@ -35,7 +35,7 @@ public class RestfulDriver extends AbstractTaosDriver {
Properties props = parseURL(url, info); Properties props = parseURL(url, info);
String host = props.getProperty(TSDBDriver.PROPERTY_KEY_HOST, "localhost"); String host = props.getProperty(TSDBDriver.PROPERTY_KEY_HOST, "localhost");
String port = props.getProperty(TSDBDriver.PROPERTY_KEY_PORT, "6041"); String port = props.getProperty(TSDBDriver.PROPERTY_KEY_PORT, "6041");
String database = props.getProperty(TSDBDriver.PROPERTY_KEY_DBNAME); String database = props.containsKey(TSDBDriver.PROPERTY_KEY_DBNAME) ? props.getProperty(TSDBDriver.PROPERTY_KEY_DBNAME) : null;
String loginUrl = "http://" + props.getProperty(TSDBDriver.PROPERTY_KEY_HOST) + ":" String loginUrl = "http://" + props.getProperty(TSDBDriver.PROPERTY_KEY_HOST) + ":"
+ props.getProperty(TSDBDriver.PROPERTY_KEY_PORT) + "/rest/login/" + props.getProperty(TSDBDriver.PROPERTY_KEY_PORT) + "/rest/login/"
......
...@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON; ...@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.taosdata.jdbc.TSDBConstants; import com.taosdata.jdbc.TSDBConstants;
import com.taosdata.jdbc.rs.util.HttpClientPoolUtil; import com.taosdata.jdbc.rs.util.HttpClientPoolUtil;
import com.taosdata.jdbc.utils.SqlSyntaxValidator;
import java.sql.*; import java.sql.*;
import java.util.Arrays; import java.util.Arrays;
...@@ -11,19 +12,23 @@ import java.util.List; ...@@ -11,19 +12,23 @@ import java.util.List;
public class RestfulStatement implements Statement { public class RestfulStatement implements Statement {
private final String catalog; private boolean closed;
private String database;
private final RestfulConnection conn; private final RestfulConnection conn;
public RestfulStatement(RestfulConnection c, String catalog) { public RestfulStatement(RestfulConnection c, String database) {
this.conn = c; this.conn = c;
this.catalog = catalog; this.database = database;
} }
@Override @Override
public ResultSet executeQuery(String sql) throws SQLException { public ResultSet executeQuery(String sql) throws SQLException {
if (isClosed())
throw new SQLException("statement already closed");
if (!SqlSyntaxValidator.isSelectSql(sql))
throw new SQLException("not a select sql for executeQuery: " + sql);
final String url = "http://" + conn.getHost() + ":"+conn.getPort()+"/rest/sql"; final String url = "http://" + conn.getHost() + ":" + conn.getPort() + "/rest/sql";
String result = HttpClientPoolUtil.execute(url, sql); String result = HttpClientPoolUtil.execute(url, sql);
String fields = ""; String fields = "";
List<String> words = Arrays.asList(sql.split(" ")); List<String> words = Arrays.asList(sql.split(" "));
...@@ -65,12 +70,29 @@ public class RestfulStatement implements Statement { ...@@ -65,12 +70,29 @@ public class RestfulStatement implements Statement {
@Override @Override
public int executeUpdate(String sql) throws SQLException { public int executeUpdate(String sql) throws SQLException {
return 0; if (isClosed())
throw new SQLException("statement already closed");
if (!SqlSyntaxValidator.isValidForExecuteUpdate(sql))
throw new SQLException("not a valid sql for executeUpdate: " + sql);
if (this.database == null)
throw new SQLException("Database not specified or available");
final String url = "http://" + conn.getHost() + ":" + conn.getPort() + "/rest/sql";
HttpClientPoolUtil.execute(url, "use " + conn.getDatabase());
String result = HttpClientPoolUtil.execute(url, sql);
JSONObject jsonObject = JSON.parseObject(result);
if (jsonObject.getString("status").equals("error")) {
throw new SQLException(TSDBConstants.WrapErrMsg("SQL execution error: " +
jsonObject.getString("desc") + "\n" +
"error code: " + jsonObject.getString("code")));
}
return Integer.parseInt(jsonObject.getString("rows"));
} }
@Override @Override
public void close() throws SQLException { public void close() throws SQLException {
this.closed = true;
} }
@Override @Override
...@@ -115,6 +137,7 @@ public class RestfulStatement implements Statement { ...@@ -115,6 +137,7 @@ public class RestfulStatement implements Statement {
@Override @Override
public SQLWarning getWarnings() throws SQLException { public SQLWarning getWarnings() throws SQLException {
//TODO: getWarnings not Implemented
return null; return null;
} }
...@@ -130,7 +153,29 @@ public class RestfulStatement implements Statement { ...@@ -130,7 +153,29 @@ public class RestfulStatement implements Statement {
@Override @Override
public boolean execute(String sql) throws SQLException { public boolean execute(String sql) throws SQLException {
return false; if (isClosed()) {
throw new SQLException("Invalid method call on a closed statement.");
}
//如果执行了use操作应该将当前Statement的catalog设置为新的database
if (SqlSyntaxValidator.isUseSql(sql)) {
this.database = sql.trim().replace("use", "").trim();
}
if (this.database == null)
throw new SQLException("Database not specified or available");
final String url = "http://" + conn.getHost() + ":" + conn.getPort() + "/rest/sql";
// use database
HttpClientPoolUtil.execute(url, "use " + conn.getDatabase());
// execute sql
String result = HttpClientPoolUtil.execute(url, sql);
// parse result
JSONObject jsonObject = JSON.parseObject(result);
if (jsonObject.getString("status").equals("error")) {
throw new SQLException(TSDBConstants.WrapErrMsg("SQL execution error: " +
jsonObject.getString("desc") + "\n" +
"error code: " + jsonObject.getString("code")));
}
return true;
} }
@Override @Override
...@@ -245,7 +290,7 @@ public class RestfulStatement implements Statement { ...@@ -245,7 +290,7 @@ public class RestfulStatement implements Statement {
@Override @Override
public boolean isClosed() throws SQLException { public boolean isClosed() throws SQLException {
return false; return closed;
} }
@Override @Override
...@@ -270,11 +315,15 @@ public class RestfulStatement implements Statement { ...@@ -270,11 +315,15 @@ public class RestfulStatement implements Statement {
@Override @Override
public <T> T unwrap(Class<T> iface) throws SQLException { public <T> T unwrap(Class<T> iface) throws SQLException {
return null; try {
return iface.cast(this);
} catch (ClassCastException cce) {
throw new SQLException("Unable to unwrap to " + iface.toString());
}
} }
@Override @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException { public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false; return iface.isInstance(this);
} }
} }
...@@ -22,6 +22,9 @@ import java.sql.SQLException; ...@@ -22,6 +22,9 @@ import java.sql.SQLException;
public class SqlSyntaxValidator { public class SqlSyntaxValidator {
private static final String[] updateSQL = {"insert", "update", "delete", "create", "alter", "drop", "show", "describe", "use"};
private static final String[] querySQL = {"select"};
private TSDBConnection tsdbConnection; private TSDBConnection tsdbConnection;
public SqlSyntaxValidator(Connection connection) { public SqlSyntaxValidator(Connection connection) {
...@@ -34,7 +37,7 @@ public class SqlSyntaxValidator { ...@@ -34,7 +37,7 @@ public class SqlSyntaxValidator {
if (tsdbConnection == null || tsdbConnection.isClosed()) { if (tsdbConnection == null || tsdbConnection.isClosed()) {
throw new SQLException("invalid connection"); throw new SQLException("invalid connection");
} else { } else {
TSDBJNIConnector jniConnector = tsdbConnection.getConnection(); TSDBJNIConnector jniConnector = tsdbConnection.getConnection();
if (jniConnector == null) { if (jniConnector == null) {
throw new SQLException("jniConnector is null"); throw new SQLException("jniConnector is null");
} else { } else {
...@@ -43,4 +46,28 @@ public class SqlSyntaxValidator { ...@@ -43,4 +46,28 @@ public class SqlSyntaxValidator {
} }
return res; return res;
} }
public static boolean isValidForExecuteUpdate(String sql) {
for (String prefix : updateSQL) {
if (sql.trim().toLowerCase().startsWith(prefix))
return true;
}
return false;
}
public static boolean isUseSql(String sql) {
return sql.trim().toLowerCase().startsWith(updateSQL[8]) || sql.trim().toLowerCase().matches("create\\s*database.*") || sql.toLowerCase().toLowerCase().matches("drop\\s*database.*");
}
public static boolean isUpdateSql(String sql) {
return sql.trim().toLowerCase().startsWith(updateSQL[1]);
}
public static boolean isInsertSql(String sql) {
return sql.trim().toLowerCase().startsWith(updateSQL[0]);
}
public static boolean isSelectSql(String sql) {
return sql.trim().toLowerCase().startsWith(querySQL[0]);
}
} }
...@@ -7,29 +7,6 @@ import java.sql.*; ...@@ -7,29 +7,6 @@ import java.sql.*;
public class RestfulDriverTest { public class RestfulDriverTest {
@Test
public void testCase001() {
try {
Class.forName("com.taosdata.jdbc.rs.RestfulDriver");
Connection connection = DriverManager.getConnection("jdbc:TAOS-RS://master:6041/?user=root&password=taosdata");
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from log.log");
ResultSetMetaData metaData = resultSet.getMetaData();
while (resultSet.next()) {
for (int i = 1; i <= metaData.getColumnCount(); i++) {
String column = metaData.getColumnLabel(i);
String value = resultSet.getString(i);
System.out.print(column + ":" + value + "\t");
}
System.out.println();
}
statement.close();
connection.close();
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
}
}
@Test @Test
public void connect() { public void connect() {
......
package com.taosdata.jdbc.rs;
import org.junit.*;
import org.junit.runners.MethodSorters;
import java.sql.*;
import java.util.Random;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class RestfulJDBCTest {
private Connection connection;
@Before
public void before() throws ClassNotFoundException, SQLException {
Class.forName("com.taosdata.jdbc.rs.RestfulDriver");
connection = DriverManager.getConnection("jdbc:TAOS-RS://master:6041/restful_test?user=root&password=taosdata");
}
@After
public void after() throws SQLException {
if (connection != null)
connection.close();
}
/**
* 查询所有log.log
**/
@Test
public void testCase001() {
try {
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from log.log");
ResultSetMetaData metaData = resultSet.getMetaData();
while (resultSet.next()) {
for (int i = 1; i <= metaData.getColumnCount(); i++) {
String column = metaData.getColumnLabel(i);
String value = resultSet.getString(i);
System.out.print(column + ":" + value + "\t");
}
System.out.println();
}
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* create database
*/
@Test
public void testCase002() {
try (Statement stmt = connection.createStatement()) {
stmt.execute("drop database if exists restful_test");
stmt.execute("create database if not exists restful_test");
stmt.execute("use restful_test");
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* create super table
***/
@Test
public void testCase003() {
try (Statement stmt = connection.createStatement()) {
stmt.execute("create table weather(ts timestamp, temperature float, humidity int) tags(location nchar(64), groupId int)");
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
public void testCase004() {
try (Statement stmt = connection.createStatement()) {
for (int i = 1; i <= 100; i++) {
stmt.execute("create table t" + i + " using weather tags('beijing', '" + i + "')");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private Random random = new Random(System.currentTimeMillis());
@Test
public void testCase005() {
try (Statement stmt = connection.createStatement()) {
int rows = 0;
for (int i = 0; i < 10; i++) {
for (int j = 1; j <= 100; j++) {
long currentTimeMillis = System.currentTimeMillis();
int affectRows = stmt.executeUpdate("insert into t" + j + " values(" + currentTimeMillis + "," + (random.nextFloat() * 50) + "," + random.nextInt(100) + ")");
Assert.assertEquals(1, affectRows);
rows += affectRows;
}
}
Assert.assertEquals(1000, rows);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
package com.taosdata.jdbc.utils;
import com.sun.source.tree.AssertTree;
import org.junit.Assert;
import org.junit.Test;
public class SqlSyntaxValidatorTest {
@Test
public void validateSqlSyntax() {
}
@Test
public void isSelectSQL() {
Assert.assertTrue(SqlSyntaxValidator.isSelectSql("select * from test.weather"));
Assert.assertTrue(SqlSyntaxValidator.isSelectSql(" select * from test.weather"));
Assert.assertTrue(SqlSyntaxValidator.isSelectSql(" select * from test.weather "));
Assert.assertFalse(SqlSyntaxValidator.isSelectSql("insert into test.weather values(now, 1.1, 2)"));
}
@Test
public void isUseSQL() {
Assert.assertTrue(SqlSyntaxValidator.isUseSql("use database test"));
Assert.assertTrue(SqlSyntaxValidator.isUseSql("create database test"));
Assert.assertTrue(SqlSyntaxValidator.isUseSql("create database if not exist test"));
Assert.assertTrue(SqlSyntaxValidator.isUseSql("drop database test"));
Assert.assertTrue(SqlSyntaxValidator.isUseSql("drop database if exist test"));
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册