Skip to content

Commit 49cac0f

Browse files
committed
[CALCITE-5543] WIP: Implement BigQuery functions for parsing DATE, TIME, TIMESTAMP, DATETIME (#27)
1 parent 88f664a commit 49cac0f

File tree

4 files changed

+86
-0
lines changed

4 files changed

+86
-0
lines changed

core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,49 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding,
881881
OperandTypes.STRING_STRING,
882882
SqlFunctionCategory.TIMEDATE);
883883

884+
/**
885+
* The "PARSE_TIME(string, string)" function (BigQuery); Converts a string representation of time
886+
* to a TIME object.
887+
*/
888+
@LibraryOperator(libraries = {BIG_QUERY})
889+
public static final SqlFunction PARSE_TIME =
890+
SqlBasicFunction.create("PARSE_TIME", ReturnTypes.TIME_NULLABLE,
891+
OperandTypes.STRING_STRING, SqlFunctionCategory.TIMEDATE);
892+
893+
/**
894+
* The "PARSE_DATE(string, string)" function (BigQuery); Converts a string representation of date
895+
* to a DATE object.
896+
*/
897+
@LibraryOperator(libraries = {BIG_QUERY})
898+
public static final SqlFunction PARSE_DATE =
899+
SqlBasicFunction.create("PARSE_DATE",
900+
ReturnTypes.DATE_NULLABLE, OperandTypes.STRING_STRING, SqlFunctionCategory.TIMEDATE);
901+
902+
/**
903+
* The "PARSE_TIMESTAMP(string, string [, timezone])" function (BigQuery); Formats a timestamp
904+
* object according to the specified string.
905+
*
906+
* <p>In BigQuery, the "TIMESTAMP" datatype maps to Calcite's
907+
* TIMESTAMP_WITH_LOCAL_TIME_ZONE
908+
*/
909+
@LibraryOperator(libraries = {BIG_QUERY})
910+
public static final SqlFunction PARSE_TIMESTAMP =
911+
SqlBasicFunction.create("PARSE_TIMESTAMP",
912+
ReturnTypes.TIMESTAMP_LTZ_NULLABLE, OperandTypes.STRING_STRING_OPTIONAL_STRING,
913+
SqlFunctionCategory.TIMEDATE);
914+
915+
/**
916+
* The "PARSE_DATETIME(string, string [, timezone])" function (BigQuery); Formats a timestamp
917+
* object according to the specified string.
918+
*
919+
* <p>Note that the {@code TIMESTAMP} type of Calcite and Standard SQL
920+
* is called {@code DATETIME} in BigQuery.
921+
*/
922+
@LibraryOperator(libraries = {BIG_QUERY})
923+
public static final SqlFunction PARSE_DATETIME =
924+
SqlBasicFunction.create("PARSE_DATETIME", ReturnTypes.TIMESTAMP_NULLABLE,
925+
OperandTypes.STRING_STRING, SqlFunctionCategory.TIMEDATE);
926+
884927
/** The "FORMAT_TIME(string, time)" function (BigQuery);
885928
* Formats a time object according to the specified string. */
886929
@LibraryOperator(libraries = {BIG_QUERY})

