{"record":{"id":"0770b1cb29b05164","repo":"prestodb/presto","slug":"clickhouse-pushdown-unsupported-expression","errorCode":"CLICKHOUSE_PUSHDOWN_UNSUPPORTED_EXPRESSION","errorMessage":" is not supported in ClickHouse filter","messagePattern":" is not supported in ClickHouse filter","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"warning","filePath":"presto-clickhouse/src/main/java/com/facebook/presto/plugin/clickhouse/optimization/ClickHouseFilterExpressionConverter.java","lineNumber":90,"sourceCode":"            SpecialFormExpression specialForm,\n            boolean isWhitelist,\n            Function<VariableReferenceExpression, Selection> context)\n    {\n        return derived(format(\"(%s %s (%s))\",\n                specialForm.getArguments().get(0).accept(this, context).getDefinition(),\n                isWhitelist ? \"IN\" : \"NOT IN\",\n                specialForm.getArguments().subList(1, specialForm.getArguments().size()).stream()\n                        .map(argument -> argument.accept(this, context).getDefinition())\n                        .collect(Collectors.joining(\", \"))));\n    }\n\n    private ClickHouseColumnExpression handleLogicalBinary(\n            String operator,\n            CallExpression call,\n            Function<VariableReferenceExpression, Selection> context)\n    {\n        if (!LOGICAL_BINARY_OPS_FILTER.contains(operator)) {\n            throw new PrestoException(CLICKHOUSE_PUSHDOWN_UNSUPPORTED_EXPRESSION, operator + \" is not supported in ClickHouse filter\");\n        }\n        List<RowExpression> arguments = call.getArguments();\n        if (arguments.size() == 2) {\n            return derived(format(\n                    \"(%s %s %s)\",\n                    arguments.get(0).accept(this, context).getDefinition(),\n                    operator,\n                    arguments.get(1).accept(this, context).getDefinition()));\n        }\n        throw new PrestoException(CLICKHOUSE_PUSHDOWN_UNSUPPORTED_EXPRESSION, \"Unknown logical binary: \" + call);\n    }\n\n    private ClickHouseColumnExpression handleBetween(\n            CallExpression between,\n            Function<VariableReferenceExpression, Selection> context)\n    {\n        if (between.getArguments().size() == 3) {\n            RowExpression value = between.getArguments().get(0);","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-clickhouse/src/main/java/com/facebook/presto/plugin/clickhouse/optimization/ClickHouseFilterExpressionConverter.java#L72-L108","documentation":"ClickHouseFilterExpressionConverter.handleLogicalBinary() only supports logical binary operators in LOGICAL_BINARY_OPS_FILTER (AND/OR). When the converter visits a logical binary CallExpression with an operator outside that set, it throws CLICKHOUSE_PUSHDOWN_UNSUPPORTED_EXPRESSION telling the optimizer this filter cannot be translated to ClickHouse SQL.","triggerScenarios":"A WHERE clause containing a logical binary expression not in the supported set (e.g. XOR, or an unexpected logical function) that the optimizer attempts to push down to ClickHouse.","commonSituations":"Queries using XOR or exotic logical functions in filters; planner rewriting expressions into logical calls the converter does not recognize; complex boolean filters produced by view expansion.","solutions":["Rewrite the filter using AND/OR (e.g. XOR(a,b) → (a OR b) AND NOT (a AND b))","Restructure the query so the unsupported logical expression is computed after the pushed-down filter (e.g. in an outer query)","Disable filter pushdown for this catalog/query so the predicate is evaluated locally","Upgrade the connector for a wider supported-operator set"],"exampleFix":"// before\nSELECT * FROM ch.db.t WHERE a XOR b;\n// after\nSELECT * FROM ch.db.t WHERE (a OR b) AND NOT (a AND b);","handlingStrategy":"fallback","validationCode":"// Pre-check filter operators before pushdown\nSet<String> supportedLogicalOps = Set.of(\"AND\", \"OR\");\nif (isLogicalBinary(call) && !supportedLogicalOps.contains(operatorOf(call).toUpperCase())) {\n    log.info(\"Operator \" + operatorOf(call) + \" not pushable to ClickHouse; will evaluate locally\");\n}\n","typeGuard":"boolean isClickHousePushableLogicalOp(String operator) {\n    return Set.of(\"AND\", \"OR\").contains(operator.toUpperCase(Locale.ENGLISH));\n}","tryCatchPattern":"try {\n    return expressionConverter.convert(filter);\n} catch (PrestoException e) {\n    if (e.getErrorCode().getCode() == CLICKHOUSE_PUSHDOWN_UNSUPPORTED_EXPRESSION.toErrorCode().getCode()) {\n        return Optional.empty(); // leave the filter unpushed; evaluate locally\n    }\n    throw e;\n}","preventionTips":["Rewrite XOR and exotic boolean functions using AND/OR/NOT","Structure queries so unsupported expressions sit above the pushed-down filter","Keep filter pushdown enabled but rely on graceful local evaluation","Check LOGICAL_BINARY_OPS_FILTER when writing custom logical expressions"],"tags":["filter-pushdown","expression-conversion","unsupported-expression","clickhouse"],"backgroundTag":"pushdown-unsupported-expression","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"}