Skip to content

Commit

Permalink
Add fix for null-valued TIMESTAMP (#188)
Browse files Browse the repository at this point in the history
  • Loading branch information
wnob authored Dec 21, 2023
1 parent 94614a9 commit 134cd89
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 0 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@
<version>1.7</version>
<style>GOOGLE</style>
</googleJavaFormat>
<toggleOffOn />
</java>
</configuration>
</plugin>
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/net/starschema/clouddb/jdbc/DateTimeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ static Timestamp parseDateTime(String value, Calendar cal) throws SQLException {

/** Parse a BigQuery TIMESTAMP literal, represented as the number of seconds since epoch. */
static Timestamp parseTimestamp(String value) throws SQLException {
if (value == null) {
return null;
}
try {
// BigQuery TIMESTAMP has a string representation that looks like e.g. "1.288061375E9"
// for 2010-10-26 02:49:35 UTC.
Expand All @@ -119,6 +122,9 @@ static Timestamp parseTimestamp(String value) throws SQLException {
}

static String formatTimestamp(String rawString) throws SQLException {
if (rawString == null) {
return null;
}
Timestamp timestamp = parseTimestamp(rawString);
return DATETIME_FORMATTER.format(OffsetDateTime.ofInstant(timestamp.toInstant(), UTC_ZONE))
+ " UTC";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
Expand Down Expand Up @@ -796,6 +797,62 @@ public void testHandlesAllNullResponseFields() throws Exception {
throw new AssertionError("Expected graceful failure due to lack of job reference");
}

@Test
public void testHandlesNullTimeDateObjects() throws Exception {
this.NewConnection("&useLegacySql=false");
Statement stmt =
BQForwardOnlyResultSetFunctionTest.con.createStatement(
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);

final String date = "2011-11-11";
final String time = "12:12:12";
final String dateTime = date + " " + time;
final String dateTimeWithT = date + "T" + time;
// The number of milliseconds between epoch and 2011-11-11 12:12:12 UTC+0.
final long millis = 1321013532000L;

// spotless:off
String sql = "SELECT " +
"TIMESTAMP('" + dateTime + "') AS ts, " +
"DATETIME('" + dateTime + "') AS dt, " +
"DATE('" + date + "') AS d, " +
"TIME(12, 12, 12) AS t\n" +
"UNION ALL SELECT " +
"CASE WHEN 1 = 0 THEN TIMESTAMP('" + dateTime + "') ELSE NULL END, " +
"CASE WHEN 1 = 0 THEN DATETIME('" + dateTime + "') ELSE NULL END, " +
"CASE WHEN 1 = 0 THEN DATE('" + date + "') ELSE NULL END, " +
"CASE WHEN 1 = 0 THEN TIME(12, 12, 12) ELSE NULL END";
// spotless:on

ResultSet results = stmt.executeQuery(sql);

// First row has all non-null objects.
Assertions.assertThat(results.next()).isTrue();
Assertions.assertThat(results.getObject("ts"))
.isEqualTo(Timestamp.from(Instant.ofEpochMilli(millis)));
Assertions.assertThat(results.getString("ts")).isEqualTo(dateTime + " UTC");
Assertions.assertThat(results.getObject("dt")).isEqualTo(Timestamp.valueOf(dateTime));
Assertions.assertThat(results.getString("dt")).isEqualTo(dateTimeWithT);
Assertions.assertThat(results.getObject("d")).isEqualTo(java.sql.Date.valueOf(date));
Assertions.assertThat(results.getString("d")).isEqualTo(date);
Assertions.assertThat(results.getObject("t")).isEqualTo(java.sql.Time.valueOf(time));
Assertions.assertThat(results.getString("t")).isEqualTo(time);

// Second row is all null.
Assertions.assertThat(results.next()).isTrue();
Assertions.assertThat(results.getObject("ts")).isNull();
Assertions.assertThat(results.getString("ts")).isNull();
Assertions.assertThat(results.getObject("dt")).isNull();
Assertions.assertThat(results.getString("dt")).isNull();
Assertions.assertThat(results.getObject("d")).isNull();
Assertions.assertThat(results.getString("d")).isNull();
Assertions.assertThat(results.getObject("t")).isNull();
Assertions.assertThat(results.getString("t")).isNull();

// Only two rows.
Assertions.assertThat(results.next()).isFalse();
}

@Test
public void testHandlesSomeNullResponseFields() throws Exception {
// Make sure we don't get any NPE's due to null values;
Expand Down

0 comments on commit 134cd89

Please sign in to comment.