core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,13 @@ public static SqlCall stripSeparator(SqlCall call) {
352352
public static final SqlReturnTypeInference TIMESTAMP_LTZ =
353353
explicit(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
354354

355+
/**
356+
* Type-inference strategy whereby the result type of a call is nullable
357+
* TIMESTAMP WITH LOCAL TIME ZONE.
358+
*/
359+
public static final SqlReturnTypeInference TIMESTAMP_LTZ_NULLABLE =
360+
TIMESTAMP_LTZ.andThen(SqlTypeTransforms.TO_NULLABLE);
361+
355362
/**
356363
* Type-inference strategy whereby the result type of a call is Double.
357364
*/

core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,6 +1828,38 @@ private SqlDialect nonOrdinalDialect() {
18281828
sql(query).withBigQuery().ok(expected);
18291829
}
18301830

1831+
@Test void testBigQueryParseDatetimeFunctions() {
1832+
String parseTime = "select parse_time('%I:%M:%S', '07:30:00')\n"
1833+
+ "from \"foodmart\".\"product\"\n";
1834+
final String expectedTimestampTrunc =
1835+
"SELECT PARSE_TIME('%I:%M:%S', '07:30:00')\n"
1836+
+ "FROM \"foodmart\".\"product\"";
1837+
sql(parseTime).withLibrary(SqlLibrary.BIG_QUERY).ok(expectedTimestampTrunc);
1838+
1839+
String parseDate = "select parse_date('%A %b %e %Y', 'Thursday Dec 25 2008')\n"
1840+
+ "from \"foodmart\".\"product\"\n";
1841+
final String expectedParseDate =
1842+
"SELECT PARSE_DATE('%A %b %e %Y', 'Thursday Dec 25 2008')\n"
1843+
+ "FROM \"foodmart\".\"product\"";
1844+
sql(parseDate).withLibrary(SqlLibrary.BIG_QUERY).ok(expectedParseDate);
1845+
1846+
String parseTimestamp =
1847+
"select parse_timestamp('%a %b %e %I:%M:%S %Y', 'Thu Dec 25 07:30:00 2008')\n"
1848+
+ "from \"foodmart\".\"product\"\n";
1849+
final String expectedParseTimestamp =
1850+
"SELECT PARSE_TIMESTAMP('%a %b %e %I:%M:%S %Y', 'Thu Dec 25 07:30:00 2008')\n"
1851+
+ "FROM \"foodmart\".\"product\"";
1852+
sql(parseTimestamp).withLibrary(SqlLibrary.BIG_QUERY).ok(expectedParseTimestamp);
1853+
1854+
String parseDatetime =
1855+
"select parse_datetime('%a %b %e %I:%M:%S %Y', 'Thu Dec 25 07:30:00 2008')\n"
1856+
+ "from \"foodmart\".\"product\"\n";
1857+
final String expectedParseDatetime =
1858+
"SELECT PARSE_DATETIME('%a %b %e %I:%M:%S %Y', 'Thu Dec 25 07:30:00 2008')\n"
1859+
+ "FROM \"foodmart\".\"product\"";
1860+
sql(parseDatetime).withLibrary(SqlLibrary.BIG_QUERY).ok(expectedParseDatetime);
1861+
}
1862+
18311863
@Test void testBigQueryTimeTruncFunctions() {
18321864
String timestampTrunc = "select timestamp_trunc(timestamp '2012-02-03 15:30:00', month)\n"
18331865
+ "from \"foodmart\".\"product\"\n";

site/_docs/reference.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,6 +2713,10 @@ BigQuery's type system uses confusingly different names for types and functions:
27132713
| b m p | MD5(string) | Calculates an MD5 128-bit checksum of *string* and returns it as a hex string
27142714
| m | MONTHNAME(date) | Returns the name, in the connection's locale, of the month in *datetime*; for example, it returns '二月' for both DATE '2020-02-10' and TIMESTAMP '2020-02-10 10:10:10'
27152715
| o | NVL(value1, value2) | Returns *value1* if *value1* is not null, otherwise *value2*
2716+
| b | PARSE_DATE(string1, string2) | Uses format specified by *string1* to convert *string2* representation of date to a DATE object
2717+
| b | PARSE_DATETIME(string1, string2) | Uses format specified by *string1* to convert *string2* representation of datetime to a TIMESTAMP object
2718+
| b | PARSE_TIME(string1, string2) | Uses format specified by *string1* to convert *string2* representation of time to a TIME object
2719+
| b | PARSE_TIMESTAMP(string1, string2[, timeZone]) | Uses format specified by *string1* to convert *string2* representation of timestamp to a TIMESTAMP WITH LOCAL TIME ZONE object in *timeZone*
27162720
| b | POW(numeric1, numeric2) | Returns *numeric1* raised to the power *numeric2*
27172721
| m o | REGEXP_REPLACE(string, regexp, rep, [, pos [, occurrence [, matchType]]]) | Replaces all substrings of *string* that match *regexp* with *rep* at the starting *pos* in expr (if omitted, the default is 1), *occurrence* means which occurrence of a match to search for (if omitted, the default is 1), *matchType* specifies how to perform matching
27182722
| b m p | REPEAT(string, integer) | Returns a string consisting of *string* repeated of *integer* times; returns an empty string if *integer* is less than 1

0 commit comments

Comments
 (0)