Skip to content

Commit

Permalink
[CALCITE-5955] BigQuery PERCENTILE functions are unparsed incorrectly
Browse files Browse the repository at this point in the history
  • Loading branch information
tanclary committed Nov 28, 2023
1 parent 2408c67 commit 565ecf0
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ ExInst<SqlValidatorException> intervalFieldExceedsPrecision(Number a0,
@BaseMessage("QUALIFY expression ''{0}'' must contain a window function")
ExInst<SqlValidatorException> qualifyExpressionMustContainWindowFunction(String a0);

@BaseMessage("ROW/RANGE not allowed with RANK, DENSE_RANK or ROW_NUMBER functions")
@BaseMessage("ROW/RANGE not allowed with RANK, DENSE_RANK, PERCENTILE_CONT/DISC or ROW_NUMBER functions")
ExInst<SqlValidatorException> rankWithFrame();

@BaseMessage("RANK or DENSE_RANK functions require ORDER BY clause in window specification")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ public BigQuerySqlDialect(SqlDialect.Context context) {
}
unparseItem(writer, call, leftPrec);
break;
case OVER:
call.operand(0).unparse(writer, leftPrec, rightPrec);
writer.keyword("OVER");
call.operand(1).unparse(writer, leftPrec, rightPrec);
break;
default:
super.unparseCall(writer, call, leftPrec, rightPrec);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public final class SqlBasicAggFunction extends SqlAggFunction {
private final boolean allowsNullTreatment;
private final boolean allowsSeparator;
private final boolean percentile;
private final boolean allowsFraming;

//~ Constructors -----------------------------------------------------------

Expand All @@ -66,7 +67,7 @@ private SqlBasicAggFunction(String name, @Nullable SqlIdentifier sqlIdentifier,
boolean requiresOrder, boolean requiresOver,
Optionality requiresGroupOrder, Optionality distinctOptionality,
SqlSyntax syntax, boolean allowsNullTreatment, boolean allowsSeparator,
boolean percentile) {
boolean percentile, boolean allowsFraming) {
super(name, sqlIdentifier, kind,
requireNonNull(returnTypeInference, "returnTypeInference"), operandTypeInference,
requireNonNull(operandTypeChecker, "operandTypeChecker"),
Expand All @@ -79,6 +80,7 @@ private SqlBasicAggFunction(String name, @Nullable SqlIdentifier sqlIdentifier,
this.allowsNullTreatment = allowsNullTreatment;
this.allowsSeparator = allowsSeparator;
this.percentile = percentile;
this.allowsFraming = allowsFraming;
}

/** Creates a SqlBasicAggFunction whose name is the same as its kind. */
Expand All @@ -95,7 +97,7 @@ public static SqlBasicAggFunction create(String name, SqlKind kind,
return new SqlBasicAggFunction(name, null, kind, returnTypeInference, null,
operandTypeChecker, null, SqlFunctionCategory.NUMERIC, false, false,
Optionality.FORBIDDEN, Optionality.OPTIONAL, SqlSyntax.FUNCTION, false,
false, false);
false, false, true);
}

//~ Methods ----------------------------------------------------------------
Expand Down Expand Up @@ -147,7 +149,7 @@ public SqlAggFunction withName(String name) {
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, getFunctionType(), requiresOrder(),
requiresOver(), requiresGroupOrder(), distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile);
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}

/** Sets {@link #getDistinctOptionality()}. */
Expand All @@ -156,7 +158,7 @@ SqlBasicAggFunction withDistinct(Optionality distinctOptionality) {
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, getFunctionType(), requiresOrder(),
requiresOver(), requiresGroupOrder(), distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile);
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}

/** Sets {@link #getFunctionType()}. */
Expand All @@ -165,7 +167,7 @@ public SqlBasicAggFunction withFunctionType(SqlFunctionCategory category) {
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, category, requiresOrder(),
requiresOver(), requiresGroupOrder(), distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile);
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}

