Skip to content

Commit e085f81

Browse files
committed
Rewrite IN / NOT IN with tuple inputs to row(...tuple) to conform to correct SQL syntax (1867/2027)
Signed-off-by: Yuanchun Shen <yuanchu@amazon.com>
1 parent 1530d68 commit e085f81

1 file changed

Lines changed: 24 additions & 0 deletions

File tree

core/src/main/java/org/opensearch/sql/executor/QueryService.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,30 @@ public SqlNode visit(SqlIdentifier id) {
361361
List.of(SqlIdentifier.STAR),
362362
call.getParserPosition(),
363363
call.getFunctionQuantifier());
364+
} else if (call.getKind() == SqlKind.IN || call.getKind() == SqlKind.NOT_IN) {
365+
// Fix for tuple IN / NOT IN queries: Convert SqlNodeList to ROW SqlCall
366+
//
367+
// When RelToSqlConverter converts a tuple expression like (id, name) back to
368+
// SqlNode, it generates a bare SqlNodeList instead of wrapping it in a ROW
369+
// operator. This causes validation to fail because:
370+
// 1. SqlValidator.deriveType() doesn't know how to handle SqlNodeList
371+
// 2. SqlToRelConverter.visit(SqlNodeList) throws UnsupportedOperationException
372+
//
373+
// For example, the query:
374+
// WHERE (id, name) NOT IN (SELECT uid, name FROM ...)
375+
//
376+
// After Rel-to-SQL conversion becomes:
377+
// IN operator with operands: [SqlNodeList[id, name], SqlSelect[...]]
378+
//
379+
// But it should be:
380+
// IN operator with operands: [ROW(id, name), SqlSelect[...]]
381+
//
382+
// This fix wraps the SqlNodeList in a ROW SqlCall before validation,
383+
// ensuring proper type derivation and subsequent SQL-to-Rel conversion.
384+
if (!call.getOperandList().isEmpty()
385+
&& call.getOperandList().get(0) instanceof SqlNodeList nodes) {
386+
call.setOperand(0, SqlStdOperatorTable.ROW.createCall(nodes));
387+
}
364388
}
365389
return super.visit(call);
366390
}

0 commit comments

Comments
 (0)