prestodb/presto · error · SemanticException
INVALID_PARAMETER_USAGE
INVALID_PARAMETER_USAGE
Error message
Subscript expression on ROW requires a constant index
What it means
ROW subscript (rowExpr[i]) is resolved by hand in the analyzer rather than via a dedicated operator, so the index must be a constant LongLiteral. A dynamic index expression (column, function call, arithmetic) cannot be used and triggers INVALID_PARAMETER_USAGE.
Source
Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java:808
}
private Type getVarcharType(Expression value, StackableAstVisitorContext<Context> context)
{
Type type = process(value, context);
if (!(type instanceof VarcharType)) {
return VARCHAR;
}
return type;
}
@Override
protected Type visitSubscriptExpression(SubscriptExpression node, StackableAstVisitorContext<Context> context)
{
Type baseType = process(node.getBase(), context);
// Subscript on Row hasn't got a dedicated operator. Its Type is resolved by hand.
if (baseType instanceof RowType) {
if (!(node.getIndex() instanceof LongLiteral)) {
throw new SemanticException(
INVALID_PARAMETER_USAGE,
node.getIndex(),
"Subscript expression on ROW requires a constant index");
}
Type indexType = process(node.getIndex(), context);
if (!indexType.equals(INTEGER)) {
throw new SemanticException(
TYPE_MISMATCH,
node.getIndex(),
"Subscript expression on ROW requires integer index, found %s", indexType);
}
int indexValue = toIntExact(((LongLiteral) node.getIndex()).getValue());
if (indexValue <= 0) {
throw new SemanticException(
INVALID_PARAMETER_USAGE,
node.getIndex(),
"Invalid subscript index: %s. ROW indices start at 1", indexValue);
}View on GitHub (pinned to 55bb57d202)
Solutions
- Use a literal integer index, e.g. my_row[1]
- If dynamic access is needed, convert the ROW to a map or use a CASE expression over literal indices
- Extract the field in application code instead of SQL
Example fix
// before SELECT my_row[idx] FROM t // after SELECT CASE idx WHEN 1 THEN my_row[1] WHEN 2 THEN my_row[2] END FROM t
Defensive patterns
Strategy: validation
Validate before calling
if (isRowType(baseType) && !(indexExpr instanceof LongLiteral)) {
throw new IllegalArgumentException("ROW subscript requires a constant integer literal");
} Type guard
boolean isConstantRowIndex(Expression index) { return index instanceof LongLiteral; } Try / catch
try {
return execute(sql);
} catch (SemanticException e) {
if (e.getCode() == INVALID_PARAMETER_USAGE && e.getMessage().contains("requires a constant index")) {
// rewrite to CASE over literal indices or extract in app code
} else throw e;
} Prevention
- Only use literal integer indices on ROW values
- Convert ROWs to MAPs when dynamic key access is required
- Do dynamic field selection in application code, not SQL
- Lint generated SQL for non-literal subscripts on ROW-typed columns
When it happens
Trigger: my_row[expr] where expr is not a literal, e.g. my_row[idx_col], my_row[1+1] as a non-literal AST node, or a parameterized index.
Common situations: Trying to iterate ROW fields with a loop variable, generated SQL using dynamic indices, porting array-style dynamic subscripting from other engines.
Related errors
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/9569adb2029a13ee.
Report an issue: GitHub.