Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CALCITE-5180] Implement BigQuery Date/Time Type Aliases and Constructors #3023

Closed
wants to merge 11 commits into from
5 changes: 1 addition & 4 deletions babel/src/main/codegen/config.fmpp
Original file line number Diff line number Diff line change
Expand Up @@ -531,11 +531,8 @@ data: {

# List of methods for parsing builtin function calls.
# Return type of method implementation should be "SqlNode".
# Example: "DateFunctionCall()".
# Example: "DateaddFunctionCall()".
builtinFunctionCallMethods: [
"DateFunctionCall()"
"TimestampFunctionCall()"
"TimeFunctionCall()"
"DateaddFunctionCall()"
]

Expand Down
60 changes: 0 additions & 60 deletions babel/src/main/codegen/includes/parserImpls.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -22,66 +22,6 @@ JoinType LeftSemiJoin() :
<LEFT> <SEMI> <JOIN> { return JoinType.LEFT_SEMI_JOIN; }
}

SqlNode DateFunctionCall() :
{
final SqlFunctionCategory funcType = SqlFunctionCategory.TIMEDATE;
final SqlIdentifier qualifiedName;
final Span s;
final SqlLiteral quantifier;
final List<? extends SqlNode> args;
}
{
<DATE> {
s = span();
qualifiedName = new SqlIdentifier(unquotedIdentifier(), getPos());
}
args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
quantifier = (SqlLiteral) args.get(0);
args.remove(0);
return createCall(qualifiedName, s.end(this), funcType, quantifier, args);
}
}

SqlNode TimestampFunctionCall() :
{
final SqlFunctionCategory funcType = SqlFunctionCategory.TIMEDATE;
final SqlIdentifier qualifiedName;
final Span s;
final SqlLiteral quantifier;
final List<? extends SqlNode> args;
}
{
<TIMESTAMP> {
s = span();
qualifiedName = new SqlIdentifier(unquotedIdentifier(), getPos());
}
args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
quantifier = (SqlLiteral) args.get(0);
args.remove(0);
return createCall(qualifiedName, s.end(this), funcType, quantifier, args);
}
}

SqlNode TimeFunctionCall() :
{
final SqlFunctionCategory funcType = SqlFunctionCategory.TIMEDATE;
final SqlIdentifier qualifiedName;
final Span s;
final SqlLiteral quantifier;
final List<? extends SqlNode> args;
}
{
<TIME> {
s = span();
qualifiedName = new SqlIdentifier(unquotedIdentifier(), getPos());
}
args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
quantifier = (SqlLiteral) args.get(0);
args.remove(0);
return createCall(qualifiedName, s.end(this), funcType, quantifier, args);
}
}