@Override public SqlSyntax getSyntax() {
Expand All @@ -178,7 +180,7 @@ public SqlBasicAggFunction withSyntax(SqlSyntax syntax) {
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, getFunctionType(), requiresOrder(),
requiresOver(), requiresGroupOrder(), distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile);
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}

@Override public boolean allowsNullTreatment() {
Expand All @@ -191,7 +193,7 @@ public SqlBasicAggFunction withAllowsNullTreatment(boolean allowsNullTreatment)
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, getFunctionType(), requiresOrder(),
requiresOver(), requiresGroupOrder(), distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile);
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}

/** Returns whether this aggregate function allows '{@code SEPARATOR string}'
Expand All @@ -206,7 +208,7 @@ public SqlBasicAggFunction withAllowsSeparator(boolean allowsSeparator) {
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, getFunctionType(), requiresOrder(),
requiresOver(), requiresGroupOrder(), distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile);
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}

@Override public boolean isPercentile() {
Expand All @@ -219,7 +221,20 @@ public SqlBasicAggFunction withPercentile(boolean percentile) {
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, getFunctionType(), requiresOrder(),
requiresOver(), requiresGroupOrder(), distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile);
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}

@Override public boolean allowsFraming() {
return allowsFraming;
}

/** Sets {@link #allowsFraming()}. */
public SqlBasicAggFunction withAllowsFraming(boolean allowsFraming) {
return new SqlBasicAggFunction(getName(), getSqlIdentifier(), kind,
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, getFunctionType(), requiresOrder(),
requiresOver(), requiresGroupOrder(), distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}

/** Sets {@link #requiresOver()}. */
Expand All @@ -228,7 +243,7 @@ public SqlBasicAggFunction withOver(boolean over) {
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, getFunctionType(), requiresOrder(),
over, requiresGroupOrder(), distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile);
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}

/** Sets {@link #requiresGroupOrder()}. */
Expand All @@ -237,7 +252,7 @@ public SqlBasicAggFunction withGroupOrder(Optionality groupOrder) {
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, getFunctionType(), requiresOrder(),
requiresOver(), groupOrder, distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile);
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}

