未验证 提交 dcd6f3e6 编写于 作者: M Minglei Jin 提交者: GitHub

Merge pull request #9044 from taosdata/hotfix/TS-836

[TS-836]<feature>: getLong return a timestamp with good precision
......@@ -11,6 +11,11 @@ import java.util.Map;
public abstract class AbstractResultSet extends WrapperImpl implements ResultSet {
private int fetchSize;
protected boolean wasNull;
protected int timestampPrecision;
public void setTimestampPrecision(int timestampPrecision) {
this.timestampPrecision = timestampPrecision;
}
protected void checkAvailability(int columnIndex, int bounds) throws SQLException {
if (isClosed())
......
......@@ -74,9 +74,8 @@ public class TSDBResultSet extends AbstractResultSet implements ResultSet {
public boolean next() throws SQLException {
if (this.getBatchFetch()) {
if (this.blockData.forward()) {
if (this.blockData.forward())
return true;
}
int code = this.jniConnector.fetchBlock(this.resultSetPointer, this.blockData);
this.blockData.reset();
......@@ -214,7 +213,18 @@ public class TSDBResultSet extends AbstractResultSet implements ResultSet {
if (!lastWasNull) {
Object value = this.rowData.getObject(columnIndex);
if (value instanceof Timestamp) {
res = ((Timestamp) value).getTime();
Timestamp ts = (Timestamp) value;
long epochSec = ts.getTime() / 1000;
long nanoAdjustment = ts.getNanos();
switch (this.timestampPrecision) {
case 0:
default: // ms
return ts.getTime();
case 1: // us
return epochSec * 1000_000L + nanoAdjustment / 1000L;
case 2: // ns
return epochSec * 1000_000_000L + nanoAdjustment;
}
} else {
int nativeType = this.columnMetaDataList.get(columnIndex - 1).getColType();
res = this.rowData.getLong(columnIndex, nativeType);
......
......@@ -47,6 +47,8 @@ public class TSDBStatement extends AbstractStatement {
throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_WITH_EXECUTEQUERY);
}
TSDBResultSet res = new TSDBResultSet(this, this.connection.getConnector(), pSql);
int timestampPrecision = this.connection.getConnector().getResultTimePrecision(pSql);
res.setTimestampPrecision(timestampPrecision);
res.setBatchFetch(this.connection.getBatchFetch());
return res;
}
......
package com.taosdata.jdbc.enums;
public enum TimestampPrecision {
MS,
US,
NS,
UNKNOWN
public class TimestampPrecision {
public static final int MS = 0;
public static final int US = 1;
public static final int NS = 2;
}
......@@ -168,11 +168,22 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
case TIMESTAMP: {
Long value = row.getLong(colIndex);
//TODO: this implementation has bug if the timestamp bigger than 9999_9999_9999_9
if (value < 1_0000_0000_0000_0L)
if (value < 1_0000_0000_0000_0L) {
this.timestampPrecision = TimestampPrecision.MS;
return new Timestamp(value);
long epochSec = value / 1000_000L;
long nanoAdjustment = value % 1000_000L * 1000L;
return Timestamp.from(Instant.ofEpochSecond(epochSec, nanoAdjustment));
}
if (value >= 1_0000_0000_0000_0L && value < 1_000_000_000_000_000_0l) {
this.timestampPrecision = TimestampPrecision.US;
long epochSec = value / 1000_000L;
long nanoAdjustment = value % 1000_000L * 1000L;
return Timestamp.from(Instant.ofEpochSecond(epochSec, nanoAdjustment));
}
if (value >= 1_000_000_000_000_000_0l) {
this.timestampPrecision = TimestampPrecision.NS;
long epochSec = value / 1000_000_000L;
long nanoAdjustment = value % 1000_000_000L;
return Timestamp.from(Instant.ofEpochSecond(epochSec, nanoAdjustment));
}
}
case UTC: {
String value = row.getString(colIndex);
......@@ -182,12 +193,15 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
if (value.length() > 31) {
// ns timestamp: yyyy-MM-ddTHH:mm:ss.SSSSSSSSS+0x00
nanoAdjustment = fractionalSec;
this.timestampPrecision = TimestampPrecision.NS;
} else if (value.length() > 28) {
// ms timestamp: yyyy-MM-ddTHH:mm:ss.SSSSSS+0x00
nanoAdjustment = fractionalSec * 1000L;
this.timestampPrecision = TimestampPrecision.US;
} else {
// ms timestamp: yyyy-MM-ddTHH:mm:ss.SSS+0x00
nanoAdjustment = fractionalSec * 1000_000L;
this.timestampPrecision = TimestampPrecision.MS;
}
ZoneOffset zoneOffset = ZoneOffset.of(value.substring(value.length() - 5));
Instant instant = Instant.ofEpochSecond(epochSec, nanoAdjustment).atOffset(zoneOffset).toInstant();
......@@ -196,7 +210,9 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
case STRING:
default: {
String value = row.getString(colIndex);
TimestampPrecision precision = Utils.guessTimestampPrecision(value);
int precision = Utils.guessTimestampPrecision(value);
this.timestampPrecision = precision;
if (precision == TimestampPrecision.MS) {
// ms timestamp: yyyy-MM-dd HH:mm:ss.SSS
return row.getTimestamp(colIndex);
......@@ -338,8 +354,18 @@ public class RestfulResultSet extends AbstractResultSet implements ResultSet {
wasNull = value == null;
if (value == null)
return 0;
if (value instanceof Timestamp)
return ((Timestamp) value).getTime();
if (value instanceof Timestamp) {
Timestamp ts = (Timestamp) value;
switch (this.timestampPrecision) {
case TimestampPrecision.MS:
default:
return ts.getTime();
case TimestampPrecision.US:
return ts.getTime() * 1000 + ts.getNanos() / 1000 % 1000;
case TimestampPrecision.NS:
return ts.getTime() * 1000_000 + ts.getNanos() % 1000_000;
}
}
long valueAsLong = 0;
try {
valueAsLong = Long.parseLong(value.toString());
......
......@@ -194,14 +194,14 @@ public class Utils {
return timestamp.toLocalDateTime().format(milliSecFormatter);
}
public static TimestampPrecision guessTimestampPrecision(String value) {
public static int guessTimestampPrecision(String value) {
if (isMilliSecFormat(value))
return TimestampPrecision.MS;
if (isMicroSecFormat(value))
return TimestampPrecision.US;
if (isNanoSecFormat(value))
return TimestampPrecision.NS;
return TimestampPrecision.UNKNOWN;
return TimestampPrecision.MS;
}
private static boolean isMilliSecFormat(String timestampStr) {
......
package com.taosdata.jdbc.cases;
import org.junit.Assert;
import org.junit.Test;
import java.sql.*;
import java.text.SimpleDateFormat;
public class GetLongWithDifferentTimestampPrecision {
private final String host = "127.0.0.1";
@Test
public void testRestful() throws SQLException {
// given
String url = "jdbc:TAOS-RS://" + host + ":6041/";
Connection conn = DriverManager.getConnection(url, "root", "taosdata");
long ts = System.currentTimeMillis();
// when and then
assertResultSet(conn, "ms", ts, ts);
assertResultSet(conn, "us", ts, ts * 1000);
assertResultSet(conn, "ns", ts, ts * 1000_000);
}
@Test
public void testJni() throws SQLException {
// given
String url = "jdbc:TAOS://" + host + ":6030/";
Connection conn = DriverManager.getConnection(url, "root", "taosdata");
long ts = System.currentTimeMillis();
// when and then
assertResultSet(conn, "ms", ts, ts);
assertResultSet(conn, "us", ts, ts * 1000);
assertResultSet(conn, "ns", ts, ts * 1000_000);
}
private void assertResultSet(Connection conn, String precision, long timestamp, long expect) throws SQLException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
try (Statement stmt = conn.createStatement()) {
stmt.execute("drop database if exists test");
stmt.execute("create database if not exists test precision '" + precision + "'");
stmt.execute("create table test.weather(ts timestamp, f1 int)");
String dateTimeStr = sdf.format(new Date(timestamp));
stmt.execute("insert into test.weather values('" + dateTimeStr + "', 1)");
ResultSet rs = stmt.executeQuery("select * from test.weather");
rs.next();
long actual = rs.getLong("ts");
Assert.assertEquals(expect, actual);
stmt.execute("drop database if exists test");
}
}
}
......@@ -9,29 +9,29 @@ import java.util.Random;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class RestfulJDBCTest {
// private static final String host = "127.0.0.1";
private static final String host = "master";
private static final String host = "127.0.0.1";
private static final Random random = new Random(System.currentTimeMillis());
private static Connection connection;
private static final String dbname = "restful_test";
@Test
public void testCase001() throws SQLException {
// given
String sql = "drop database if exists restful_test";
String sql = "drop database if exists " + dbname;
// when
boolean execute = execute(connection, sql);
// then
Assert.assertFalse(execute);
// given
sql = "create database if not exists restful_test";
sql = "create database if not exists " + dbname;
// when
execute = execute(connection, sql);
// then
Assert.assertFalse(execute);
// given
sql = "use restful_test";
sql = "use " + dbname;
// when
execute = execute(connection, sql);
// then
......@@ -41,7 +41,7 @@ public class RestfulJDBCTest {
@Test
public void testCase002() throws SQLException {
// given
String sql = "create table weather(ts timestamp, temperature float, humidity int) tags(location nchar(64), groupId int)";
String sql = "create table " + dbname + ".weather(ts timestamp, temperature float, humidity int) tags(location nchar(64), groupId int)";
// when
boolean execute = execute(connection, sql);
// then
......@@ -52,7 +52,7 @@ public class RestfulJDBCTest {
public void testCase004() throws SQLException {
for (int i = 1; i <= 100; i++) {
// given
String sql = "create table t" + i + " using weather tags('beijing', '" + i + "')";
String sql = "create table " + dbname + ".t" + i + " using " + dbname + ".weather tags('beijing', '" + i + "')";
// when
boolean execute = execute(connection, sql);
// then
......@@ -68,7 +68,7 @@ public class RestfulJDBCTest {
// given
long currentTimeMillis = System.currentTimeMillis();
String sql = "insert into t" + j + " values(" + currentTimeMillis + "," + (random.nextFloat() * 50) + "," + random.nextInt(100) + ")";
String sql = "insert into " + dbname + ".t" + j + " values(" + currentTimeMillis + "," + (random.nextFloat() * 50) + "," + random.nextInt(100) + ")";
// when
int affectRows = executeUpdate(connection, sql);
// then
......@@ -83,7 +83,7 @@ public class RestfulJDBCTest {
@Test
public void testCase006() throws SQLException {
// given
String sql = "select * from weather";
String sql = "select * from " + dbname + ".weather";
// when
ResultSet rs = executeQuery(connection, sql);
ResultSetMetaData meta = rs.getMetaData();
......@@ -102,7 +102,7 @@ public class RestfulJDBCTest {
@Test
public void testCase007() throws SQLException {
// given
String sql = "drop database restful_test";
String sql = "drop database " + dbname;
// when
boolean execute = execute(connection, sql);
......@@ -143,7 +143,7 @@ public class RestfulJDBCTest {
public static void afterClass() throws SQLException {
if (connection != null) {
Statement stmt = connection.createStatement();
stmt.execute("drop database if exists restful_test");
stmt.execute("drop database if exists " + dbname);
stmt.close();
connection.close();
}
......
......@@ -17,12 +17,25 @@ import java.text.SimpleDateFormat;
public class RestfulResultSetTest {
// private static final String host = "127.0.0.1";
private static final String host = "master";
private static final String host = "127.0.0.1";
private static Connection conn;
private static Statement stmt;
private static ResultSet rs;
@BeforeClass
public static void beforeClass() throws SQLException {
conn = DriverManager.getConnection("jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata");
stmt = conn.createStatement();
stmt.execute("drop database if exists restful_test");
stmt.execute("create database if not exists restful_test");
stmt.execute("use restful_test");
stmt.execute("drop table if exists weather");
stmt.execute("create table if not exists weather(f1 timestamp, f2 int, f3 bigint, f4 float, f5 double, f6 binary(64), f7 smallint, f8 tinyint, f9 bool, f10 nchar(64))");
stmt.execute("insert into restful_test.weather values('2021-01-01 00:00:00.000', 1, 100, 3.1415, 3.1415926, 'abc', 10, 10, true, '涛思数据')");
rs = stmt.executeQuery("select * from restful_test.weather");
rs.next();
}
@Test
public void wasNull() throws SQLException {
Assert.assertFalse(rs.wasNull());
......@@ -658,20 +671,6 @@ public class RestfulResultSetTest {
Assert.assertTrue(rs.isWrapperFor(RestfulResultSet.class));
}
@BeforeClass
public static void beforeClass() throws SQLException {
conn = DriverManager.getConnection("jdbc:TAOS-RS://" + host + ":6041/?user=root&password=taosdata");
stmt = conn.createStatement();
stmt.execute("drop database if exists restful_test");
stmt.execute("create database if not exists restful_test");
stmt.execute("use restful_test");
stmt.execute("drop table if exists weather");
stmt.execute("create table if not exists weather(f1 timestamp, f2 int, f3 bigint, f4 float, f5 double, f6 binary(64), f7 smallint, f8 tinyint, f9 bool, f10 nchar(64))");
stmt.execute("insert into restful_test.weather values('2021-01-01 00:00:00.000', 1, 100, 3.1415, 3.1415926, 'abc', 10, 10, true, '涛思数据')");
rs = stmt.executeQuery("select * from restful_test.weather");
rs.next();
}
@AfterClass
public static void afterClass() throws SQLException {
if (rs != null)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册