Skip to content

Commit

Permalink
[CALCITE-6814] Support weekofyear fucntion for Hive
Browse files Browse the repository at this point in the history
  • Loading branch information
xuyu committed Feb 4, 2025
1 parent f10d0ce commit 2ee8d47
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@
import static org.apache.calcite.sql.fun.SqlLibraryOperators.UNIX_SECONDS;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.URL_DECODE;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.URL_ENCODE;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.WEEKOFYEAR;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.XML_TRANSFORM;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ABS;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ACOS;
Expand Down Expand Up @@ -960,6 +961,8 @@ void populate2() {
BuiltInMethod.DATE_FROM_UNIX_DATE.method, NullPolicy.STRICT);
defineMethod(UNIX_DATE, BuiltInMethod.UNIX_DATE.method,
NullPolicy.STRICT);
defineMethod(WEEKOFYEAR, BuiltInMethod.WEEK_OF_YEAR.method,
NullPolicy.STRICT);

// Datetime constructors
defineMethod(DATE, BuiltInMethod.DATE.method, NullPolicy.STRICT);
Expand Down
63 changes: 62 additions & 1 deletion core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
import java.text.Normalizer;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
Expand All @@ -104,8 +105,10 @@
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.format.SignStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
Expand All @@ -114,6 +117,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -145,6 +149,9 @@
import static java.lang.Long.parseLong;
import static java.lang.Short.parseShort;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.YEAR;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;

Expand Down Expand Up @@ -231,7 +238,7 @@ public class SqlFunctions {
new DateTimeFormatterBuilder()
// Unlike ISO 8601, BQ only supports years between 1 - 9999,
// but can support single-digit month and day parts.
.appendValue(ChronoField.YEAR, 4)
.appendValue(YEAR, 4)
.appendLiteral('-')
.appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NOT_NEGATIVE)
.appendLiteral('-')
Expand Down Expand Up @@ -5514,6 +5521,60 @@ public static String dayNameWithDate(int date, Locale locale) {
.format(ROOT_DAY_FORMAT.withLocale(locale));
}

/**
* SQL {@code WEEKOFYEAR} function, applied to a String argument.
*
* @param date string of date
* @return The week of the year of the given date
*/
public static @Nullable Integer weekOfYear(String date) {
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.setGregorianChange(new Date(Long.MIN_VALUE));
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setMinimalDaysInFirstWeek(4);
try {
LocalDate localDate = valueOf(date.trim());
calendar.setTimeInMillis(localDate.atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
return calendar.get(Calendar.WEEK_OF_YEAR);
} catch (DateTimeParseException | IllegalArgumentException e) {
return null;
}
}

/**
* Obtains an instance of Date from a text string such as 2021-02-22T09:39:27.
* Other supported formats are "2021-02-22T09:39:27Z", "2021-02-22 09:39:27",
* "2021-02-22T09:39:27+00:00", "2021-02-22". Any time information is simply
* dropped.
*
* @param text the text to parse, not null
* @return The {@code LocalDate} objects parsed from the text
* @throws IllegalArgumentException if the text cannot be parsed into a
* {@code Date}
* @throws NullPointerException if {@code text} is null
*/
public static LocalDate valueOf(final String text) {
String s = requireNonNull(text, "text").trim();
ParsePosition pos = new ParsePosition(0);
try {
DateTimeFormatter format =
new DateTimeFormatterBuilder().appendValue(YEAR, 1, 10, SignStyle.NORMAL)
.appendLiteral('-')
.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL)
.appendLiteral('-')
.appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NORMAL)
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
TemporalAccessor t = format.parseUnresolved(s, pos);
if (pos.getErrorIndex() >= 0) {
throw new DateTimeParseException("Text could not be parsed to date", s, pos.getErrorIndex());
}
return LocalDate.of(t.get(YEAR), t.get(MONTH_OF_YEAR), t.get(DAY_OF_MONTH));
} catch (DateTimeException e) {
throw new IllegalArgumentException("Cannot create date, parsing error");SqlFunctions
}
}

