未验证 提交 15d7c397 编写于 作者: Z zyyang 提交者: GitHub

Merge branch 'develop' into docs/Update-Latest-Feature

...@@ -13,7 +13,7 @@ branches: ...@@ -13,7 +13,7 @@ branches:
matrix: matrix:
- os: linux - os: linux
dist: focal dist: bionic
language: c language: c
git: git:
...@@ -28,8 +28,8 @@ matrix: ...@@ -28,8 +28,8 @@ matrix:
- build-essential - build-essential
- cmake - cmake
- net-tools - net-tools
- python3-pip - python3.8
- python3-setuptools - libc6-dbg
- valgrind - valgrind
- psmisc - psmisc
- unixodbc - unixodbc
...@@ -39,6 +39,8 @@ matrix: ...@@ -39,6 +39,8 @@ matrix:
before_script: before_script:
- export TZ=Asia/Harbin - export TZ=Asia/Harbin
- date - date
- curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && python3.8 get-pip.py
- python3.8 -m pip install --upgrade pip setuptools
- cd ${TRAVIS_BUILD_DIR} - cd ${TRAVIS_BUILD_DIR}
- mkdir debug - mkdir debug
- cd debug - cd debug
......
...@@ -14,11 +14,12 @@ ...@@ -14,11 +14,12 @@
*****************************************************************************/ *****************************************************************************/
package com.taosdata.jdbc; package com.taosdata.jdbc;
import com.taosdata.jdbc.utils.Utils;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.net.URL; import java.net.URL;
import java.nio.charset.Charset;
import java.sql.*; import java.sql.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
...@@ -126,28 +127,7 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat ...@@ -126,28 +127,7 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat
* @return a string of the native sql statement for TSDB * @return a string of the native sql statement for TSDB
*/ */
private String getNativeSql(String rawSql) throws SQLException { private String getNativeSql(String rawSql) throws SQLException {
String sql = rawSql; return Utils.getNativeSql(rawSql, this.parameters);
for (int i = 0; i < parameters.length; ++i) {
Object para = parameters[i];
if (para != null) {
String paraStr;
if (para instanceof byte[]) {
paraStr = new String((byte[]) para, Charset.forName("UTF-8"));
} else {
paraStr = para.toString();
}
// if para is timestamp or String or byte[] need to translate ' character
if (para instanceof Timestamp || para instanceof String || para instanceof byte[]) {
paraStr = paraStr.replaceAll("'", "\\\\\\\\'");
paraStr = "'" + paraStr + "'";
}
sql = sql.replaceFirst("[?]", paraStr);
} else {
sql = sql.replaceFirst("[?]", "NULL");
}
}
clearParameters();
return sql;
} }
@Override @Override
...@@ -275,7 +255,7 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat ...@@ -275,7 +255,7 @@ public class TSDBPreparedStatement extends TSDBStatement implements PreparedStat
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
if (isClosed()) if (isClosed())
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED); throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
setObject(parameterIndex,x); setObject(parameterIndex, x);
} }
@Override @Override
......
package com.taosdata.jdbc.rs; package com.taosdata.jdbc.rs;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
import com.taosdata.jdbc.TSDBError; import com.taosdata.jdbc.TSDBError;
import com.taosdata.jdbc.TSDBErrorNumbers; import com.taosdata.jdbc.TSDBErrorNumbers;
import com.taosdata.jdbc.utils.SqlSyntaxValidator;
import com.taosdata.jdbc.utils.Utils;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
...@@ -10,6 +15,12 @@ import java.net.URL; ...@@ -10,6 +15,12 @@ import java.net.URL;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.sql.*; import java.sql.*;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class RestfulPreparedStatement extends RestfulStatement implements PreparedStatement { public class RestfulPreparedStatement extends RestfulStatement implements PreparedStatement {
...@@ -21,6 +32,7 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar ...@@ -21,6 +32,7 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar
public RestfulPreparedStatement(RestfulConnection conn, String database, String sql) { public RestfulPreparedStatement(RestfulConnection conn, String database, String sql) {
super(conn, database); super(conn, database);
this.rawSql = sql; this.rawSql = sql;
if (sql.contains("?")) { if (sql.contains("?")) {
int parameterCnt = 0; int parameterCnt = 0;
for (int i = 0; i < sql.length(); i++) { for (int i = 0; i < sql.length(); i++) {
...@@ -58,29 +70,14 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar ...@@ -58,29 +70,14 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar
return executeUpdate(sql); return executeUpdate(sql);
} }
private String getNativeSql(String rawSql) throws SQLException { /****
String sql = rawSql; * 将rawSql转换成一条可执行的sql语句,使用属性parameters中的变脸进行替换
for (int i = 0; i < parameters.length; ++i) { * 对于insert into ?.? (?,?,?) using ?.? (?,?,?) tags(?, ?, ?) values(?, ?, ?)
Object para = parameters[i]; * @param rawSql,可能是insert、select或其他,使用?做占位符
if (para != null) { * @return
String paraStr; */
if (para instanceof byte[]) { private String getNativeSql(String rawSql) {
paraStr = new String((byte[]) para, Charset.forName("UTF-8")); return Utils.getNativeSql(rawSql, this.parameters);
} else {
paraStr = para.toString();
}
// if para is timestamp or String or byte[] need to translate ' character
if (para instanceof Timestamp || para instanceof String || para instanceof byte[]) {
paraStr = paraStr.replaceAll("'", "\\\\\\\\'");
paraStr = "'" + paraStr + "'";
}
sql = sql.replaceFirst("[?]", paraStr);
} else {
sql = sql.replaceFirst("[?]", "NULL");
}
}
clearParameters();
return sql;
} }
@Override @Override
...@@ -221,7 +218,7 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar ...@@ -221,7 +218,7 @@ public class RestfulPreparedStatement extends RestfulStatement implements Prepar
if (isClosed()) if (isClosed())
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED); throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_STATEMENT_CLOSED);
setObject(parameterIndex,x); setObject(parameterIndex, x);
} }
@Override @Override
......
...@@ -136,21 +136,21 @@ public class RestfulStatement extends AbstractStatement { ...@@ -136,21 +136,21 @@ public class RestfulStatement extends AbstractStatement {
throw TSDBError.createSQLException(jsonObject.getInteger("code"), jsonObject.getString("desc")); throw TSDBError.createSQLException(jsonObject.getInteger("code"), jsonObject.getString("desc"));
} }
this.resultSet = null; this.resultSet = null;
this.affectedRows = checkJsonResultSet(jsonObject); this.affectedRows = getAffectedRows(jsonObject);
return this.affectedRows; return this.affectedRows;
} }
private int checkJsonResultSet(JSONObject jsonObject) { private int getAffectedRows(JSONObject jsonObject) throws SQLException {
// create ... SQLs should return 0 , and Restful result is this: // create ... SQLs should return 0 , and Restful result is this:
// {"status": "succ", "head": ["affected_rows"], "data": [[0]], "rows": 1} // {"status": "succ", "head": ["affected_rows"], "data": [[0]], "rows": 1}
JSONArray head = jsonObject.getJSONArray("head"); JSONArray head = jsonObject.getJSONArray("head");
if (head.size() != 1 || !"affected_rows".equals(head.getString(0)))
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE);
JSONArray data = jsonObject.getJSONArray("data"); JSONArray data = jsonObject.getJSONArray("data");
int rows = Integer.parseInt(jsonObject.getString("rows")); if (data != null)
if (head.size() == 1 && "affected_rows".equals(head.getString(0)) return data.getJSONArray(0).getInteger(0);
&& data.size() == 1 && data.getJSONArray(0).getInteger(0) == 0 && rows == 1) {
return 0; throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_VARIABLE);
}
return rows;
} }
@Override @Override
......
package com.taosdata.jdbc.utils;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
import java.nio.charset.Charset;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Utils {
private static Pattern ptn = Pattern.compile(".*?'");
public static String escapeSingleQuota(String origin) {
Matcher m = ptn.matcher(origin);
StringBuffer sb = new StringBuffer();
int end = 0;
while (m.find()) {
end = m.end();
String seg = origin.substring(m.start(), end);
int len = seg.length();
if (len == 1) {
if ('\'' == seg.charAt(0)) {
sb.append("\\'");
} else {
sb.append(seg);
}
} else { // len > 1
sb.append(seg.substring(0, seg.length() - 2));
char lastcSec = seg.charAt(seg.length() - 2);
if (lastcSec == '\\') {
sb.append("\\'");
} else {
sb.append(lastcSec);
sb.append("\\'");
}
}
}
if (end < origin.length()) {
sb.append(origin.substring(end));
}
return sb.toString();
}
public static String getNativeSql(String rawSql, Object[] parameters) {
// toLowerCase
String preparedSql = rawSql.trim().toLowerCase();
String[] clause = new String[0];
if (SqlSyntaxValidator.isInsertSql(preparedSql)) {
// insert or import
clause = new String[]{"values\\s*\\(.*?\\)", "tags\\s*\\(.*?\\)"};
}
if (SqlSyntaxValidator.isSelectSql(preparedSql)) {
// select
clause = new String[]{"where\\s*.*"};
}
Map<Integer, Integer> placeholderPositions = new HashMap<>();
RangeSet<Integer> clauseRangeSet = TreeRangeSet.create();
findPlaceholderPosition(preparedSql, placeholderPositions);
findClauseRangeSet(preparedSql, clause, clauseRangeSet);
return transformSql(preparedSql, parameters, placeholderPositions, clauseRangeSet);
}
private static void findClauseRangeSet(String preparedSql, String[] regexArr, RangeSet<Integer> clauseRangeSet) {
clauseRangeSet.clear();
for (String regex : regexArr) {
Matcher matcher = Pattern.compile(regex).matcher(preparedSql);
while (matcher.find()) {
int start = matcher.start();
int end = matcher.end();
clauseRangeSet.add(Range.closed(start, end));
}
}
}
private static void findPlaceholderPosition(String preparedSql, Map<Integer, Integer> placeholderPosition) {
placeholderPosition.clear();
Matcher matcher = Pattern.compile("\\?").matcher(preparedSql);
int index = 0;
while (matcher.find()) {
int pos = matcher.start();
placeholderPosition.put(index, pos);
index++;
}
}
/***
*
* @param preparedSql
* @param paramArr
* @param placeholderPosition
* @param clauseRangeSet
* @return
*/
private static String transformSql(String preparedSql, Object[] paramArr, Map<Integer, Integer> placeholderPosition, RangeSet<Integer> clauseRangeSet) {
String[] sqlArr = preparedSql.split("\\?");
return IntStream.range(0, sqlArr.length).mapToObj(index -> {
if (index == paramArr.length)
return sqlArr[index];
Object para = paramArr[index];
String paraStr;
if (para != null) {
if (para instanceof byte[]) {
paraStr = new String((byte[]) para, Charset.forName("UTF-8"));
} else {
paraStr = para.toString();
}
// if para is timestamp or String or byte[] need to translate ' character
if (para instanceof Timestamp || para instanceof String || para instanceof byte[]) {
paraStr = Utils.escapeSingleQuota(paraStr);
Integer pos = placeholderPosition.get(index);
boolean contains = clauseRangeSet.contains(pos);
if (contains) {
paraStr = "'" + paraStr + "'";
}
}
} else {
paraStr = "NULL";
}
return sqlArr[index] + paraStr;
}).collect(Collectors.joining());
}
}
package com.taosdata.jdbc.cases;
import org.junit.*;
import java.sql.*;
public class InsertSpecialCharacterJniTest {
private static final String host = "127.0.0.1";
private static Connection conn;
private static String dbName = "spec_char_test";
private static String tbname1 = "test";
private static String tbname2 = "weather";
private static String special_character_str_1 = "$asd$$fsfsf$";
private static String special_character_str_2 = "\\asdfsfsf\\\\";
private static String special_character_str_3 = "\\\\asdfsfsf\\";
private static String special_character_str_4 = "?asd??fsf?sf?";
private static String special_character_str_5 = "?#sd@$f(('<(s[P)>\"){]}f?s[]{}%vaew|\"fsfs^a&d*jhg)(j))(f@~!?$";
@Test
public void testCase01() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1) values(?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_1.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from ?";
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
pstmt.setString(1, tbname1);
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_1, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test
public void testCase02() throws SQLException {
//TODO:
// Expected :\asdfsfsf\\
// Actual :\asdfsfsf\
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1) values(?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_2.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from " + tbname1;
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
//TODO: bug to be fixed
// Assert.assertEquals(special_character_str_2, f1);
Assert.assertEquals(special_character_str_2.substring(0, special_character_str_1.length() - 2), f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test(expected = SQLException.class)
public void testCase03() throws SQLException {
//TODO:
// TDengine ERROR (216): Syntax error in SQL
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1) values(?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_3.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from " + tbname1;
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_3, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test
public void testCase04() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1) values(?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_4.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from " + tbname1;
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_4, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test
public void testCase05() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1) values(?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_5.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from " + tbname1;
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_5, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test
public void testCase06() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into t? using " + tbname2 + " tags(?) values(?, ?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 1);
pstmt.setString(2, special_character_str_4);
pstmt.setTimestamp(3, new Timestamp(now));
pstmt.setBytes(4, special_character_str_4.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query t1
final String query = "select * from t1";
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_4, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test
public void testCase07() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1, f2) values(?, ?, ?) ; ";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_4.getBytes());
pstmt.setString(3, special_character_str_4);
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from " + tbname1;
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_4, f1);
String f2 = rs.getString(3);
Assert.assertEquals(special_character_str_4, f2);
}
}
@Test(expected = SQLException.class)
public void testCase08() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into t? using " + tbname2 + " tags(?) values(?, ?, ?) ? ";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 1);
pstmt.setString(2, special_character_str_5);
pstmt.setTimestamp(3, new Timestamp(now));
pstmt.setBytes(4, special_character_str_5.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
}
@Test
public void testCase09() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into ?.t? using " + tbname2 + " tags(?) values(?, ?, ?) t? using weather tags(?) values(?,?,?) ";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
// t1
pstmt.setString(1, dbName);
pstmt.setInt(2, 1);
pstmt.setString(3, special_character_str_5);
pstmt.setTimestamp(4, new Timestamp(now));
pstmt.setBytes(5, special_character_str_5.getBytes());
// t2
pstmt.setInt(7, 2);
pstmt.setString(8, special_character_str_5);
pstmt.setTimestamp(9, new Timestamp(now));
pstmt.setString(11, special_character_str_5);
int ret = pstmt.executeUpdate();
Assert.assertEquals(2, ret);
}
// query t1
String query = "select * from t?";
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
pstmt.setInt(1, 1);
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_5, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
// query t2
query = "select * from t2";
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
byte[] f1 = rs.getBytes(2);
Assert.assertNull(f1);
String f2 = new String(rs.getBytes(3));
Assert.assertEquals(special_character_str_5, f2);
}
}
@Test
public void testCase10() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into t? using ? tags(?) values(?, ?, ?) t? using " + tbname2 + " tags(?) values(?,?,?) ";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
// t1
pstmt.setInt(1, 1);
pstmt.setString(2, tbname2);
pstmt.setString(3, special_character_str_5);
pstmt.setTimestamp(4, new Timestamp(now));
pstmt.setBytes(5, special_character_str_5.getBytes());
// t2
pstmt.setInt(7, 2);
pstmt.setString(8, special_character_str_5);
pstmt.setTimestamp(9, new Timestamp(now));
pstmt.setString(11, special_character_str_5);
int ret = pstmt.executeUpdate();
Assert.assertEquals(2, ret);
}
//query t1
String query = "select * from ?.t? where ts < ? and ts >= ? and ? is not null";
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
pstmt.setString(1, dbName);
pstmt.setInt(2, 1);
pstmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
pstmt.setTimestamp(4, new Timestamp(0));
pstmt.setString(5, "f1");
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_5, f1);
byte[] f2 = rs.getBytes(3);
Assert.assertNull(f2);
}
// query t2
query = "select * from t? where ts < ? and ts >= ? and ? is not null";
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
pstmt.setInt(1, 2);
pstmt.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
pstmt.setTimestamp(3, new Timestamp(0));
pstmt.setString(4, "f2");
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
byte[] f1 = rs.getBytes(2);
Assert.assertNull(f1);
String f2 = new String(rs.getBytes(3));
Assert.assertEquals(special_character_str_5, f2);
}
}
@Test(expected = SQLException.class)
public void testCase11() throws SQLException {
final String speicalCharacterStr = "?#sd@$f(((s[P)){]}f?s[]{}%vs^a&d*jhg)(j))(f@~!?$";
final long now = System.currentTimeMillis();
final String sql = "insert into t? using " + tbname2 + " values(?, ?, 'abc?abc') ";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 1);
pstmt.setTimestamp(2, new Timestamp(now));
pstmt.setBytes(3, speicalCharacterStr.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
}
@Before
public void before() throws SQLException {
try (Statement stmt = conn.createStatement()) {
stmt.execute("drop table if exists " + tbname1 + "");
stmt.execute("create table " + tbname1 + "(ts timestamp,f1 binary(64),f2 nchar(64))");
stmt.execute("drop table if exists " + tbname2);
stmt.execute("create table " + tbname2 + "(ts timestamp, f1 binary(64), f2 nchar(64)) tags(loc nchar(64))");
}
}
@BeforeClass
public static void beforeClass() throws SQLException {
String url = "jdbc:TAOS://" + host + ":6030/?user=root&password=taosdata";
conn = DriverManager.getConnection(url);
try (Statement stmt = conn.createStatement()) {
stmt.execute("drop database if exists " + dbName);
stmt.execute("create database if not exists " + dbName);
stmt.execute("use " + dbName);
}
}
@AfterClass
public static void afterClass() throws SQLException {
if (conn != null)
conn.close();
}
}
package com.taosdata.jdbc.cases;
import org.junit.*;
import java.sql.*;
public class InsertSpecialCharacterRestfulTest {
private static final String host = "127.0.0.1";
// private static final String host = "master";
private static Connection conn;
private static String dbName = "spec_char_test";
private static String tbname1 = "test";
private static String tbname2 = "weather";
private static String special_character_str_1 = "$asd$$fsfsf$";
private static String special_character_str_2 = "\\asdfsfsf\\\\";
private static String special_character_str_3 = "\\\\asdfsfsf\\";
private static String special_character_str_4 = "?asd??fsf?sf?";
private static String special_character_str_5 = "?#sd@$f(('<(s[P)>\"){]}f?s[]{}%vaew|\"fsfs^a&d*jhg)(j))(f@~!?$";
@Test
public void testCase01() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1) values(?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_1.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from ?";
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
pstmt.setString(1, tbname1);
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_1, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test
public void testCase02() throws SQLException {
//TODO:
// Expected :\asdfsfsf\\
// Actual :\asdfsfsf\
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1) values(?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_2.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from " + tbname1;
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
//TODO: bug to be fixed
// Assert.assertEquals(special_character_str_2, f1);
Assert.assertEquals(special_character_str_2.substring(0, special_character_str_1.length() - 2), f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test(expected = SQLException.class)
public void testCase03() throws SQLException {
//TODO:
// TDengine ERROR (216): Syntax error in SQL
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1) values(?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_3.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from " + tbname1;
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_3, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test
public void testCase04() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1) values(?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_4.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from " + tbname1;
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_4, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test
public void testCase05() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1) values(?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_5.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from " + tbname1;
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_5, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test
public void testCase06() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into t? using " + tbname2 + " tags(?) values(?, ?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 1);
pstmt.setString(2, special_character_str_4);
pstmt.setTimestamp(3, new Timestamp(now));
pstmt.setBytes(4, special_character_str_4.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query t1
final String query = "select * from t1";
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_4, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
}
@Test
public void testCase07() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into " + tbname1 + "(ts, f1, f2) values(?, ?, ?) ; ";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setTimestamp(1, new Timestamp(now));
pstmt.setBytes(2, special_character_str_4.getBytes());
pstmt.setString(3, special_character_str_4);
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
// query
final String query = "select * from " + tbname1;
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_4, f1);
String f2 = rs.getString(3);
Assert.assertEquals(special_character_str_4, f2);
}
}
@Test(expected = SQLException.class)
public void testCase08() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into t? using " + tbname2 + " tags(?) values(?, ?, ?) ? ";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 1);
pstmt.setString(2, special_character_str_5);
pstmt.setTimestamp(3, new Timestamp(now));
pstmt.setBytes(4, special_character_str_5.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
}
@Test
public void testCase09() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into ?.t? using " + tbname2 + " tags(?) values(?, ?, ?) t? using weather tags(?) values(?,?,?) ";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
// t1
pstmt.setString(1, dbName);
pstmt.setInt(2, 1);
pstmt.setString(3, special_character_str_5);
pstmt.setTimestamp(4, new Timestamp(now));
pstmt.setBytes(5, special_character_str_5.getBytes());
// t2
pstmt.setInt(7, 2);
pstmt.setString(8, special_character_str_5);
pstmt.setTimestamp(9, new Timestamp(now));
pstmt.setString(11, special_character_str_5);
int ret = pstmt.executeUpdate();
Assert.assertEquals(2, ret);
}
// query t1
String query = "select * from t?";
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
pstmt.setInt(1, 1);
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_5, f1);
String f2 = rs.getString(3);
Assert.assertNull(f2);
}
// query t2
query = "select * from t2";
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
byte[] f1 = rs.getBytes(2);
Assert.assertNull(f1);
String f2 = new String(rs.getBytes(3));
Assert.assertEquals(special_character_str_5, f2);
}
}
@Test
public void testCase10() throws SQLException {
final long now = System.currentTimeMillis();
// insert
final String sql = "insert into t? using ? tags(?) values(?, ?, ?) t? using " + tbname2 + " tags(?) values(?,?,?) ";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
// t1
pstmt.setInt(1, 1);
pstmt.setString(2, tbname2);
pstmt.setString(3, special_character_str_5);
pstmt.setTimestamp(4, new Timestamp(now));
pstmt.setBytes(5, special_character_str_5.getBytes());
// t2
pstmt.setInt(7, 2);
pstmt.setString(8, special_character_str_5);
pstmt.setTimestamp(9, new Timestamp(now));
pstmt.setString(11, special_character_str_5);
int ret = pstmt.executeUpdate();
Assert.assertEquals(2, ret);
}
//query t1
String query = "select * from ?.t? where ts < ? and ts >= ? and ? is not null";
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
pstmt.setString(1, dbName);
pstmt.setInt(2, 1);
pstmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
pstmt.setTimestamp(4, new Timestamp(0));
pstmt.setString(5, "f1");
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
String f1 = new String(rs.getBytes(2));
Assert.assertEquals(special_character_str_5, f1);
byte[] f2 = rs.getBytes(3);
Assert.assertNull(f2);
}
// query t2
query = "select * from t? where ts < ? and ts >= ? and ? is not null";
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
pstmt.setInt(1, 2);
pstmt.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
pstmt.setTimestamp(3, new Timestamp(0));
pstmt.setString(4, "f2");
ResultSet rs = pstmt.executeQuery();
rs.next();
long timestamp = rs.getTimestamp(1).getTime();
Assert.assertEquals(now, timestamp);
byte[] f1 = rs.getBytes(2);
Assert.assertNull(f1);
String f2 = new String(rs.getBytes(3));
Assert.assertEquals(special_character_str_5, f2);
}
}
@Test(expected = SQLException.class)
public void testCase11() throws SQLException {
final String speicalCharacterStr = "?#sd@$f(((s[P)){]}f?s[]{}%vs^a&d*jhg)(j))(f@~!?$";
final long now = System.currentTimeMillis();
final String sql = "insert into t? using " + tbname2 + " values(?, ?, 'abc?abc') ";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 1);
pstmt.setTimestamp(2, new Timestamp(now));
pstmt.setBytes(3, speicalCharacterStr.getBytes());
int ret = pstmt.executeUpdate();
Assert.assertEquals(1, ret);
}
}
@Before
public void before() throws SQLException {
try (Statement stmt = conn.createStatement()) {
stmt.execute("drop table if exists " + tbname1 + "");
stmt.execute("create table " + tbname1 + "(ts timestamp,f1 binary(64),f2 nchar(64))");
stmt.execute("drop table if exists " + tbname2);
stmt.execute("create table " + tbname2 + "(ts timestamp, f1 binary(64), f2 nchar(64)) tags(loc nchar(64))");
}
}
@BeforeClass
public static void beforeClass() throws SQLException {
String url = "jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata";
conn = DriverManager.getConnection(url);
try (Statement stmt = conn.createStatement()) {
stmt.execute("drop database if exists " + dbName);
stmt.execute("create database if not exists " + dbName);
stmt.execute("use " + dbName);
}
}
@AfterClass
public static void afterClass() throws SQLException {
if (conn != null)
conn.close();
}
}
...@@ -6,11 +6,11 @@ import org.junit.BeforeClass; ...@@ -6,11 +6,11 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable;
import java.sql.*; import java.sql.*;
public class RestfulPreparedStatementTest { public class RestfulPreparedStatementTest {
private static final String host = "127.0.0.1"; private static final String host = "127.0.0.1";
// private static final String host = "master";
private static Connection conn; private static Connection conn;
private static final String sql_insert = "insert into t1 values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; private static final String sql_insert = "insert into t1 values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
private static PreparedStatement pstmt_insert; private static PreparedStatement pstmt_insert;
......
package com.taosdata.jdbc.utils;
import org.junit.Assert;
import org.junit.Test;
import static org.junit.Assert.*;
public class UtilsTest {
@Test
public void escapeSingleQuota() {
String s = "'''''a\\'";
String news = Utils.escapeSingleQuota(s);
Assert.assertEquals("\\'\\'\\'\\'\\'a\\'", news);
s = "\'''''a\\'";
news = Utils.escapeSingleQuota(s);
Assert.assertEquals("\\'\\'\\'\\'\\'a\\'", news);
s = "\'\'\'\''a\\'";
news = Utils.escapeSingleQuota(s);
Assert.assertEquals("\\'\\'\\'\\'\\'a\\'", news);
}
}
\ No newline at end of file
...@@ -81,7 +81,7 @@ enum QUERY_MODE { ...@@ -81,7 +81,7 @@ enum QUERY_MODE {
#define MAX_DB_NAME_SIZE 64 #define MAX_DB_NAME_SIZE 64
#define MAX_HOSTNAME_SIZE 64 #define MAX_HOSTNAME_SIZE 64
#define MAX_TB_NAME_SIZE 64 #define MAX_TB_NAME_SIZE 64
#define MAX_DATA_SIZE (16*1024) #define MAX_DATA_SIZE (16*1024)+20 // max record len: 16*1024, timestamp string and ,('') need extra space
#define MAX_NUM_DATATYPE 10 #define MAX_NUM_DATATYPE 10
#define OPT_ABORT 1 /* –abort */ #define OPT_ABORT 1 /* –abort */
#define STRING_LEN 60000 #define STRING_LEN 60000
...@@ -241,7 +241,7 @@ typedef struct SSuperTable_S { ...@@ -241,7 +241,7 @@ typedef struct SSuperTable_S {
int8_t autoCreateTable; // 0: create sub table, 1: auto create sub table int8_t autoCreateTable; // 0: create sub table, 1: auto create sub table
char childTblPrefix[MAX_TB_NAME_SIZE]; char childTblPrefix[MAX_TB_NAME_SIZE];
char dataSource[MAX_TB_NAME_SIZE+1]; // rand_gen or sample char dataSource[MAX_TB_NAME_SIZE+1]; // rand_gen or sample
char insertMode[MAX_TB_NAME_SIZE]; // taosc, restful char insertMode[MAX_TB_NAME_SIZE]; // taosc, rest
int64_t childTblLimit; int64_t childTblLimit;
int64_t childTblOffset; int64_t childTblOffset;
...@@ -334,6 +334,8 @@ typedef struct SDataBase_S { ...@@ -334,6 +334,8 @@ typedef struct SDataBase_S {
typedef struct SDbs_S { typedef struct SDbs_S {
char cfgDir[MAX_FILE_NAME_LEN+1]; char cfgDir[MAX_FILE_NAME_LEN+1];
char host[MAX_HOSTNAME_SIZE]; char host[MAX_HOSTNAME_SIZE];
struct sockaddr_in serv_addr;
uint16_t port; uint16_t port;
char user[MAX_USERNAME_SIZE]; char user[MAX_USERNAME_SIZE];
char password[MAX_PASSWORD_SIZE]; char password[MAX_PASSWORD_SIZE];
...@@ -393,10 +395,11 @@ typedef struct SQueryMetaInfo_S { ...@@ -393,10 +395,11 @@ typedef struct SQueryMetaInfo_S {
char cfgDir[MAX_FILE_NAME_LEN+1]; char cfgDir[MAX_FILE_NAME_LEN+1];
char host[MAX_HOSTNAME_SIZE]; char host[MAX_HOSTNAME_SIZE];
uint16_t port; uint16_t port;
struct sockaddr_in serv_addr;
char user[MAX_USERNAME_SIZE]; char user[MAX_USERNAME_SIZE];
char password[MAX_PASSWORD_SIZE]; char password[MAX_PASSWORD_SIZE];
char dbName[MAX_DB_NAME_SIZE+1]; char dbName[MAX_DB_NAME_SIZE+1];
char queryMode[MAX_TB_NAME_SIZE]; // taosc, restful char queryMode[MAX_TB_NAME_SIZE]; // taosc, rest
SpecifiedQueryInfo specifiedQueryInfo; SpecifiedQueryInfo specifiedQueryInfo;
SuperQueryInfo superQueryInfo; SuperQueryInfo superQueryInfo;
...@@ -566,7 +569,7 @@ SArguments g_args = { ...@@ -566,7 +569,7 @@ SArguments g_args = {
1, // query_times 1, // query_times
0, // interlace_rows; 0, // interlace_rows;
30000, // num_of_RPR 30000, // num_of_RPR
1024000, // max_sql_len (1024*1024), // max_sql_len
10000, // num_of_tables 10000, // num_of_tables
10000, // num_of_DPT 10000, // num_of_DPT
0, // abort 0, // abort
...@@ -663,11 +666,11 @@ static void printHelp() { ...@@ -663,11 +666,11 @@ static void printHelp() {
printf("%s%s%s%s\n", indent, "-q", indent, printf("%s%s%s%s\n", indent, "-q", indent,
"Query mode -- 0: SYNC, 1: ASYNC. Default is SYNC."); "Query mode -- 0: SYNC, 1: ASYNC. Default is SYNC.");
printf("%s%s%s%s\n", indent, "-b", indent, printf("%s%s%s%s\n", indent, "-b", indent,
"The data_type of columns, default: TINYINT,SMALLINT,INT,BIGINT,FLOAT,DOUBLE,BINARY,NCHAR,BOOL,TIMESTAMP."); "The data_type of columns, default: INT,INT,INT,INT.");
printf("%s%s%s%s\n", indent, "-w", indent, printf("%s%s%s%s\n", indent, "-w", indent,
"The length of data_type 'BINARY' or 'NCHAR'. Default is 16"); "The length of data_type 'BINARY' or 'NCHAR'. Default is 16");
printf("%s%s%s%s\n", indent, "-l", indent, printf("%s%s%s%s\n", indent, "-l", indent,
"The number of columns per record. Default is 10."); "The number of columns per record. Default is 4.");
printf("%s%s%s%s\n", indent, "-T", indent, printf("%s%s%s%s\n", indent, "-T", indent,
"The number of threads. Default is 10."); "The number of threads. Default is 10.");
printf("%s%s%s%s\n", indent, "-i", indent, printf("%s%s%s%s\n", indent, "-i", indent,
...@@ -1188,13 +1191,31 @@ static float rand_float(){ ...@@ -1188,13 +1191,31 @@ static float rand_float(){
return randfloat[cursor]; return randfloat[cursor];
} }
#if 0
static const char charNum[] = "0123456789";
static void nonrand_string(char *, int) __attribute__ ((unused)); // reserve for debugging purpose
static void nonrand_string(char *str, int size)
{
str[0] = 0;
if (size > 0) {
int n;
for (n = 0; n < size; n++) {
str[n] = charNum[n % 10];
}
str[n] = 0;
}
}
#endif
static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
static void rand_string(char *str, int size) { static void rand_string(char *str, int size) {
str[0] = 0; str[0] = 0;
if (size > 0) { if (size > 0) {
//--size; //--size;
int n; int n;
for (n = 0; n < size - 1; n++) { for (n = 0; n < size; n++) {
int key = abs(rand_tinyint()) % (int)(sizeof(charset) - 1); int key = abs(rand_tinyint()) % (int)(sizeof(charset) - 1);
str[n] = charset[key]; str[n] = charset[key];
} }
...@@ -1955,14 +1976,12 @@ static void printfQuerySystemInfo(TAOS * taos) { ...@@ -1955,14 +1976,12 @@ static void printfQuerySystemInfo(TAOS * taos) {
free(dbInfos); free(dbInfos);
} }
static int postProceSql(char* host, uint16_t port, char* sqlstr) static int postProceSql(char *host, struct sockaddr_in *pServAddr, uint16_t port, char* sqlstr)
{ {
char *req_fmt = "POST %s HTTP/1.1\r\nHost: %s:%d\r\nAccept: */*\r\nAuthorization: Basic %s\r\nContent-Length: %d\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n%s"; char *req_fmt = "POST %s HTTP/1.1\r\nHost: %s:%d\r\nAccept: */*\r\nAuthorization: Basic %s\r\nContent-Length: %d\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n%s";
char *url = "/rest/sql"; char *url = "/rest/sql";
struct hostent *server;
struct sockaddr_in serv_addr;
int bytes, sent, received, req_str_len, resp_len; int bytes, sent, received, req_str_len, resp_len;
char *request_buf; char *request_buf;
char response_buf[RESP_BUF_LEN]; char response_buf[RESP_BUF_LEN];
...@@ -2011,27 +2030,7 @@ static int postProceSql(char* host, uint16_t port, char* sqlstr) ...@@ -2011,27 +2030,7 @@ static int postProceSql(char* host, uint16_t port, char* sqlstr)
ERROR_EXIT("ERROR opening socket"); ERROR_EXIT("ERROR opening socket");
} }
server = gethostbyname(host); int retConn = connect(sockfd, (struct sockaddr *)pServAddr, sizeof(struct sockaddr));
if (server == NULL) {
free(request_buf);
ERROR_EXIT("ERROR, no such host");
}
debugPrint("h_name: %s\nh_addretype: %s\nh_length: %d\n",
server->h_name,
(server->h_addrtype == AF_INET)?"ipv4":"ipv6",
server->h_length);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(rest_port);
#ifdef WINDOWS
serv_addr.sin_addr.s_addr = inet_addr(host);
#else
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
#endif
int retConn = connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
debugPrint("%s() LN%d connect() return %d\n", __func__, __LINE__, retConn); debugPrint("%s() LN%d connect() return %d\n", __func__, __LINE__, retConn);
if (retConn < 0) { if (retConn < 0) {
free(request_buf); free(request_buf);
...@@ -3335,7 +3334,7 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { ...@@ -3335,7 +3334,7 @@ static bool getMetaFromInsertJsonFile(cJSON* root) {
if (threads2 && threads2->type == cJSON_Number) { if (threads2 && threads2->type == cJSON_Number) {
g_Dbs.threadCountByCreateTbl = threads2->valueint; g_Dbs.threadCountByCreateTbl = threads2->valueint;
} else if (!threads2) { } else if (!threads2) {
g_Dbs.threadCountByCreateTbl = g_args.num_of_threads; g_Dbs.threadCountByCreateTbl = 1;
} else { } else {
errorPrint("%s() LN%d, failed to read json, threads2 not found\n", errorPrint("%s() LN%d, failed to read json, threads2 not found\n",
__func__, __LINE__); __func__, __LINE__);
...@@ -3379,7 +3378,7 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { ...@@ -3379,7 +3378,7 @@ static bool getMetaFromInsertJsonFile(cJSON* root) {
if (maxSqlLen && maxSqlLen->type == cJSON_Number) { if (maxSqlLen && maxSqlLen->type == cJSON_Number) {
g_args.max_sql_len = maxSqlLen->valueint; g_args.max_sql_len = maxSqlLen->valueint;
} else if (!maxSqlLen) { } else if (!maxSqlLen) {
g_args.max_sql_len = 1024000; g_args.max_sql_len = (1024*1024);
} else { } else {
errorPrint("%s() LN%d, failed to read json, max_sql_len input mistake\n", errorPrint("%s() LN%d, failed to read json, max_sql_len input mistake\n",
__func__, __LINE__); __func__, __LINE__);
...@@ -3724,7 +3723,7 @@ static bool getMetaFromInsertJsonFile(cJSON* root) { ...@@ -3724,7 +3723,7 @@ static bool getMetaFromInsertJsonFile(cJSON* root) {
goto PARSE_OVER; goto PARSE_OVER;
} }
cJSON *insertMode = cJSON_GetObjectItem(stbInfo, "insert_mode"); // taosc , restful cJSON *insertMode = cJSON_GetObjectItem(stbInfo, "insert_mode"); // taosc , rest
if (insertMode && insertMode->type == cJSON_String if (insertMode && insertMode->type == cJSON_String
&& insertMode->valuestring != NULL) { && insertMode->valuestring != NULL) {
tstrncpy(g_Dbs.db[i].superTbls[j].insertMode, tstrncpy(g_Dbs.db[i].superTbls[j].insertMode,
...@@ -4457,11 +4456,11 @@ static int64_t generateRowData(char* recBuf, int64_t timestamp, SSuperTable* stb ...@@ -4457,11 +4456,11 @@ static int64_t generateRowData(char* recBuf, int64_t timestamp, SSuperTable* stb
char *pstr = recBuf; char *pstr = recBuf;
int64_t maxLen = MAX_DATA_SIZE; int64_t maxLen = MAX_DATA_SIZE;
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, "(%" PRId64 ", ", timestamp); dataLen += snprintf(pstr + dataLen, maxLen - dataLen, "(%" PRId64 ",", timestamp);
for (int i = 0; i < stbInfo->columnCount; i++) { for (int i = 0; i < stbInfo->columnCount; i++) {
if ((0 == strncasecmp(stbInfo->columns[i].dataType, "binary", 6)) if ((0 == strncasecmp(stbInfo->columns[i].dataType, "BINARY", strlen("BINARY")))
|| (0 == strncasecmp(stbInfo->columns[i].dataType, "nchar", 5))) { || (0 == strncasecmp(stbInfo->columns[i].dataType, "NCHAR", strlen("NCHAR")))) {
if (stbInfo->columns[i].dataLen > TSDB_MAX_BINARY_LEN) { if (stbInfo->columns[i].dataLen > TSDB_MAX_BINARY_LEN) {
errorPrint( "binary or nchar length overflow, max size:%u\n", errorPrint( "binary or nchar length overflow, max size:%u\n",
(uint32_t)TSDB_MAX_BINARY_LEN); (uint32_t)TSDB_MAX_BINARY_LEN);
...@@ -4474,47 +4473,47 @@ static int64_t generateRowData(char* recBuf, int64_t timestamp, SSuperTable* stb ...@@ -4474,47 +4473,47 @@ static int64_t generateRowData(char* recBuf, int64_t timestamp, SSuperTable* stb
return -1; return -1;
} }
rand_string(buf, stbInfo->columns[i].dataLen); rand_string(buf, stbInfo->columns[i].dataLen);
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, "\'%s\', ", buf); dataLen += snprintf(pstr + dataLen, maxLen - dataLen, "\'%s\',", buf);
tmfree(buf); tmfree(buf);
} else if (0 == strncasecmp(stbInfo->columns[i].dataType, } else if (0 == strncasecmp(stbInfo->columns[i].dataType,
"int", 3)) { "INT", 3)) {
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, dataLen += snprintf(pstr + dataLen, maxLen - dataLen,
"%d, ", rand_int()); "%d,", rand_int());
} else if (0 == strncasecmp(stbInfo->columns[i].dataType, } else if (0 == strncasecmp(stbInfo->columns[i].dataType,
"bigint", 6)) { "BIGINT", 6)) {
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, dataLen += snprintf(pstr + dataLen, maxLen - dataLen,
"%"PRId64", ", rand_bigint()); "%"PRId64",", rand_bigint());
} else if (0 == strncasecmp(stbInfo->columns[i].dataType, } else if (0 == strncasecmp(stbInfo->columns[i].dataType,
"float", 5)) { "FLOAT", 5)) {
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, dataLen += snprintf(pstr + dataLen, maxLen - dataLen,
"%f, ", rand_float()); "%f,", rand_float());
} else if (0 == strncasecmp(stbInfo->columns[i].dataType, } else if (0 == strncasecmp(stbInfo->columns[i].dataType,
"double", 6)) { "DOUBLE", 6)) {
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, dataLen += snprintf(pstr + dataLen, maxLen - dataLen,
"%f, ", rand_double()); "%f,", rand_double());
} else if (0 == strncasecmp(stbInfo->columns[i].dataType, } else if (0 == strncasecmp(stbInfo->columns[i].dataType,
"smallint", 8)) { "SMALLINT", 8)) {
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, dataLen += snprintf(pstr + dataLen, maxLen - dataLen,
"%d, ", rand_smallint()); "%d,", rand_smallint());
} else if (0 == strncasecmp(stbInfo->columns[i].dataType, } else if (0 == strncasecmp(stbInfo->columns[i].dataType,
"tinyint", strlen("tinyint"))) { "TINYINT", strlen("TINYINT"))) {
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, dataLen += snprintf(pstr + dataLen, maxLen - dataLen,
"%d, ", rand_tinyint()); "%d,", rand_tinyint());
} else if (0 == strncasecmp(stbInfo->columns[i].dataType, } else if (0 == strncasecmp(stbInfo->columns[i].dataType,
"bool", strlen("bool"))) { "BOOL", strlen("BOOL"))) {
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, dataLen += snprintf(pstr + dataLen, maxLen - dataLen,
"%d, ", rand_bool()); "%d,", rand_bool());
} else if (0 == strncasecmp(stbInfo->columns[i].dataType, } else if (0 == strncasecmp(stbInfo->columns[i].dataType,
"timestamp", strlen("timestamp"))) { "TIMESTAMP", strlen("TIMESTAMP"))) {
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, dataLen += snprintf(pstr + dataLen, maxLen - dataLen,
"%"PRId64", ", rand_bigint()); "%"PRId64",", rand_bigint());
} else { } else {
errorPrint( "No support data type: %s\n", stbInfo->columns[i].dataType); errorPrint( "No support data type: %s\n", stbInfo->columns[i].dataType);
return -1; return -1;
} }
} }
dataLen -= 2; dataLen -= 1;
dataLen += snprintf(pstr + dataLen, maxLen - dataLen, ")"); dataLen += snprintf(pstr + dataLen, maxLen - dataLen, ")");
verbosePrint("%s() LN%d, recBuf:\n\t%s\n", __func__, __LINE__, recBuf); verbosePrint("%s() LN%d, recBuf:\n\t%s\n", __func__, __LINE__, recBuf);
...@@ -4541,31 +4540,31 @@ static int64_t generateData(char *recBuf, char **data_type, ...@@ -4541,31 +4540,31 @@ static int64_t generateData(char *recBuf, char **data_type,
} }
for (int i = 0; i < c; i++) { for (int i = 0; i < c; i++) {
if (strcasecmp(data_type[i % c], "tinyint") == 0) { if (strcasecmp(data_type[i % c], "TINYINT") == 0) {
pstr += sprintf(pstr, ", %d", rand_tinyint() ); pstr += sprintf(pstr, ",%d", rand_tinyint() );
} else if (strcasecmp(data_type[i % c], "smallint") == 0) { } else if (strcasecmp(data_type[i % c], "SMALLINT") == 0) {
pstr += sprintf(pstr, ", %d", rand_smallint()); pstr += sprintf(pstr, ",%d", rand_smallint());
} else if (strcasecmp(data_type[i % c], "int") == 0) { } else if (strcasecmp(data_type[i % c], "INT") == 0) {
pstr += sprintf(pstr, ", %d", rand_int()); pstr += sprintf(pstr, ",%d", rand_int());
} else if (strcasecmp(data_type[i % c], "bigint") == 0) { } else if (strcasecmp(data_type[i % c], "BIGINT") == 0) {
pstr += sprintf(pstr, ", %" PRId64, rand_bigint()); pstr += sprintf(pstr, ",%" PRId64, rand_bigint());
} else if (strcasecmp(data_type[i % c], "float") == 0) { } else if (strcasecmp(data_type[i % c], "FLOAT") == 0) {
pstr += sprintf(pstr, ", %10.4f", rand_float()); pstr += sprintf(pstr, ",%10.4f", rand_float());
} else if (strcasecmp(data_type[i % c], "double") == 0) { } else if (strcasecmp(data_type[i % c], "DOUBLE") == 0) {
double t = rand_double(); double t = rand_double();
pstr += sprintf(pstr, ", %20.8f", t); pstr += sprintf(pstr, ",%20.8f", t);
} else if (strcasecmp(data_type[i % c], "bool") == 0) { } else if (strcasecmp(data_type[i % c], "BOOL") == 0) {
bool b = taosRandom() & 1; bool b = taosRandom() & 1;
pstr += sprintf(pstr, ", %s", b ? "true" : "false"); pstr += sprintf(pstr, ",%s", b ? "true" : "false");
} else if (strcasecmp(data_type[i % c], "binary") == 0) { } else if (strcasecmp(data_type[i % c], "BINARY") == 0) {
char *s = malloc(lenOfBinary); char *s = malloc(lenOfBinary);
rand_string(s, lenOfBinary); rand_string(s, lenOfBinary);
pstr += sprintf(pstr, ", \"%s\"", s); pstr += sprintf(pstr, ",\"%s\"", s);
free(s); free(s);
} else if (strcasecmp(data_type[i % c], "nchar") == 0) { } else if (strcasecmp(data_type[i % c], "NCHAR") == 0) {
char *s = malloc(lenOfBinary); char *s = malloc(lenOfBinary);
rand_string(s, lenOfBinary); rand_string(s, lenOfBinary);
pstr += sprintf(pstr, ", \"%s\"", s); pstr += sprintf(pstr, ",\"%s\"", s);
free(s); free(s);
} }
...@@ -4619,14 +4618,18 @@ static int64_t execInsert(threadInfo *pThreadInfo, char *buffer, int k) ...@@ -4619,14 +4618,18 @@ static int64_t execInsert(threadInfo *pThreadInfo, char *buffer, int k)
if (superTblInfo) { if (superTblInfo) {
if (0 == strncasecmp(superTblInfo->insertMode, "taosc", strlen("taosc"))) { if (0 == strncasecmp(superTblInfo->insertMode, "taosc", strlen("taosc"))) {
affectedRows = queryDbExec(pThreadInfo->taos, buffer, INSERT_TYPE, false); affectedRows = queryDbExec(pThreadInfo->taos, buffer, INSERT_TYPE, false);
} else { } else if (0 == strncasecmp(superTblInfo->insertMode, "rest", strlen("rest"))) {
if (0 != postProceSql(g_Dbs.host, g_Dbs.port, buffer)) { if (0 != postProceSql(g_Dbs.host, &g_Dbs.serv_addr, g_Dbs.port, buffer)) {
affectedRows = -1; affectedRows = -1;
printf("========restful return fail, threadID[%d]\n", printf("========restful return fail, threadID[%d]\n",
pThreadInfo->threadID); pThreadInfo->threadID);
} else { } else {
affectedRows = k; affectedRows = k;
} }
} else {
errorPrint("%s() LN%d: unknown insert mode: %s\n",
__func__, __LINE__, superTblInfo->insertMode);
affectedRows = 0;
} }
} else { } else {
affectedRows = queryDbExec(pThreadInfo->taos, buffer, INSERT_TYPE, false); affectedRows = queryDbExec(pThreadInfo->taos, buffer, INSERT_TYPE, false);
...@@ -5425,6 +5428,32 @@ static void *asyncWrite(void *sarg) { ...@@ -5425,6 +5428,32 @@ static void *asyncWrite(void *sarg) {
return NULL; return NULL;
} }
static int convertHostToServAddr(char *host, uint16_t port, struct sockaddr_in *serv_addr)
{
uint16_t rest_port = port + TSDB_PORT_HTTP;
struct hostent *server = gethostbyname(host);
if ((server == NULL) || (server->h_addr == NULL)) {
errorPrint("%s", "ERROR, no such host");
return -1;
}
debugPrint("h_name: %s\nh_addr=%p\nh_addretype: %s\nh_length: %d\n",
server->h_name,
server->h_addr,
(server->h_addrtype == AF_INET)?"ipv4":"ipv6",
server->h_length);
memset(serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(rest_port);
#ifdef WINDOWS
serv_addr->sin_addr.s_addr = inet_addr(host);
#else
memcpy(&(serv_addr->sin_addr.s_addr), server->h_addr, server->h_length);
#endif
return 0;
}
static void startMultiThreadInsertData(int threads, char* db_name, static void startMultiThreadInsertData(int threads, char* db_name,
char* precision,SSuperTable* superTblInfo) { char* precision,SSuperTable* superTblInfo) {
...@@ -5588,6 +5617,12 @@ static void startMultiThreadInsertData(int threads, char* db_name, ...@@ -5588,6 +5617,12 @@ static void startMultiThreadInsertData(int threads, char* db_name,
b = ntables % threads; b = ntables % threads;
} }
if ((superTblInfo)
&& (0 == strncasecmp(superTblInfo->insertMode, "rest", strlen("rest")))) {
if (convertHostToServAddr(g_Dbs.host, g_Dbs.port, &(g_Dbs.serv_addr)) != 0)
exit(-1);
}
for (int i = 0; i < threads; i++) { for (int i = 0; i < threads; i++) {
threadInfo *t_info = infos + i; threadInfo *t_info = infos + i;
t_info->threadID = i; t_info->threadID = i;
...@@ -5996,7 +6031,7 @@ static void *specifiedTableQuery(void *sarg) { ...@@ -5996,7 +6031,7 @@ static void *specifiedTableQuery(void *sarg) {
st = taosGetTimestampMs(); st = taosGetTimestampMs();
if (0 == strncasecmp(g_queryInfo.queryMode, "taosc", 5)) { if (0 == strncasecmp(g_queryInfo.queryMode, "taosc", strlen("taosc"))) {
int64_t t1 = taosGetTimestampMs(); int64_t t1 = taosGetTimestampMs();
char tmpFile[MAX_FILE_NAME_LEN*2] = {0}; char tmpFile[MAX_FILE_NAME_LEN*2] = {0};
if (g_queryInfo.specifiedQueryInfo.result[pThreadInfo->querySeq][0] != 0) { if (g_queryInfo.specifiedQueryInfo.result[pThreadInfo->querySeq][0] != 0) {
...@@ -6009,9 +6044,9 @@ static void *specifiedTableQuery(void *sarg) { ...@@ -6009,9 +6044,9 @@ static void *specifiedTableQuery(void *sarg) {
int64_t t2 = taosGetTimestampMs(); int64_t t2 = taosGetTimestampMs();
printf("=[taosc] thread[%"PRId64"] complete one sql, Spent %10.3f s\n", printf("=[taosc] thread[%"PRId64"] complete one sql, Spent %10.3f s\n",
taosGetSelfPthreadId(), (t2 - t1)/1000.0); taosGetSelfPthreadId(), (t2 - t1)/1000.0);
} else { } else if (0 == strncasecmp(g_queryInfo.queryMode, "rest", strlen("rest"))) {
int64_t t1 = taosGetTimestampMs(); int64_t t1 = taosGetTimestampMs();
int retCode = postProceSql(g_queryInfo.host, int retCode = postProceSql(g_queryInfo.host, &(g_queryInfo.serv_addr),
g_queryInfo.port, g_queryInfo.port,
g_queryInfo.specifiedQueryInfo.sql[pThreadInfo->querySeq]); g_queryInfo.specifiedQueryInfo.sql[pThreadInfo->querySeq]);
if (0 != retCode) { if (0 != retCode) {
...@@ -6022,6 +6057,10 @@ static void *specifiedTableQuery(void *sarg) { ...@@ -6022,6 +6057,10 @@ static void *specifiedTableQuery(void *sarg) {
printf("=[restful] thread[%"PRId64"] complete one sql, Spent %10.3f s\n", printf("=[restful] thread[%"PRId64"] complete one sql, Spent %10.3f s\n",
taosGetSelfPthreadId(), (t2 - t1)/1000.0); taosGetSelfPthreadId(), (t2 - t1)/1000.0);
} else {
errorPrint("%s() LN%d, unknown query mode: %s\n",
__func__, __LINE__, g_queryInfo.queryMode);
return NULL;
} }
totalQueried ++; totalQueried ++;
g_queryInfo.specifiedQueryInfo.totalQueried ++; g_queryInfo.specifiedQueryInfo.totalQueried ++;
...@@ -6171,6 +6210,12 @@ static int queryTestProcess() { ...@@ -6171,6 +6210,12 @@ static int queryTestProcess() {
printfQuerySystemInfo(taos); printfQuerySystemInfo(taos);
if (0 == strncasecmp(g_queryInfo.queryMode, "rest", strlen("rest"))) {
if (convertHostToServAddr(
g_queryInfo.host, g_queryInfo.port, &g_queryInfo.serv_addr) != 0)
exit(-1);
}
pthread_t *pids = NULL; pthread_t *pids = NULL;
threadInfo *infos = NULL; threadInfo *infos = NULL;
//==== create sub threads for query from specify table //==== create sub threads for query from specify table
......
...@@ -2,36 +2,36 @@ ...@@ -2,36 +2,36 @@
ulimit -c unlimited ulimit -c unlimited
# insert # insert
python3 ./test.py $1 -f insert/basic.py python3.8 ./test.py $1 -f insert/basic.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
python3 ./test.py $1 -f insert/bigint.py python3.8 ./test.py $1 -f insert/bigint.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
python3 ./test.py $1 -f insert/nchar.py python3.8 ./test.py $1 -f insert/nchar.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
python3 ./test.py $1 -f insert/multi.py python3.8 ./test.py $1 -f insert/multi.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
# table # table
python3 ./test.py $1 -f table/column_name.py python3.8 ./test.py $1 -f table/column_name.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
python3 ./test.py $1 -f table/column_num.py python3.8 ./test.py $1 -f table/column_num.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
python3 ./test.py $1 -f table/db_table.py python3.8 ./test.py $1 -f table/db_table.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
# import # import
python3 ./test.py $1 -f import_merge/importDataLastSub.py python3.8 ./test.py $1 -f import_merge/importDataLastSub.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
#tag #tag
python3 ./test.py $1 -f tag_lite/filter.py python3.8 ./test.py $1 -f tag_lite/filter.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
#query #query
python3 ./test.py $1 -f query/filter.py python3.8 ./test.py $1 -f query/filter.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
# client # client
python3 ./test.py $1 -f client/client.py python3.8 ./test.py $1 -f client/client.py
python3 ./test.py $1 -s && sleep 1 python3.8 ./test.py $1 -s && sleep 1
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册