Skip to content

Commit

Permalink
Fixed RD-10923: Milliseconds lost in TIME/TIMESTAMP
Browse files Browse the repository at this point in the history
  • Loading branch information
bgaidioz committed Jun 13, 2024
1 parent 776aa57 commit 382b053
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ trait MySQLPackageTest extends CompilerTestContext with CredentialsTestContext w
| CAST(3.14 AS DOUBLE) AS doublecol,
| CAST(1200000000 AS DECIMAL) AS decimalcol,
| '120' AS stringcol,
| CAST('12:23:34' AS TIME) AS timecol,
| TIME('12:23:34.123') AS timecol,
| CAST('2020-01-01' AS DATE) AS datecol,
| CAST('2020-01-01 12:23:34' AS DATETIME) AS timestampcol,
| TIMESTAMP('2020-01-01 12:23:34.123') AS timestampcol,
| 1 = 0 AS boolcol,
| convert('Olala!' using utf8) AS binarycol$ttt, type collection(
| record(
Expand Down Expand Up @@ -65,9 +65,9 @@ trait MySQLPackageTest extends CompilerTestContext with CredentialsTestContext w
| doublecol: 3.14,
| decimalcol: Decimal.From(1200000000),
| stringcol: "120",
| timecol: Time.Build(12, 23, seconds=34),
| timecol: Time.Build(12, 23, seconds=34, millis=123),
| datecol: Date.Build(2020, 1, 1),
| timestampcol: Timestamp.Build(2020, 1, 1, 12, 23, seconds=34),
| timestampcol: Timestamp.Build(2020, 1, 1, 12, 23, seconds=34, millis=123),
| boolcol: false,
| binarycol: Binary.FromString("Olala!")
|}]""".stripMargin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ trait PostgreSQLPackageTest extends CompilerTestContext with CredentialsTestCont
| CAST('3.14' AS DOUBLE PRECISION) AS doublecol,
| CAST('12000000' AS DECIMAL) AS decimalcol,
| CAST('120' AS VARCHAR) AS stringcol,
| CAST('12:23:34' AS TIME) AS timecol,
| CAST('12:23:34.123' AS TIME) AS timecol,
| CAST('2020-01-01' AS DATE) AS datecol,
| CAST('2020-01-01 12:23:34' AS TIMESTAMP) AS timestampcol,
| CAST('2020-01-01 12:23:34.123' AS TIMESTAMP) AS timestampcol,
| CAST('false' AS BOOL) AS boolcol,
| decode('T2xhbGEh', 'base64') as binarycol$ttt, type collection(
| record(
Expand Down Expand Up @@ -65,9 +65,9 @@ trait PostgreSQLPackageTest extends CompilerTestContext with CredentialsTestCont
| doublecol: 3.14,
| decimalcol: Decimal.From(12000000),
| stringcol: "120",
| timecol: Time.Build(12, 23, seconds=34),
| timecol: Time.Build(12, 23, seconds=34, millis=123),
| datecol: Date.Build(2020, 1, 1),
| timestampcol: Timestamp.Build(2020, 1, 1, 12, 23, seconds=34),
| timestampcol: Timestamp.Build(2020, 1, 1, 12, 23, seconds=34, millis=123),
| boolcol: false,
| binarycol: Binary.FromString("Olala!")
|}]""".stripMargin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ trait SnowflakePackageTest extends CompilerTestContext with CredentialsTestConte
| CAST('3.14' AS FLOAT8) AS "doublecol",
| CAST('12000000' AS DECIMAL) AS "decimalcol",
| CAST('120' AS VARCHAR) AS "stringcol",
| CAST('12:23:34' AS TIME) AS "timecol",
| CAST('12:23:34.123' AS TIME) AS "timecol",
| CAST('2020-01-01' AS DATE) AS "datecol",
| CAST('2020-01-01 12:23:34' AS DATETIME) AS "timestampcol",
| CAST('2020-01-01 12:23:34.123' AS DATETIME) AS "timestampcol",
| 1 = 0 AS "boolcol",
| to_binary('tralala', 'utf-8') AS "binarycol" $ttt, type collection(
| record(
Expand Down Expand Up @@ -67,9 +67,9 @@ trait SnowflakePackageTest extends CompilerTestContext with CredentialsTestConte
| doublecol: 3.14,
| decimalcol: Decimal.From(12000000),
| stringcol: "120",
| timecol: Time.Build(12, 23, seconds=34),
| timecol: Time.Build(12, 23, seconds=34, millis=123),
| datecol: Date.Build(2020, 1, 1),
| timestampcol: Timestamp.Build(2020, 1, 1, 12, 23, seconds=34),
| timestampcol: Timestamp.Build(2020, 1, 1, 12, 23, seconds=34, millis=123),
| boolcol: false,
| binarycol: String.Encode("tralala", "utf-8")
|}]""".stripMargin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ trait SqlServerPackageTest extends CompilerTestContext with CredentialsTestConte
| CAST('3.14' AS DOUBLE PRECISION) AS doublecol,
| CAST('12000000' AS DECIMAL) AS decimalcol,
| CAST('120' AS VARCHAR) AS stringcol,
| CAST('12:23:34' AS TIME) AS timecol,
| CAST('12:23:34.123' AS TIME) AS timecol,
| CAST('2020-01-01' AS DATE) AS datecol,
| CAST('2020-01-01 12:23:34' AS DATETIME) AS timestampcol,
| CAST('2020-01-01 12:23:34.123' AS DATETIME) AS timestampcol,
| CAST('Olala!' AS VARBINARY(MAX)) AS binarycol $ttt)""".stripMargin) { it =>
it should typeAs("""collection(
| record(
Expand All @@ -64,9 +64,9 @@ trait SqlServerPackageTest extends CompilerTestContext with CredentialsTestConte
| doublecol: 3.14,
| decimalcol: Decimal.From(12000000),
| stringcol: "120",
| timecol: Time.Build(12, 23, seconds=34),
| timecol: Time.Build(12, 23, seconds=34, millis=123),
| datecol: Date.Build(2020, 1, 1),
| timestampcol: Timestamp.Build(2020, 1, 1, 12, 23, seconds=34),
| timestampcol: Timestamp.Build(2020, 1, 1, 12, 23, seconds=34, millis=123),
| binarycol: Binary.FromString("Olala!")
|}]""".stripMargin)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalTime;
import raw.client.api.LocationDescription;
import raw.runtime.truffle.runtime.exceptions.rdbms.JdbcExceptionHandler;
import raw.runtime.truffle.runtime.exceptions.rdbms.JdbcReaderRawTruffleException;
Expand Down Expand Up @@ -177,7 +178,8 @@ DateObject getDate(String colName, Node node) {
TimeObject getTime(String colName, Node node) {
try {
java.sql.Time sqlTime = rs.getTime(colName);
return new TimeObject(sqlTime.toLocalTime());
LocalTime localTime = LocalTime.ofNanoOfDay(sqlTime.getTime() * 1000000);
return new TimeObject(localTime);
} catch (SQLException e) {
throw exceptionHandler.columnParseError(e, colName, node);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import raw.client.api._
import raw.client.sql.antlr4._

import java.sql.{Connection, ResultSet, ResultSetMetaData}
import java.time.LocalTime
import scala.collection.mutable

/* This class is wrapping the PreparedStatement class from the JDBC API.
Expand Down Expand Up @@ -455,7 +456,7 @@ class NamedParametersPreparedStatement(
case RawString(v) => setString(paramName, v)
case RawDecimal(v) => setBigDecimal(paramName, v)
case RawDate(v) => setDate(paramName, java.sql.Date.valueOf(v))
case RawTime(v) => setTime(paramName, java.sql.Time.valueOf(v))
case RawTime(v) => setTime(paramName, new java.sql.Time(v.toNanoOfDay / 1000000))
case RawTimestamp(v) => setTimestamp(paramName, java.sql.Timestamp.valueOf(v))
case RawInterval(years, months, weeks, days, hours, minutes, seconds, millis) => ???
case RawBinary(v) => setBytes(paramName, v)
Expand Down Expand Up @@ -586,7 +587,7 @@ class NamedParametersPreparedStatement(
case java.sql.Types.FLOAT => RawFloat(rs.getFloat(1))
case java.sql.Types.DOUBLE => RawDouble(rs.getDouble(1))
case java.sql.Types.DATE => RawDate(rs.getDate(1).toLocalDate)
case java.sql.Types.TIME => RawTime(rs.getTime(1).toLocalTime)
case java.sql.Types.TIME => RawTime(LocalTime.ofNanoOfDay(rs.getTime(1).getTime * 1000000))
case java.sql.Types.TIMESTAMP => RawTimestamp(rs.getTimestamp(1).toLocalDateTime)
case java.sql.Types.BOOLEAN => RawBool(rs.getBoolean(1))
case java.sql.Types.VARCHAR => RawString(rs.getString(1))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import raw.client.utils.RecordFieldsNaming

import java.io.{IOException, OutputStream}
import java.sql.ResultSet
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import scala.annotation.tailrec

Expand Down Expand Up @@ -113,7 +114,8 @@ class TypedResultSetCsvWriter(os: OutputStream, lineSeparator: String) {
val date = v.getDate(i).toLocalDate
gen.writeString(dateFormatter.format(date))
case _: RawTimeType =>
val time = v.getTime(i).toLocalTime
val sqlTime = v.getTime(i)
val time = LocalTime.ofNanoOfDay(sqlTime.getTime * 1000000)
val formatter = if (time.getNano > 0) timeFormatter else timeFormatterNoMs
val formatted = formatter.format(time)
gen.writeString(formatted)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import raw.client.utils.RecordFieldsNaming

import java.io.{IOException, OutputStream}
import java.sql.ResultSet
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import scala.annotation.tailrec

Expand Down Expand Up @@ -102,7 +103,8 @@ class TypedResultSetJsonWriter(os: OutputStream) {
val date = v.getDate(i).toLocalDate
gen.writeString(dateFormatter.format(date))
case _: RawTimeType =>
val time = v.getTime(i).toLocalTime
val sqlTime = v.getTime(i)
val time = LocalTime.ofNanoOfDay(sqlTime.getTime * 1000000)
val formatted = timeFormatter.format(time)
gen.writeString(formatted)
case _: RawTimestampType =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1048,4 +1048,109 @@ class TestSqlCompilerServiceAirports
== """[{"trip_id":0,"departure_date":"2016-02-27","arrival_date":"2016-03-06"}]"""
)
}

test("""SELECT pg_typeof(NOW())""".stripMargin) { t =>
assume(password != "")
val ValidateResponse(errors) = compilerService.validate(t.q, asJson())
assert(errors.isEmpty)
val GetProgramDescriptionFailure(errors2) = compilerService.getProgramDescription(t.q, asJson())
errors2.map(_.message).contains("unsupported type: regtype")
}

test("""SELECT CAST(pg_typeof(NOW()) AS VARCHAR)""".stripMargin) { t =>
assume(password != "")
val ValidateResponse(errors) = compilerService.validate(t.q, asJson())
assert(errors.isEmpty)
val GetProgramDescriptionSuccess(_) = compilerService.getProgramDescription(t.q, asJson())
val baos = new ByteArrayOutputStream()
baos.reset()
val noParam = ProgramEnvironment(
user,
None,
Set.empty,
Map("output-format" -> "json")
)
assert(compilerService.execute(t.q, noParam, None, baos) == ExecutionSuccess)
assert(baos.toString() == """[{"pg_typeof":"timestamp with time zone"}]""")

}

test("""SELECT NOW()""".stripMargin) { t =>
assume(password != "")
// NOW() is a timestamp with timezone. The one of the SQL connection. This test is to make sure
// it works (we cannot assert on the result).
val ValidateResponse(errors) = compilerService.validate(t.q, asJson())
assert(errors.isEmpty)
val GetProgramDescriptionSuccess(description) = compilerService.getProgramDescription(t.q, asJson())
val baos = new ByteArrayOutputStream()
baos.reset()
val noParam = ProgramEnvironment(
user,
None,
Set.empty,
Map("output-format" -> "json")
)
assert(compilerService.execute(t.q, noParam, None, baos) == ExecutionSuccess)
}

test("""SELECT TIMESTAMP '2001-07-01 12:13:14.567' AS t""".stripMargin) { t =>
assume(password != "")
val ValidateResponse(errors) = compilerService.validate(t.q, asJson())
assert(errors.isEmpty)
val GetProgramDescriptionSuccess(_) = compilerService.getProgramDescription(t.q, asJson())
val baos = new ByteArrayOutputStream()
for (fmt <- Seq("json", "csv")) {
baos.reset()
val noParam = ProgramEnvironment(
user,
None,
Set.empty,
Map("output-format" -> fmt)
)
assert(compilerService.execute(t.q, noParam, None, baos) == ExecutionSuccess)
assert(baos.toString().contains("14.567"))
}
}

test("""SELECT TIME '12:13:14.567' AS t""".stripMargin) { t =>
assume(password != "")
val ValidateResponse(errors) = compilerService.validate(t.q, asJson())
assert(errors.isEmpty)
val GetProgramDescriptionSuccess(_) = compilerService.getProgramDescription(t.q, asJson())
val baos = new ByteArrayOutputStream()
baos.reset()
for (fmt <- Seq("json", "csv")) {
baos.reset()
val noParam = ProgramEnvironment(
user,
None,
Set.empty,
Map("output-format" -> fmt)
)
assert(compilerService.execute(t.q, noParam, None, baos) == ExecutionSuccess)
assert(baos.toString().contains("14.567"))
}
}

test("""-- @default t TIME '12:13:14.567'
|SELECT :t AS t""".stripMargin) { t =>
assume(password != "")
val ValidateResponse(errors) = compilerService.validate(t.q, asJson())
assert(errors.isEmpty)
val GetProgramDescriptionSuccess(_) = compilerService.getProgramDescription(t.q, asJson())
val baos = new ByteArrayOutputStream()
baos.reset()
for (fmt <- Seq("json", "csv")) {
baos.reset()
val noParam = ProgramEnvironment(
user,
None,
Set.empty,
Map("output-format" -> fmt)
)
assert(compilerService.execute(t.q, noParam, None, baos) == ExecutionSuccess)
assert(baos.toString().contains("14.567"))
}
}

}

0 comments on commit 382b053

Please sign in to comment.