/** Sets that value to be returned when {@link #unwrap} is applied to
Expand All @@ -247,6 +262,6 @@ public SqlBasicAggFunction withStatic(SqlStaticAggFunction staticFun) {
getReturnTypeInference(), getOperandTypeInference(),
getOperandTypeChecker(), staticFun, getFunctionType(), requiresOrder(),
requiresOver(), requiresGroupOrder(), distinctOptionality, syntax,
allowsNullTreatment, allowsSeparator, percentile);
allowsNullTreatment, allowsSeparator, percentile, allowsFraming);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,8 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding,
.withFunctionType(SqlFunctionCategory.SYSTEM)
.withOver(true)
.withPercentile(true)
.withAllowsNullTreatment(true);
.withAllowsNullTreatment(true)
.withAllowsFraming(false);

/** The {@code PERCENTILE_DISC} function, BigQuery's
* equivalent to {@link SqlStdOperatorTable#PERCENTILE_DISC},
Expand All @@ -713,7 +714,8 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding,
.withFunctionType(SqlFunctionCategory.SYSTEM)
.withOver(true)
.withPercentile(true)
.withAllowsNullTreatment(true);
.withAllowsNullTreatment(true)
.withAllowsFraming(false);

/** The "DATE" function. It has the following overloads:
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2265,7 +2265,8 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
OperandTypes.UNIT_INTERVAL_NUMERIC_LITERAL)
.withFunctionType(SqlFunctionCategory.SYSTEM)
.withGroupOrder(Optionality.MANDATORY)
.withPercentile(true);
.withPercentile(true)
.withAllowsFraming(false);

/**
* {@code PERCENTILE_DISC} inverse distribution aggregate function.
Expand All @@ -2280,7 +2281,8 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
OperandTypes.UNIT_INTERVAL_NUMERIC_LITERAL)
.withFunctionType(SqlFunctionCategory.SYSTEM)
.withGroupOrder(Optionality.MANDATORY)
.withPercentile(true);
.withPercentile(true)
.withAllowsFraming(false);

/**
* The LISTAGG operator. String aggregator function.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ DuplicateWindowName=Duplicate window names not allowed
EmptyWindowSpec=Empty window specification not allowed
DupWindowSpec=Duplicate window specification not allowed in the same window clause
QualifyExpressionMustContainWindowFunction=QUALIFY expression ''{0}'' must contain a window function
RankWithFrame=ROW/RANGE not allowed with RANK, DENSE_RANK or ROW_NUMBER functions
RankWithFrame=ROW/RANGE not allowed with RANK, DENSE_RANK, PERCENTILE_CONT/DISC or ROW_NUMBER functions
FuncNeedsOrderBy=RANK or DENSE_RANK functions require ORDER BY clause in window specification
PartitionNotAllowed=PARTITION BY not allowed with existing window reference
OrderByOverlap=ORDER BY not allowed in both base and referenced windows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1312,6 +1312,42 @@ private static String toSql(RelNode root, SqlDialect dialect,
.withPostgresql().ok(expectedPostgresql);
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-5955">[CALCITE-5955]
* BigQuery PERCENTILE functions are unparsed incorrectly</a>. */
@Test void testPercentileContWindow() {
final String partitionQuery = "select percentile_cont(\"product_id\", 0.5)\n"
+ "over(partition by \"product_id\")\n"
+ "from \"foodmart\".\"product\"";
final String expectedPartition = "SELECT PERCENTILE_CONT(product_id, 0.5) "
+ "OVER (PARTITION BY product_id)\n"
+ "FROM foodmart.product";
final String query = "select percentile_cont(\"product_id\", 0.5) over()\n"
+ "from \"foodmart\".\"product\"";
final String expected = "SELECT PERCENTILE_CONT(product_id, 0.5) OVER ()\n"
+ "FROM foodmart.product";
sql(partitionQuery).withBigQuery().withLibrary(SqlLibrary.BIG_QUERY).ok(expectedPartition);
sql(query).withBigQuery().withLibrary(SqlLibrary.BIG_QUERY).ok(expected);
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-5955">[CALCITE-5955]
* BigQuery PERCENTILE functions are unparsed incorrectly</a>. */
@Test void testPercentileDiscWindowFrameClause() {
final String partitionQuery = "select percentile_disc(\"product_id\", 0.5)\n"
+ "over(partition by \"product_id\")\n"
+ "from \"foodmart\".\"product\"";
final String expectedPartition = "SELECT PERCENTILE_DISC(product_id, 0.5) "
+ "OVER (PARTITION BY product_id)\n"
+ "FROM foodmart.product";
final String query = "select percentile_disc(\"product_id\", 0.5) over()\n"
+ "from \"foodmart\".\"product\"";
final String expected = "SELECT PERCENTILE_DISC(product_id, 0.5) OVER ()\n"
+ "FROM foodmart.product";
sql(partitionQuery).withBigQuery().withLibrary(SqlLibrary.BIG_QUERY).ok(expectedPartition);
sql(query).withBigQuery().withLibrary(SqlLibrary.BIG_QUERY).ok(expected);
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-2722">[CALCITE-2722]
* SqlImplementor createLeftCall method throws StackOverflowError</a>. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
"Set operator cannot combine streaming and non-streaming inputs";

private static final String ROW_RANGE_NOT_ALLOWED_WITH_RANK =
"ROW/RANGE not allowed with RANK, DENSE_RANK or ROW_NUMBER functions";
"ROW/RANGE not allowed with RANK, DENSE_RANK, PERCENTILE_CONT/DISC or ROW_NUMBER functions";

private static final String RANK_REQUIRES_ORDER_BY = "RANK or DENSE_RANK "
+ "functions require ORDER BY clause in window specification";
Expand Down

0 comments on commit 565ecf0

Please sign in to comment.