diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java index 3dbc6be3231..a1a4c080228 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java @@ -881,6 +881,49 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding, OperandTypes.STRING_STRING, SqlFunctionCategory.TIMEDATE); + /** + * The "PARSE_TIME(string, string)" function (BigQuery); Converts a string representation of time + * to a TIME object. + */ + @LibraryOperator(libraries = {BIG_QUERY}) + public static final SqlFunction PARSE_TIME = + SqlBasicFunction.create("PARSE_TIME", ReturnTypes.TIME_NULLABLE, + OperandTypes.STRING_STRING, SqlFunctionCategory.TIMEDATE); + + /** + * The "PARSE_DATE(string, string)" function (BigQuery); Converts a string representation of date + * to a DATE object. + */ + @LibraryOperator(libraries = {BIG_QUERY}) + public static final SqlFunction PARSE_DATE = + SqlBasicFunction.create("PARSE_DATE", + ReturnTypes.DATE_NULLABLE, OperandTypes.STRING_STRING, SqlFunctionCategory.TIMEDATE); + + /** + * The "PARSE_TIMESTAMP(string, string [, timezone])" function (BigQuery); Formats a timestamp + * object according to the specified string. + * + *

In BigQuery, the "TIMESTAMP" datatype maps to Calcite's + * TIMESTAMP_WITH_LOCAL_TIME_ZONE + */ + @LibraryOperator(libraries = {BIG_QUERY}) + public static final SqlFunction PARSE_TIMESTAMP = + SqlBasicFunction.create("PARSE_TIMESTAMP", + ReturnTypes.TIMESTAMP_LTZ_NULLABLE, OperandTypes.STRING_STRING_OPTIONAL_STRING, + SqlFunctionCategory.TIMEDATE); + + /** + * The "PARSE_DATETIME(string, string [, timezone])" function (BigQuery); Formats a timestamp + * object according to the specified string. + * + *

Note that the {@code TIMESTAMP} type of Calcite and Standard SQL + * is called {@code DATETIME} in BigQuery. + */ + @LibraryOperator(libraries = {BIG_QUERY}) + public static final SqlFunction PARSE_DATETIME = + SqlBasicFunction.create("PARSE_DATETIME", ReturnTypes.TIMESTAMP_NULLABLE, + OperandTypes.STRING_STRING, SqlFunctionCategory.TIMEDATE); + /** The "FORMAT_TIME(string, time)" function (BigQuery); * Formats a time object according to the specified string. */ @LibraryOperator(libraries = {BIG_QUERY}) diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java index fa795d044eb..3b68ff04fe0 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java +++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java @@ -352,6 +352,13 @@ public static SqlCall stripSeparator(SqlCall call) { public static final SqlReturnTypeInference TIMESTAMP_LTZ = explicit(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE); + /** + * Type-inference strategy whereby the result type of a call is nullable + * TIMESTAMP WITH LOCAL TIME ZONE. + */ + public static final SqlReturnTypeInference TIMESTAMP_LTZ_NULLABLE = + TIMESTAMP_LTZ.andThen(SqlTypeTransforms.TO_NULLABLE); + /** * Type-inference strategy whereby the result type of a call is Double. */ diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java index 21929a6bdf2..08bef2ccc6c 100644 --- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java +++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java @@ -1828,6 +1828,38 @@ private SqlDialect nonOrdinalDialect() { sql(query).withBigQuery().ok(expected); } + @Test void testBigQueryParseDatetimeFunctions() { + String parseTime = "select parse_time('%I:%M:%S', '07:30:00')\n" + + "from \"foodmart\".\"product\"\n"; + final String expectedTimestampTrunc = + "SELECT PARSE_TIME('%I:%M:%S', '07:30:00')\n" + + "FROM \"foodmart\".\"product\""; + sql(parseTime).withLibrary(SqlLibrary.BIG_QUERY).ok(expectedTimestampTrunc); + + String parseDate = "select parse_date('%A %b %e %Y', 'Thursday Dec 25 2008')\n" + + "from \"foodmart\".\"product\"\n"; + final String expectedParseDate = + "SELECT PARSE_DATE('%A %b %e %Y', 'Thursday Dec 25 2008')\n" + + "FROM \"foodmart\".\"product\""; + sql(parseDate).withLibrary(SqlLibrary.BIG_QUERY).ok(expectedParseDate); + + String parseTimestamp = + "select parse_timestamp('%a %b %e %I:%M:%S %Y', 'Thu Dec 25 07:30:00 2008')\n" + + "from \"foodmart\".\"product\"\n"; + final String expectedParseTimestamp = + "SELECT PARSE_TIMESTAMP('%a %b %e %I:%M:%S %Y', 'Thu Dec 25 07:30:00 2008')\n" + + "FROM \"foodmart\".\"product\""; + sql(parseTimestamp).withLibrary(SqlLibrary.BIG_QUERY).ok(expectedParseTimestamp); + + String parseDatetime = + "select parse_datetime('%a %b %e %I:%M:%S %Y', 'Thu Dec 25 07:30:00 2008')\n" + + "from \"foodmart\".\"product\"\n"; + final String expectedParseDatetime = + "SELECT PARSE_DATETIME('%a %b %e %I:%M:%S %Y', 'Thu Dec 25 07:30:00 2008')\n" + + "FROM \"foodmart\".\"product\""; + sql(parseDatetime).withLibrary(SqlLibrary.BIG_QUERY).ok(expectedParseDatetime); + } + @Test void testBigQueryTimeTruncFunctions() { String timestampTrunc = "select timestamp_trunc(timestamp '2012-02-03 15:30:00', month)\n" + "from \"foodmart\".\"product\"\n"; diff --git a/site/_docs/reference.md b/site/_docs/reference.md index b7847554016..9cce589417c 100644 --- a/site/_docs/reference.md +++ b/site/_docs/reference.md @@ -2713,6 +2713,10 @@ BigQuery's type system uses confusingly different names for types and functions: | b m p | MD5(string) | Calculates an MD5 128-bit checksum of *string* and returns it as a hex string | 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' | o | NVL(value1, value2) | Returns *value1* if *value1* is not null, otherwise *value2* +| b | PARSE_DATE(string1, string2) | Uses format specified by *string1* to convert *string2* representation of date to a DATE object +| b | PARSE_DATETIME(string1, string2) | Uses format specified by *string1* to convert *string2* representation of datetime to a TIMESTAMP object +| b | PARSE_TIME(string1, string2) | Uses format specified by *string1* to convert *string2* representation of time to a TIME object +| 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* | b | POW(numeric1, numeric2) | Returns *numeric1* raised to the power *numeric2* | 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 | 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