diff --git a/snapi-frontend/src/main/scala/raw/creds/local/LocalCredentialsService.scala b/snapi-frontend/src/main/scala/raw/creds/local/LocalCredentialsService.scala index fee6e5add..36e8fd08f 100644 --- a/snapi-frontend/src/main/scala/raw/creds/local/LocalCredentialsService.scala +++ b/snapi-frontend/src/main/scala/raw/creds/local/LocalCredentialsService.scala @@ -146,7 +146,7 @@ class LocalCredentialsService extends CredentialsService { } override def getUserDb(user: AuthenticatedUser): String = { - "default-user-db" + "unittest" } override def doStop(): Unit = {} diff --git a/sql-client/src/main/scala/raw/client/sql/NamedParametersPreparedStatement.scala b/sql-client/src/main/scala/raw/client/sql/NamedParametersPreparedStatement.scala index 92f82b72e..a4013f5a2 100644 --- a/sql-client/src/main/scala/raw/client/sql/NamedParametersPreparedStatement.scala +++ b/sql-client/src/main/scala/raw/client/sql/NamedParametersPreparedStatement.scala @@ -19,7 +19,8 @@ import raw.client.api._ import raw.client.sql.antlr4._ import java.sql.{Connection, ResultSet, ResultSetMetaData} -import java.time.LocalTime +import java.time.{LocalTime, ZoneId} +import java.util.{Calendar, TimeZone} import scala.collection.mutable /* This class is wrapping the PreparedStatement class from the JDBC API. @@ -415,7 +416,7 @@ class NamedParametersPreparedStatement(conn: Connection, parsedTree: ParseProgra 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) @@ -536,6 +537,9 @@ class NamedParametersPreparedStatement(conn: Connection, parsedTree: ParseProgra } } + private val timezone = ZoneId.of("UTC") + private val zonedCalendar = Calendar.getInstance(TimeZone.getTimeZone(timezone)) + private def getDefaultValue(rs: ResultSet, postgresType: PostgresType): DefaultValue = { assert(rs.next(), "rs.next() was false") val attempt = postgresType.jdbcType match { @@ -546,8 +550,8 @@ class NamedParametersPreparedStatement(conn: Connection, parsedTree: ParseProgra 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.getObject(1, classOf[LocalTime])) - case java.sql.Types.TIMESTAMP => RawTimestamp(rs.getTimestamp(1).toLocalDateTime) + case java.sql.Types.TIME => RawTime(LocalTime.ofNanoOfDay(rs.getTime(1, zonedCalendar).getTime*1000000)) + case java.sql.Types.TIMESTAMP => RawTimestamp(rs.getTimestamp(1, zonedCalendar).toLocalDateTime) case java.sql.Types.BOOLEAN => RawBool(rs.getBoolean(1)) case java.sql.Types.VARCHAR => RawString(rs.getString(1)) } diff --git a/sql-client/src/main/scala/raw/client/sql/writers/TypedResultSetJsonWriter.scala b/sql-client/src/main/scala/raw/client/sql/writers/TypedResultSetJsonWriter.scala index b0bf93306..b74be4cf7 100644 --- a/sql-client/src/main/scala/raw/client/sql/writers/TypedResultSetJsonWriter.scala +++ b/sql-client/src/main/scala/raw/client/sql/writers/TypedResultSetJsonWriter.scala @@ -20,8 +20,9 @@ import raw.client.utils.RecordFieldsNaming import java.io.{IOException, OutputStream} import java.sql.ResultSet -import java.time.LocalTime +import java.time.{LocalDateTime, LocalTime, ZoneId} import java.time.format.DateTimeFormatter +import java.util.{Calendar, TimeZone} import scala.annotation.tailrec object TypedResultSetJsonWriter { @@ -70,6 +71,9 @@ class TypedResultSetJsonWriter(os: OutputStream) { gen.writeEndArray() } + private val timezone = ZoneId.of("UTC") + private val zonedCalendar = Calendar.getInstance(TimeZone.getTimeZone(timezone)) + @throws[IOException] @tailrec private def writeValue(v: ResultSet, i: Int, t: RawType): Unit = { @@ -103,11 +107,11 @@ class TypedResultSetJsonWriter(os: OutputStream) { val date = v.getDate(i).toLocalDate gen.writeString(dateFormatter.format(date)) case _: RawTimeType => - val time = v.getObject(i, classOf[LocalTime]) + val time = LocalTime.ofNanoOfDay(v.getTime(i, zonedCalendar).getTime*1000000) val formatted = timeFormatter.format(time) gen.writeString(formatted) case _: RawTimestampType => - val dateTime = v.getTimestamp(i).toLocalDateTime + val dateTime = LocalDateTime.ofInstant(v.getTimestamp(i, zonedCalendar).toInstant, timezone) val formatted = timestampFormatter.format(dateTime) gen.writeString(formatted) case _: RawIntervalType => diff --git a/sql-client/src/test/scala/raw/client/sql/TestSqlCompilerServiceAirports.scala b/sql-client/src/test/scala/raw/client/sql/TestSqlCompilerServiceAirports.scala index 1bb99b74e..11126a265 100644 --- a/sql-client/src/test/scala/raw/client/sql/TestSqlCompilerServiceAirports.scala +++ b/sql-client/src/test/scala/raw/client/sql/TestSqlCompilerServiceAirports.scala @@ -37,11 +37,11 @@ class TestSqlCompilerServiceAirports private var compilerService: CompilerService = _ - private val database = sys.env.getOrElse("FDW_DATABASE", "unittest") + private val database = sys.env.getOrElse("FDW_DATABASE", "raw") private val hostname = sys.env.getOrElse("FDW_HOSTNAME", "localhost") private val port = sys.env.getOrElse("FDW_HOSTNAME", "5432") - private val username = sys.env.getOrElse("FDW_USERNAME", "postgres") - private val password = sys.env.getOrElse("FDW_PASSWORD", "1234") + private val username = sys.env.getOrElse("FDW_USERNAME", "newbie") + private val password = sys.env.getOrElse("FDW_PASSWORD", "") property("raw.creds.jdbc.fdw.host", hostname) property("raw.creds.jdbc.fdw.port", port) @@ -989,7 +989,7 @@ class TestSqlCompilerServiceAirports assert(compilerService.execute(t.q, noParam, None, baos) == ExecutionSuccess) } - test("""SELECT TIMESTAMP WITH TIME ZONE '2001-01-01 12:13:14.567' AS t""".stripMargin) { t => + test("""SELECT TIMESTAMP WITH TIME ZONE '2001-01-01 12:13:14.567 UTC' AS t""".stripMargin) { t => val ValidateResponse(errors) = compilerService.validate(t.q, asJson()) assert(errors.isEmpty) val GetProgramDescriptionSuccess(description) = compilerService.getProgramDescription(t.q, asJson()) @@ -1021,7 +1021,7 @@ class TestSqlCompilerServiceAirports assert(baos.toString() == """[{"t":"2001-01-01T12:13:14.567"}]""") } - test("""SELECT TIME '12:13:14.567' AS t""".stripMargin) { t => + test("""SELECT TIME WITH TIME ZONE '12:13:14.567 UTC' AS t""".stripMargin) { t => val ValidateResponse(errors) = compilerService.validate(t.q, asJson()) assert(errors.isEmpty) val GetProgramDescriptionSuccess(description) = compilerService.getProgramDescription(t.q, asJson()) @@ -1053,4 +1053,21 @@ class TestSqlCompilerServiceAirports assert(baos.toString() == """[{"t":"12:13:14.567"}]""") } + test( + """-- @default t TIME WITHOUT TIME ZONE '12:13:14.567' + |SELECT :t AS t""".stripMargin) { t => + 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) + assert(baos.toString() == """[{"t":"12:13:14.567"}]""") + } }