{"record":{"id":"fcad2a45c37ffca0","repo":"apache/iceberg","slug":"cannot-convert-predicate-to-sql","errorCode":null,"errorMessage":"Cannot convert predicate to SQL: ","messagePattern":"Cannot convert predicate to SQL: ","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java","lineNumber":686,"sourceCode":"          return sqlString(pred.term()) + \" <= \" + sqlString(pred.literal());\n        case GT:\n          return sqlString(pred.term()) + \" > \" + sqlString(pred.literal());\n        case GT_EQ:\n          return sqlString(pred.term()) + \" >= \" + sqlString(pred.literal());\n        case EQ:\n          return sqlString(pred.term()) + \" = \" + sqlString(pred.literal());\n        case NOT_EQ:\n          return sqlString(pred.term()) + \" != \" + sqlString(pred.literal());\n        case STARTS_WITH:\n          return sqlString(pred.term()) + \" LIKE '\" + pred.literal().value() + \"%'\";\n        case NOT_STARTS_WITH:\n          return sqlString(pred.term()) + \" NOT LIKE '\" + pred.literal().value() + \"%'\";\n        case IN:\n          return sqlString(pred.term()) + \" IN (\" + sqlString(pred.literals()) + \")\";\n        case NOT_IN:\n          return sqlString(pred.term()) + \" NOT IN (\" + sqlString(pred.literals()) + \")\";\n        default:\n          throw new UnsupportedOperationException(\"Cannot convert predicate to SQL: \" + pred);\n      }\n    }\n\n    private static <T> String sqlString(UnboundTerm<T> term) {\n      if (term instanceof org.apache.iceberg.expressions.NamedReference) {\n        return term.ref().name();\n      } else if (term instanceof UnboundTransform) {\n        UnboundTransform<?, ?> transform = (UnboundTransform<?, ?>) term;\n        return transform.transform().toString() + \"(\" + transform.ref().name() + \")\";\n      } else {\n        throw new UnsupportedOperationException(\"Cannot convert term to SQL: \" + term);\n      }\n    }\n\n    private static <T> String sqlString(List<org.apache.iceberg.expressions.Literal<T>> literals) {\n      return literals.stream()\n          .map(DescribeExpressionVisitor::sqlString)\n          .collect(Collectors.joining(\", \"));","sourceCodeStart":668,"sourceCodeEnd":704,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java#L668-L704","documentation":"The unbound predicate branch of DescribeExpressionVisitor renders Iceberg predicates as SQL strings for DESCRIBE output. It supports is-null/not-null, eq/neq/lt/lteq/gt/gteq, starts-with/not-starts-with, in/not-in; other operators (e.g. not, count*, equality on terms it can't render) fall to default and throw UnsupportedOperationException.","triggerScenarios":"DESCRIBE/EXPLAIN-style SQL rendering of an Iceberg predicate using an operator outside the supported switch, such as predicates containing NOT wrappers reaching this path or new operator types added in later Iceberg specs.","commonSituations":"Rendering filters with less-common operators through Spark3Util's describe helpers; schema evolution or version skew where newer predicates are passed to an older converter.","solutions":["Rewrite the filter using supported operators (e.g. replace NOT(x) with its negated comparison)","Handle unsupported operators in your own rendering instead of DescribeExpressionVisitor","Upgrade Iceberg Spark module if the operator should be supported in newer versions"],"exampleFix":"// before\nExpressions.not(Expressions.equal(\"a\", 1)) // hits unsupported default in some paths\n// after\nExpressions.notEqual(\"a\", 1)","handlingStrategy":"try-catch","validationCode":"Set<Expression.Operation> supported = Set.of(IS_NULL, NOT_NULL, EQ, NOT_EQ, LT, LT_EQ,\n    GT, GT_EQ, STARTS_WITH, NOT_STARTS_WITH, IN, NOT_IN);\nif (!supported.contains(pred.op())) throw new IllegalArgumentException(\"Unsupported op: \" + pred.op());","typeGuard":"boolean isRenderablePredicate(UnboundPredicate<?> p) {\n  return switch (p.op()) {\n    case IS_NULL, NOT_NULL, EQ, NOT_EQ, LT, LT_EQ, GT, GT_EQ,\n         STARTS_WITH, NOT_STARTS_WITH, IN, NOT_IN -> true;\n    default -> false;\n  };\n}","tryCatchPattern":"try {\n  String sql = visitor.predicate(pred);\n} catch (UnsupportedOperationException e) {\n  return pred.toString(); // fall back to Iceberg expression string\n}","preventionTips":["Compose filters from supported comparison/set operators only","Avoid NOT-wrapped predicates when rendering through the describe visitor","Version-match the connector with the expression spec in use"],"tags":["spark","predicate","sql-rendering"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}