/**
* SQL {@code MONTHNAME} function, applied to a TIMESTAMP argument.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,14 @@ private SqlLibraryOperators() {
OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.DATE,
SqlTypeFamily.DATE));

/** The "WEEKOFYEAR(datetime)" function
* (HIVE) return the week of the year of the given date. */
@LibraryOperator(libraries = {HIVE})
public static final SqlFunction WEEKOFYEAR =
SqlBasicFunction.create("WEEKOFYEAR",
ReturnTypes.INTEGER_FORCE_NULLABLE,
OperandTypes.STRING, SqlFunctionCategory.TIMEDATE);

/** The "CONVERT(type, expr [,style])" function (Microsoft SQL Server).
*
* <p>Syntax:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,13 @@ public static SqlCall stripSeparator(SqlCall call) {
public static final SqlReturnTypeInference INTEGER_NULLABLE =
INTEGER.andThen(SqlTypeTransforms.TO_NULLABLE);

/**
* Type-inference strategy whereby the result type of a call is a nullable
* INT.
*/
public static final SqlReturnTypeInference INTEGER_FORCE_NULLABLE =
INTEGER.andThen(SqlTypeTransforms.FORCE_NULLABLE);

/**
* Type-inference strategy whereby the result type of a call is a BIGINT.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ public enum BuiltInMethod {
SHA256(SqlFunctions.class, "sha256", String.class),
SHA512(SqlFunctions.class, "sha512", String.class),
THROW_UNLESS(SqlFunctions.class, "throwUnless", boolean.class, String.class),
WEEK_OF_YEAR(SqlFunctions.class, "weekOfYear", String.class),
COMPRESS(CompressionFunctions.class, "compress", String.class),
URL_DECODE(UrlFunctions.class, "urlDecode", String.class),
URL_ENCODE(UrlFunctions.class, "urlEncode", String.class),
Expand Down
1 change: 1 addition & 0 deletions site/_docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -3032,6 +3032,7 @@ In the following:
| b s | UNIX_DATE(date) | Returns the number of days since 1970-01-01
| s | URL_DECODE(string) | Decodes a *string* in 'application/x-www-form-urlencoded' format using a specific encoding scheme, returns original *string* when decoded error
| s | URL_ENCODE(string) | Translates a *string* into 'application/x-www-form-urlencoded' format using a specific encoding scheme
| h | WEEKOFYEAR(string) | Returns the week of the year of the given date, from 1 to 54
| o | XMLTRANSFORM(xml, xslt) | Applies XSLT transform *xslt* to XML string *xml* and returns the result

Note:
Expand Down
25 changes: 25 additions & 0 deletions testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5559,6 +5559,31 @@ void testBitGetFunc(SqlOperatorFixture f, String functionName) {
f0.forEachLibrary(libraries, consumer);
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-6814">[CALCITE-6814]
* Add base64 function (enabled in Hive library)</a>. */
@Test void testWeekOfYear() {
final SqlOperatorFixture f0 = fixture().setFor(SqlLibraryOperators.WEEKOFYEAR);
final Consumer<SqlOperatorFixture> consumer = f -> {
f.checkString("weekofyear('2022-02-01')",
"5",
"INTEGER");
f.checkString("weekofyear('2022-02-01 22:21:23')",
"5",
"INTEGER");
f.checkString("weekofyear('2022-02-01T09:39:27Z')",
"5",
"INTEGER");
f.checkNull("weekofyear(NULL)");
f.checkNull("weekofyear('2022-22-01')");
f.checkNull("weekofyear('aawwss')");
f.checkNull("weekofyear('2022')");
};
final List<SqlLibrary> libraries =
list(SqlLibrary.HIVE);
f0.forEachLibrary(libraries, consumer);
}

@Test void testToDatePg() {
final SqlOperatorFixture f = fixture().withLibrary(SqlLibrary.POSTGRESQL)
.setFor(SqlLibraryOperators.TO_DATE_PG);
Expand Down

0 comments on commit 2ee8d47

Please sign in to comment.