SqlNode DateaddFunctionCall() :
{
final Span s;
Expand Down
121 changes: 90 additions & 31 deletions babel/src/test/resources/sql/big-query.iq
Original file line number Diff line number Diff line change
Expand Up @@ -1097,12 +1097,74 @@ select unix_date(datetime '2008-12-25') as d;
#
# Return Data Type: DATE

select date(2022, 11, 15) as d;
+------------+
| d |
+------------+
| 2022-11-15 |
+------------+
select date(2022, 11, 15) as d1,
date(datetime "2008-01-01 01:03:05") as d2,
date(datetime(2008, 1, 1, 1, 3, 5)) as d3;
+------------+------------+------------+
| d1 | d2 | d3 |
+------------+------------+------------+
| 2022-11-15 | 2008-01-01 | 2008-01-01 |
+------------+------------+------------+
(1 row)

!ok

# Test timezone conversion when converting TIMESTAMP to DATE.
# Denver observes DST whereas Phoenix does not.
# Both cities have a -07:00 offset in winter, but Denver has -06:00 in summer.
select date(timestamp("2008-06-21 06:30:00")) as sum_utc,
date(timestamp("2008-06-21 06:30:00"), "America/Denver") as sum_dst,
date(timestamp("2008-06-21 06:30:00"), "America/Phoenix") as sum_std,
date(timestamp("2008-12-21 06:30:00")) as win_utc,
date(timestamp("2008-12-21 06:30:00"), "America/Denver") as win_dst,
date(timestamp("2008-12-21 06:30:00"), "America/Phoenix") as win_std;
+------------+------------+------------+------------+------------+------------+
| sum_utc | sum_dst | sum_std | win_utc | win_dst | win_std |
+------------+------------+------------+------------+------------+------------+
| 2008-06-21 | 2008-06-21 | 2008-06-20 | 2008-12-21 | 2008-12-20 | 2008-12-20 |
+------------+------------+------------+------------+------------+------------+
(1 row)

!ok

#####################################################################
# DATETIME
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's a set of DATETIME tests currently disabled around line 650 btw

# DATETIME(year, month, day, hour, minute, second)

#
# 1. DATETIME(year, month, day, hour, minute, second)
# 2. DATETIME(date_expression[, time_expression])
# 3. DATETIME(timestamp_expression[, time_zone])
#
# 1. Constructs a DATETIME object using INT64 values representing the year,
# month, day, hour, minute, and second.
# 2. Constructs a DATETIME object using a DATE object and an optional TIME
# object.
# 3. Constructs a DATETIME object using a TIMESTAMP object. It supports an
# optional parameter to specify a time zone. If no time zone is specified,
# the default time zone, UTC, is used.
#
# Return Data Type: DATETIME

select datetime(2003, 05, 25, 12, 30, 59) as dt1,
datetime(date(2003, 05, 25)) as d2,
datetime(date(2003, 05, 25), time(12, 30, 59)) as d3;
+---------------------+---------------------+---------------------+
| dt1 | d2 | d3 |
+---------------------+---------------------+---------------------+
| 2003-05-25 12:30:59 | 2003-05-25 00:00:00 | 2003-05-25 12:30:59 |
+---------------------+---------------------+---------------------+
(1 row)

!ok

# This tests time zone conversion from a timestamp.
select datetime(timestamp "2008-01-01 01:03:05") as t_winter,
datetime(timestamp "2008-01-01 01:03:05", "America/Los_Angeles") as t_winter_offs,
datetime(timestamp "2008-07-01 01:03:05", "America/Los_Angeles") as t_summer_offs;
+---------------------+---------------------+---------------------+
| t_winter | t_winter_offs | t_summer_offs |
+---------------------+---------------------+---------------------+
| 2008-01-01 01:03:05 | 2007-12-31 17:03:05 | 2008-06-30 18:03:05 |
+---------------------+---------------------+---------------------+
(1 row)

!ok
Expand Down Expand Up @@ -1144,6 +1206,19 @@ select timestamp("2008-01-01 01:03:05+00") as t_space,

!ok

# This tests time zone conversion from a datetime.
select timestamp(datetime "2008-01-01 01:03:05") as t_winter,
timestamp(datetime "2008-01-01 01:03:05", "America/Los_Angeles") as t_winter_offs,
timestamp(datetime "2008-07-01 01:03:05", "America/Los_Angeles") as t_summer_offs;
+---------------------+---------------------+---------------------+
| t_winter | t_winter_offs | t_summer_offs |
+---------------------+---------------------+---------------------+
| 2008-01-01 01:03:05 | 2008-01-01 09:03:05 | 2008-07-01 08:03:05 |
+---------------------+---------------------+---------------------+
(1 row)

!ok

#####################################################################
# TIME
#
Expand All @@ -1158,32 +1233,16 @@ select timestamp("2008-01-01 01:03:05+00") as t_space,
# time zone, UTC, is used.
# 3. Constructs a TIME object using a DATETIME object.
#
# Return Data Type: TIMESTAMP

select time(12, 30, 59) as t;
+----------+
| t |
+----------+
| 12:30:59 |
+----------+
(1 row)

!ok
# Return Data Type: TIME

# Test timezone conversion when converting TIMESTAMP to DATE.
# Denver observes DST whereas Phoenix does not.
# Both cities have a -07:00 offset in winter, but Denver has -06:00 in summer.
select date(timestamp("2008-06-21 06:30:00")) as sum_utc,
date(timestamp("2008-06-21 06:30:00"), "America/Denver") as sum_dst,
date(timestamp("2008-06-21 06:30:00"), "America/Phoenix") as sum_std,
date(timestamp("2008-12-21 06:30:00")) as win_utc,
date(timestamp("2008-12-21 06:30:00"), "America/Denver") as win_dst,
date(timestamp("2008-12-21 06:30:00"), "America/Phoenix") as win_std;
+------------+------------+------------+------------+------------+------------+
| sum_utc | sum_dst | sum_std | win_utc | win_dst | win_std |
+------------+------------+------------+------------+------------+------------+
| 2008-06-21 | 2008-06-21 | 2008-06-20 | 2008-12-21 | 2008-12-20 | 2008-12-20 |
+------------+------------+------------+------------+------------+------------+
select time(12, 30, 59) as t1,
time(datetime(2008, 01, 01, 12, 30, 59)) as t2,
time(datetime(2008, 07, 01, 12, 30, 59)) as t3;
+----------+----------+----------+
| t1 | t2 | t3 |
+----------+----------+----------+
| 12:30:59 | 12:30:59 | 12:30:59 |
+----------+----------+----------+
(1 row)

!ok
Expand Down
101 changes: 101 additions & 0 deletions core/src/main/codegen/templates/Parser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -4034,6 +4034,7 @@ SqlNode AtomicRowExpression() :
}
{
(
LOOKAHEAD(2)
e = LiteralOrIntervalExpression()
|
e = DynamicParam()
Expand Down Expand Up @@ -4630,6 +4631,98 @@ SqlLiteral DateTimeLiteral() :
}
}

