Skip to content

Commit

Permalink
Improvements to date logic.
Browse files Browse the repository at this point in the history
It works like this. Since we cannot differentiate between Dates and Timestamps if it sees a 'YYYY-MM-DD' it assumes it is a date, and if it sees a "YYYY-MM-DDThh:mm:ss.sssZ" it assumes it is a timestamp. If that is incorrect you must cast it to a string by appending "STRING::". You can also cast to date with DATE:: or timestamp with TIMESTAMP:: to be more explicit, but its not needed.
  • Loading branch information
kenstott committed Oct 12, 2024
1 parent 7c73da7 commit a471f3f
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 8 deletions.
2 changes: 1 addition & 1 deletion calcite-rs-jni/calcite
Submodule calcite updated 1 files
+16 −0 pom.xml
99 changes: 92 additions & 7 deletions calcite-rs-jni/jni/src/main/java/com/hasura/StatementPreparer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Date;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
* The StatementPreparer class is responsible for preparing a SQL statement by replacing
* marked up strings and indexed question marks with the actual values.
Expand Down Expand Up @@ -41,6 +43,8 @@ public static PreparedStatement prepare(String input, Connection connection) thr
preparedStatement.setString(i + 1, (String) item);
} else if (item instanceof Timestamp) {
preparedStatement.setTimestamp(i + 1, (Timestamp) item);
} else if (item instanceof Date) {
preparedStatement.setDate(i + 1, (Date) item);
}
}
return preparedStatement;
Expand Down Expand Up @@ -81,35 +85,116 @@ private static ArrayList<Object> extractMarkedUpStrings(String input) {
* But makes a special exception for UTC formatted dates, in which case it
* converts them to ANSI SQL dates in local time.
*
* @param input The SQL statement to process.
* @param input The SQL statement to process.
* @param extractedStrings The list of extracted marked-up strings.
* @return The input SQL statement with marked up strings replaced by indexed question marks.
*/
private static String replaceWithIndexedQuestionMarks(String input, ArrayList<Object> extractedStrings) {
Pattern pattern = Pattern.compile("^\\d{4}-\\d{1,2}-\\d{1,2}([T\\s]\\d{2}:\\d{2}:\\d{2}(\\.\\d{3}))Z$");
DateTimeFormatter rfcFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");
DateTimeFormatter localFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
for (int i = 0; i < extractedStrings.size(); ++i) {
Matcher matcher = pattern.matcher((String) extractedStrings.get(i));
if (matcher.matches()) {
// if it seems like it is a date - we are going to tyr and make it a date constant
// if it seems like it is a date - we are going to try and make it a date constant
try {
// if the pattern follows the exact pattern that comes from the hasura NDC
// we will convert it to a ANSI SQL timestamp.
// Otherwise, we will convert it to a string constant verbatim.
ZonedDateTime zonedDateTime = ZonedDateTime.parse((String) extractedStrings.get(i), rfcFormatter);
zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneOffset.UTC); // adjust timezone to UTC

Object convertedDateTime; // This will store either a Date or a Timestamp
if (zonedDateTime.toLocalTime().toSecondOfDay() == 0) {
// Represents the start of the day in UTC
convertedDateTime = Date.valueOf(zonedDateTime.toLocalDate()); // Convert to java.sql.Date with LocalDate
} else {
// Does not represent the start of the day in UTC
convertedDateTime = Timestamp.from(zonedDateTime.toInstant()); // Convert to Timestamp
}

input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
PARAM_MARKER + i + PARAM_MARKER
);
extractedStrings.set(i, Timestamp.from(zonedDateTime.toInstant()));
} catch(Exception ignored) {
extractedStrings.set(i, convertedDateTime);
} catch (Exception ignored) {
input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
PARAM_MARKER + i + PARAM_MARKER
);
}
// if it's not a date constant, we will convert it into string parameter
} else if (((String) extractedStrings.get(i)).matches("\\d{4}-\\d{2}-\\d{2}")) { // Match YYYY-MM-DD pattern
try {
Date date = Date.valueOf((String) extractedStrings.get(i)); // Convert to java.sql.Date
input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
PARAM_MARKER + i + PARAM_MARKER
);
extractedStrings.set(i, date);
} catch (Exception ignored) {
input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
PARAM_MARKER + i + PARAM_MARKER
);
}
} else if (((String) extractedStrings.get(i)).startsWith("DATE::")) {
try {
String dateStr = ((String) extractedStrings.get(i)).replace("DATE::", "");
Date date = Date.valueOf(dateStr);
input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
PARAM_MARKER + i + PARAM_MARKER
);
extractedStrings.set(i, date);
} catch (Exception ignored) {
input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
PARAM_MARKER + i + PARAM_MARKER
);
}
// if it's not a date constant, we will convert it into string parameter
} else if (((String) extractedStrings.get(i)).startsWith("TIMESTAMP::")) {
String rfcDateString = ((String) extractedStrings.get(i)).replace("TIMESTAMP::", "");
DateTimeFormatter RFC_1123_DATE_TIME = DateTimeFormatter.RFC_1123_DATE_TIME;
DateTimeFormatter RFC_3339_DATE_TIME = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("yyyy-MM-dd'T'HH:mm:ss")
.optionalStart()
.appendPattern(".SSS")
.optionalEnd()
.appendPattern("XXX")
.toFormatter();
try {
ZonedDateTime zonedDateTime = ZonedDateTime.parse(rfcDateString, RFC_1123_DATE_TIME);
Timestamp timestamp = Timestamp.from(zonedDateTime.toInstant());
input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
PARAM_MARKER + i + PARAM_MARKER
);
extractedStrings.set(i, timestamp);
} catch (Exception ignored) {
try {
ZonedDateTime zonedDateTime = ZonedDateTime.parse(rfcDateString, RFC_3339_DATE_TIME);
Timestamp timestamp = Timestamp.from(zonedDateTime.toInstant());
input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
PARAM_MARKER + i + PARAM_MARKER
);
extractedStrings.set(i, timestamp);
} catch (Exception ignore) {
input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
PARAM_MARKER + i + PARAM_MARKER
);
}
}
// if it's not a date constant, we will convert it into string parameter
} else if (((String) extractedStrings.get(i)).startsWith("STRING::")) {
String rfcDateString = ((String) extractedStrings.get(i)).replace("STRING::", "");
input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
PARAM_MARKER + i + PARAM_MARKER
);
} else {
input = input.replace(
STRING_MARKER + extractedStrings.get(i) + STRING_MARKER,
Expand Down

0 comments on commit a471f3f

Please sign in to comment.