{"record":{"id":"38f4105d70a7182a","repo":"apache/druid","slug":"cannot-join-lookup-with-non-equi-condition-s","errorCode":null,"errorMessage":"Cannot join lookup with non-equi condition: %s","messagePattern":"Cannot join lookup with non-equi condition: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinMatcher.java","lineNumber":194,"sourceCode":"      );\n    }\n  }\n\n  public static LookupJoinMatcher create(\n      LookupExtractor extractor,\n      ColumnSelectorFactory leftSelectorFactory,\n      JoinConditionAnalysis condition,\n      boolean remainderNeeded\n  )\n  {\n    final List<Expr> keyExprs;\n\n    if (condition.isAlwaysTrue()) {\n      keyExprs = null;\n    } else if (condition.isAlwaysFalse()) {\n      keyExprs = null;\n    } else if (!condition.getNonEquiConditions().isEmpty()) {\n      throw new IAE(\"Cannot join lookup with non-equi condition: %s\", condition);\n    } else if (!condition.getRightEquiConditionKeys()\n                         .stream()\n                         .allMatch(LookupColumnSelectorFactory.KEY_COLUMN::equals)) {\n      throw new IAE(\"Cannot join lookup with condition referring to non-key column: %s\", condition);\n    } else {\n      keyExprs = condition.getEquiConditions().stream().map(Equality::getLeftExpr).collect(Collectors.toList());\n    }\n\n    return new LookupJoinMatcher(extractor, leftSelectorFactory, condition, keyExprs, remainderNeeded);\n  }\n\n  @Override\n  public ColumnSelectorFactory getColumnSelectorFactory()\n  {\n    return selectorFactory;\n  }\n\n  @Override","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinMatcher.java#L176-L212","documentation":"Lookup joins only support equi-joins on the lookup's single key column. LookupJoinMatcher.create rejects a JoinCondition that contains any non-equi (inequality) condition, because a hash-based lookup cannot evaluate arbitrary predicates.","triggerScenarios":"Creating a LookupJoinMatcher with a join condition containing non-equi clauses, e.g. ON l.k = t.k AND t.ts > l.start, when joining a lookup table.","commonSituations":"SQL with an ON clause mixing equality and range predicates against a lookup; hand-written native join clauses with extra inequality conditions on a lookup datasource.","solutions":["Move the non-equi condition into the WHERE clause (applied after the join) instead of the ON clause","Restrict the lookup join ON clause to pure equality on the lookup key column","Use a regular table join instead of a lookup if range predicates in ON are required"],"exampleFix":"// before\nSELECT ... FROM t JOIN lookup l ON t.k = l.k AND t.ts >= l.start\n// after\nSELECT ... FROM t JOIN lookup l ON t.k = l.k WHERE t.ts >= l.start","handlingStrategy":"validation","validationCode":"if (!condition.getNonEquiConditions().isEmpty())\n  throw new IllegalArgumentException(\"move non-equi conditions to WHERE for lookup joins: \" + condition);","typeGuard":null,"tryCatchPattern":"try { LookupJoinMatcher.create(...); } catch (IAE e) { if (e.getMessage().startsWith(\"Cannot join lookup with non-equi\")) { /* rewrite query or surface to user */ } }","preventionTips":["Keep lookup join ON clauses to pure equality","Push extra predicates into WHERE clauses","Validate generated SQL for lookup joins in tests"],"tags":["java","join","lookup","sql","druid"],"backgroundTag":"invalid-argument-value","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"}