{"record":{"id":"f2f8f1dbb1e0a497","repo":"prestodb/presto","slug":"invalid-ordinal","errorCode":"INVALID_ORDINAL","errorMessage":"GROUP BY position %s is not in select list","messagePattern":"GROUP BY position (.+?) is not in select list","errorType":"error_code","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java","lineNumber":4383,"sourceCode":"\n        private List<Expression> analyzeGroupBy(QuerySpecification node, Scope scope, List<Expression> outputExpressions)\n        {\n            if (node.getGroupBy().isPresent()) {\n                ImmutableList.Builder<Set<FieldId>> cubes = ImmutableList.builder();\n                ImmutableList.Builder<List<FieldId>> rollups = ImmutableList.builder();\n                ImmutableList.Builder<List<Set<FieldId>>> sets = ImmutableList.builder();\n                ImmutableList.Builder<Expression> complexExpressions = ImmutableList.builder();\n                ImmutableList.Builder<Expression> groupingExpressions = ImmutableList.builder();\n\n                checkGroupingSetsCount(node.getGroupBy().get());\n                for (GroupingElement groupingElement : node.getGroupBy().get().getGroupingElements()) {\n                    if (groupingElement instanceof SimpleGroupBy) {\n                        for (Expression column : groupingElement.getExpressions()) {\n                            // simple GROUP BY expressions allow ordinals or arbitrary expressions\n                            if (column instanceof LongLiteral) {\n                                long ordinal = ((LongLiteral) column).getValue();\n                                if (ordinal < 1 || ordinal > outputExpressions.size()) {\n                                    throw new SemanticException(INVALID_ORDINAL, column, \"GROUP BY position %s is not in select list\", ordinal);\n                                }\n\n                                column = outputExpressions.get(toIntExact(ordinal - 1));\n                            }\n                            else {\n                                analyzeExpression(column, scope);\n                            }\n\n                            if (analysis.getColumnReferenceFields().containsKey(NodeRef.of(column))) {\n                                sets.add(ImmutableList.of(ImmutableSet.copyOf(analysis.getColumnReferenceFields().get(NodeRef.of(column)))));\n                            }\n                            else {\n                                verifyNoAggregateWindowOrGroupingFunctions(analysis.getFunctionHandles(), functionAndTypeResolver, column, \"GROUP BY clause\");\n                                analysis.recordSubqueries(node, analyzeExpression(column, scope));\n                                complexExpressions.add(column);\n                            }\n\n                            groupingExpressions.add(column);","sourceCodeStart":4365,"sourceCodeEnd":4401,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java#L4365-L4401","documentation":"When GROUP BY uses an ordinal (a GROUP BY 2 style reference), Presto validates the literal is within 1..N where N is the number of select-list expressions. An ordinal of 0, negative, or larger than the select list throws INVALID_ORDINAL. Ordinals must point at an existing SELECT output.","triggerScenarios":"GROUP BY 0, GROUP BY with a literal greater than the number of SELECT items (e.g. three select columns with GROUP BY 4), or programmatic SQL generation emitting an out-of-range ordinal.","commonSituations":"Hand-written or generated SQL where SELECT columns were added/removed without updating GROUP BY ordinals; confusion over 0-based vs 1-based indexing; ORMs reordering the projection.","solutions":["Change the ordinal to a value between 1 and the number of SELECT columns.","Reference the column by name or expression instead of an ordinal.","If SQL is generated, validate ordinal values against the projection list before emitting the query."],"exampleFix":"// before\nSELECT a, b, count(*) FROM t GROUP BY 3;\n// after\nSELECT a, b, count(*) FROM t GROUP BY 1, 2;","handlingStrategy":"validation","validationCode":"long selectCount = selectItems.size();\nfor (Expression g : groupByExpressions) {\n    if (g instanceof LongLiteral) {\n        long ordinal = ((LongLiteral) g).getValue();\n        if (ordinal < 1 || ordinal > selectCount) {\n            throw new IllegalArgumentException(\"GROUP BY ordinal \" + ordinal + \" out of range 1..\" + selectCount);\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try { execute(sql); } catch (SemanticException e) { if (e.getCode() == INVALID_ORDINAL) { /* regenerate SQL with corrected ordinals or names */ } else { throw e; } }","preventionTips":["Prefer column names over ordinals in GROUP BY.","Keep SELECT list and GROUP BY ordinals in sync when editing queries.","Remember ordinals are 1-based in Presto."],"tags":["sql","presto","group-by","ordinal"],"backgroundTag":"invalid-ordinal-reference","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}