/**
* Parses BigQuery's built-in DATE() function.
*/
SqlNode DateFunctionCall() :
{
final SqlFunctionCategory funcType = SqlFunctionCategory.TIMEDATE;
final SqlIdentifier qualifiedName;
final Span s;
final SqlLiteral quantifier;
final List<? extends SqlNode> args;
}
{
<DATE> {
s = span();
qualifiedName = new SqlIdentifier(unquotedIdentifier(), getPos());
}
args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
quantifier = (SqlLiteral) args.get(0);
args.remove(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this line doing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look at the production for FunctionParameterList() you'll see it start like this:

{
    <LPAREN>
    (
        qualifier = AllOrDistinct() { list.add(qualifier); }
    |
        { list.add(null); }
    )
    ...

Here, list is the return value (args here in DateFunctionCall()). So the first argument is always either null, or either ALL or DISTINCT, since some functions can take those qualifiers. Here, it should always be null, although I suppose this means the parser would silently accept something like DATE(DISTINCT 2023, 01, 18), which it probably shouldn't.

return createCall(qualifiedName, s.end(this), funcType, quantifier, args);
}
}

/**
* Parses BigQuery's built-in DATETIME() function.
*/
SqlNode DatetimeFunctionCall() :
{
final SqlFunctionCategory funcType = SqlFunctionCategory.TIMEDATE;
final SqlIdentifier qualifiedName;
final Span s;
final SqlLiteral quantifier;
final List<? extends SqlNode> args;
}
{
<DATETIME> {
s = span();
qualifiedName = new SqlIdentifier(unquotedIdentifier(), getPos());
}
args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
quantifier = (SqlLiteral) args.get(0);
args.remove(0);
return createCall(qualifiedName, s.end(this), funcType, quantifier, args);
}
}

/**
* Parses BigQuery's built-in TIMESTAMP() function.
*/
SqlNode TimestampFunctionCall() :
{
final SqlFunctionCategory funcType = SqlFunctionCategory.TIMEDATE;
final SqlIdentifier qualifiedName;
final Span s;
final SqlLiteral quantifier;
final List<? extends SqlNode> args;
}
{
<TIMESTAMP> {
s = span();
qualifiedName = new SqlIdentifier(unquotedIdentifier(), getPos());
}
args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
quantifier = (SqlLiteral) args.get(0);
args.remove(0);
return createCall(qualifiedName, s.end(this), funcType, quantifier, args);
}
}

/**
* Parses BigQuery's built-in TIME() function.
*/
SqlNode TimeFunctionCall() :
{
final SqlFunctionCategory funcType = SqlFunctionCategory.TIMEDATE;
final SqlIdentifier qualifiedName;
final Span s;
final SqlLiteral quantifier;
final List<? extends SqlNode> args;
}
{
<TIME> {
s = span();
qualifiedName = new SqlIdentifier(unquotedIdentifier(), getPos());
}
args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
quantifier = (SqlLiteral) args.get(0);
args.remove(0);
return createCall(qualifiedName, s.end(this), funcType, quantifier, args);
}
}

/** Parses a MULTISET constructor */
SqlNode MultisetConstructor() :
{
Expand Down Expand Up @@ -6044,6 +6137,14 @@ SqlNode BuiltinFunctionCall() :
<RPAREN> {
return SqlStdOperatorTable.TRIM.createCall(s.end(this), args);
}
|
node = DateFunctionCall() { return node; }
|
node = DatetimeFunctionCall() { return node; }
|
node = TimeFunctionCall() { return node; }
|
node = TimestampFunctionCall() { return node; }
|
node = TimestampAddFunctionCall() { return node; }
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
import static org.apache.calcite.sql.fun.SqlLibraryOperators.COSH;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.DATE;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.DATEADD;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.DATETIME;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.DATE_FROM_UNIX_DATE;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.DAYNAME;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.DIFFERENCE;
Expand Down Expand Up @@ -536,6 +537,7 @@ Builder populate2() {
defineMethod(UNIX_DATE, "unixDate", NullPolicy.STRICT);

defineMethod(DATE, "date", NullPolicy.STRICT);
defineMethod(DATETIME, "datetime", NullPolicy.STRICT);
defineMethod(TIMESTAMP, "timestamp", NullPolicy.STRICT);
defineMethod(TIME, "time", NullPolicy.STRICT);

Expand Down