createSymbols(outTable, stmtAlias, name, aliasedExpr.getAvroLabel(), type);
}
}
// Check the where clause for validity if it's non-null.
Expr where = s.getWhereConditions();
if (null != where) {
where.accept(this);
// The where clause must evaluate to a boolean value.
Type whereType = where.getType(exprTable);
if (!whereType.promotesTo(Type.getNullable(Type.TypeName.BOOLEAN))) {
throw new TypeCheckException("Expected where clause with boolean type, not "
+ whereType);
}
}
// Check the GROUP BY clause for validity if it's non-null.
GroupBy groupBy = s.getGroupBy();
if (null != groupBy) {
groupBy.accept(this);
}
// Check the OVER clause for validity if it's non-null.
// This must evaluate to a value of type WINDOW.
Expr windowOver = s.getWindowOver();
if (null != windowOver) {
windowOver.accept(this);
Type winType = windowOver.getType(exprTable);
if (!winType.equals(Type.getPrimitive(Type.TypeName.WINDOW))) {
throw new TypeCheckException("SELECT ... OVER clause requires a window, not an "
+ "identifier of type " + winType);
}
}
} finally {
// Pop the source symbol tables from the stack.
mSymTableContext.reset(symbolStackHeight);
mSelectNestingDepth--;
}
// Push our output symbols on the stack so any higher-level select stmt can
// type check against them. Memorize the symbols for this statement in the
// statement object itself.
if (null != outTable) {
mSymTableContext.push(outTable);
s.setFieldSymbols(outTable.cloneLevel());
}
// HAVING clause uses the output symbol names. It can only operate on
// fields already explicitly selected by the user -- if these are not
// already present, this will fail.
Expr having = s.getHaving();
if (null != having) {
having.accept(this);
// The having clause must evaluate to a boolean value.
Type havingType = having.getType(outTable);
if (!havingType.promotesTo(Type.getNullable(Type.TypeName.BOOLEAN))) {
throw new TypeCheckException("Expected having clause with boolean type, not "
+ havingType);
}
}