{"record":{"id":"7b4251d8041de75d","repo":"prestodb/presto","slug":"unsupported-join-criteria","errorCode":null,"errorMessage":"Unsupported join criteria","messagePattern":"Unsupported join criteria","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java","lineNumber":1651,"sourceCode":"            right = (Relation) visit(context.right);\n            return new Join(getLocation(context), Join.Type.CROSS, left, right, Optional.empty());\n        }\n\n        JoinCriteria criteria;\n        if (context.NATURAL() != null) {\n            right = (Relation) visit(context.right);\n            criteria = new NaturalJoin();\n        }\n        else {\n            right = (Relation) visit(context.rightRelation);\n            if (context.joinCriteria().ON() != null) {\n                criteria = new JoinOn((Expression) visit(context.joinCriteria().booleanExpression()));\n            }\n            else if (context.joinCriteria().USING() != null) {\n                criteria = new JoinUsing(visit(context.joinCriteria().identifier(), Identifier.class));\n            }\n            else {\n                throw new IllegalArgumentException(\"Unsupported join criteria\");\n            }\n        }\n\n        Join.Type joinType;\n        if (context.joinType().LEFT() != null) {\n            joinType = Join.Type.LEFT;\n        }\n        else if (context.joinType().RIGHT() != null) {\n            joinType = Join.Type.RIGHT;\n        }\n        else if (context.joinType().FULL() != null) {\n            joinType = Join.Type.FULL;\n        }\n        else {\n            joinType = Join.Type.INNER;\n        }\n\n        return new Join(getLocation(context), joinType, left, right, Optional.of(criteria));","sourceCodeStart":1633,"sourceCodeEnd":1669,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java#L1633-L1669","documentation":"AstBuilder.visitJoin throws this IllegalArgumentException when a JOIN clause has a joinCriteria() that is neither ON <booleanExpression> nor USING (...). The parser only recognizes those two criteria forms, so anything else (e.g. a criteria context with all children null, possible with hand-modified parse trees or grammar drift) reaches the else branch.","triggerScenarios":"Parsing a JOIN whose criteria token was altered or removed from the parse tree, or a custom grammar change that introduces a new criteria form (e.g. NATURAL variants) without updating visitJoin.","commonSituations":"Forked grammars, programmatic AST construction via the parser's generated contexts, or SQL dialects using join criteria keywords Presto does not support.","solutions":["Rewrite the join using ON <condition> or USING (col1, col2, ...).","Replace NATURAL JOIN with an explicit ON clause comparing the shared columns.","If building parse trees programmatically, ensure joinCriteria() is one of the grammar's recognized forms.","In a fork, extend visitJoin to handle the new criteria kind."],"exampleFix":"// before\nSELECT * FROM a NATURAL JOIN b;\n// after\nSELECT * FROM a JOIN b ON a.id = b.id;","handlingStrategy":"validation","validationCode":"// ensure every JOIN in the SQL text uses ON or USING\nif (Pattern.compile(\"(?i)\\\\bJOIN\\\\b(?!\\\\s+(?:ON|USING|\\\\())\").matcher(sql).find()) {\n    throw new IllegalArgumentException(\"JOIN must use ON or USING criteria\");\n}","typeGuard":"boolean hasJoinCriteria(String joinClauseSql) {\n    return Pattern.compile(\"(?i)JOIN\\\\s+(\\\\S+\\\\s+)?(ON|USING)\\\\s\").matcher(joinClauseSql).find();\n}","tryCatchPattern":"try {\n    parser.createStatement(sql);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().equals(\"Unsupported join criteria\")) {\n        // rewrite NATURAL/custom joins into explicit ON clauses\n        sql = rewriteNaturalJoins(sql);\n        parser.createStatement(sql);\n    } else throw e;\n}","preventionTips":["Avoid NATURAL JOIN; always write explicit ON/USING.","Keep custom grammar changes and visitJoin in sync.","Lint generated SQL for join criteria before parsing."],"tags":["sql-parser","join","illegal-argument"],"backgroundTag":"unsupported-sql-syntax","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"}