{"record":{"id":"047deb3541793c3e","repo":"apache/druid","slug":"cannot-handle-non-equijoin-condition-s","errorCode":null,"errorMessage":"Cannot handle non-equijoin condition: %s","messagePattern":"Cannot handle non-equijoin condition: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/querykit/common/SortMergeJoinStageProcessor.java","lineNumber":271,"sourceCode":"    return retVal.toArray(new int[0]);\n  }\n\n  /**\n   * Validates that a join condition can be handled by this processor. Returns the condition if it can be handled.\n   * Throws {@link IllegalArgumentException} if the condition cannot be handled.\n   */\n  public static JoinConditionAnalysis validateCondition(final JoinConditionAnalysis condition)\n  {\n    if (condition.isAlwaysTrue()) {\n      return condition;\n    }\n\n    if (condition.isAlwaysFalse()) {\n      throw new IAE(\"Cannot handle constant condition: %s\", condition.getOriginalExpression());\n    }\n\n    if (condition.getNonEquiConditions().size() > 0) {\n      throw new IAE(\"Cannot handle non-equijoin condition: %s\", condition.getOriginalExpression());\n    }\n\n    if (condition.getEquiConditions().stream().anyMatch(c -> !c.getLeftExpr().isIdentifier())) {\n      throw new IAE(\n          \"Cannot handle equality condition involving left-hand expression: %s\",\n          condition.getOriginalExpression()\n      );\n    }\n\n    return condition;\n  }\n\n  /**\n   * Validates that all signatures from {@link #collectAndReadPartitions(ExecutionContext)} are prefixed by the\n   * provided {@code keyColumns}.\n   */\n  private static Int2ObjectMap<List<ReadableInput>> validateInputFrameSignatures(\n      final Int2ObjectMap<List<ReadableInput>> inputsByPartition,","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/querykit/common/SortMergeJoinStageProcessor.java#L253-L289","documentation":"The MSQ sort-merge join stage only supports pure equi-joins where the left side of each equality is a simple column identifier. validateCondition throws this IllegalArgumentException when the join condition contains any non-equi predicate (e.g. t1.a > t2.b, LIKE, OR of inequalities), which the shuffle-based merge join implementation cannot evaluate.","triggerScenarios":"Running a query via the MSQ/dsql engine whose JOIN ... ON clause includes an inequality, range, LIKE, IS NULL, or other non-equality predicate, or mixes equi and non-equi conditions (e.g. ON a.id = b.id AND a.ts > b.ts).","commonSituations":"Porting SQL written for traditional databases (Postgres/MySQL) that freely uses non-equijoin conditions to Druid MSQ; adding time-range or fuzzy-match predicates to a join ON clause; hand-written native join queries with mixed conditions.","solutions":["Move the non-equi predicate out of the JOIN ON clause into a WHERE clause applied after the join.","Rewrite the condition as a pure equality join and filter rows afterwards in a separate WHERE or HAVING stage.","Use a different join strategy that supports non-equi conditions (e.g. broadcast/join via the native engine instead of MSQ sort-merge join).","Ensure equi-join left expressions are plain column identifiers, not expressions (see related error)."],"exampleFix":"// before\nSELECT ... FROM t1 JOIN t2 ON t1.id = t2.id AND t1.ts > t2.ts\n// after\nSELECT ... FROM t1 JOIN t2 ON t1.id = t2.id WHERE t1.ts > t2.ts","handlingStrategy":"validation","validationCode":"// Client-side check before submitting an MSQ join\nfunction validateJoinCondition(onClause) {\n  const hasNonEqui = onClause.predicates.some(p => !p.isEquality);\n  if (hasNonEqui) throw new Error('MSQ sort-merge join requires pure equi-join ON conditions');\n  const nonIdentifierLeft = onClause.predicates.some(p => p.isEquality && !isColumnRef(p.left));\n  if (nonIdentifierLeft) throw new Error('MSQ equi-join left side must be a plain column');\n}","typeGuard":"function isPlainColumnRef(expr) { return expr.type === 'identifier' || expr.type === 'column'; }","tryCatchPattern":null,"preventionTips":["Keep join ON clauses limited to simple column equality.","Filter non-join predicates in WHERE, not ON.","Avoid functions or arithmetic on join keys in MSQ queries.","Test migrated SQL against MSQ limitations before production use."],"tags":["sql","join","msq"],"backgroundTag":"unsupported-operation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}