MCPcopy Create free account
hub / github.com/questdb/questdb / parsePivot

Method parsePivot

core/src/main/java/io/questdb/griffin/SqlParser.java:3888–4066  ·  view source on GitHub ↗

Parses PIVOT clause with the following syntax: PIVOT ( agg_func(col) [AS alias], ... FOR pivot_col IN (val1 [AS alias1], val2 [AS alias2], ... | subquery) [FOR pivot_col2 IN (...)] [GROUP BY col1, col2, ...] ) [alias] Note: ELSE clause is not supported in PIVO

(GenericLexer lexer, IQueryModel model, SqlParserCallback sqlParserCallback)

Source from the content-addressed store, hash-verified

3886 * do not support ELSE in PIVOT. For such requirements, user can use subqueries instead.
3887 */
3888 private CharSequence parsePivot(GenericLexer lexer, IQueryModel model, SqlParserCallback sqlParserCallback) throws SqlException {
3889 CharSequence tok;
3890 expectTok(lexer, '(');
3891
3892 // Parse aggregate functions.
3893 FunctionFactoryCache functionFactoryCache = cairoEngine.getFunctionFactoryCache();
3894 pivotAliasMap.clear();
3895 do {
3896 model.addPivotGroupByColumn(parsePivotAggregateColumn(lexer, model, functionFactoryCache, sqlParserCallback));
3897 tok = optTok(lexer);
3898 } while (tok != null && isNotForKeyword(tok) && isComma(tok));
3899
3900 ObjList<QueryColumn> pivotGroupByCols = model.getPivotGroupByColumns();
3901 boolean hasNoAlias = false;
3902 for (int i = 0, n = pivotGroupByCols.size(); i < n; i++) {
3903 QueryColumn qc = pivotGroupByCols.getQuick(i);
3904 if (qc.getAlias() == null) {
3905 hasNoAlias = true;
3906 CharacterStoreEntry entry = characterStore.newEntry();
3907 qc.getAst().toSink(entry);
3908 CharSequence alias = SqlUtil.createExprColumnAlias(
3909 characterStore,
3910 entry.toImmutable(),
3911 pivotAliasMap,
3912 aliasSequenceMap,
3913 configuration.getColumnAliasGeneratedMaxSize(),
3914 true
3915 );
3916 pivotAliasMap.add(alias);
3917 qc.setAlias(alias, qc.getAst().position);
3918 }
3919 }
3920 model.setPivotGroupByColumnHasNoAlias(hasNoAlias && pivotGroupByCols.size() == 1);
3921
3922 if (tok == null || isNotForKeyword(tok)) {
3923 throw SqlException.$(lexer.lastTokenPosition(), "expected FOR");
3924 }
3925
3926 // Parse FOR expressions (e.g., FOR region IN ('East', 'West') ELSE 'Other')
3927 // We parse "col IN (values)" separately (not as standard IN expression) because
3928 // PIVOT supports per-value aliases (e.g., 'value' AS alias) which regular IN doesn't.
3929 while (true) {
3930 ExpressionNode inColumnExpr;
3931 try {
3932 // Stop at top-level IN operator to handle values list with alias support
3933 expressionParser.setStopOnTopINOperator(true);
3934 inColumnExpr = expr(lexer, model, sqlParserCallback);
3935 } finally {
3936 expressionParser.setStopOnTopINOperator(false);
3937 }
3938 if (inColumnExpr == null) {
3939 throw SqlException.$(lexer.lastTokenPosition(), "expected IN expression");
3940 }
3941 if (hasGroupByFunc(sqlNodeStack, functionFactoryCache, inColumnExpr)) {
3942 throw SqlException.$(inColumnExpr.position, "aggregate functions are not supported in PIVOT FOR expressions");
3943 }
3944
3945 expectTok(lexer, "in");

Callers 1

parseFromClauseMethod · 0.95

Calls 15

expectTokMethod · 0.95
optTokMethod · 0.95
getAliasMethod · 0.95
getAstMethod · 0.95
createExprColumnAliasMethod · 0.95
toImmutableMethod · 0.95
setAliasMethod · 0.95
$Method · 0.95
exprMethod · 0.95
tokMethod · 0.95
setSelectSubqueryExprMethod · 0.95

Tested by

no test coverage detected