{"record":{"id":"3adcd3c5705ab9b0","repo":"apache/druid","slug":"pattern-must-be-a-string-literal-3adcd3","errorCode":null,"errorMessage":"pattern must be a string literal","messagePattern":"pattern must be a string literal","errorType":"validation","errorClass":"DruidException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/expression/RegexpReplaceExprMacro.java","lineNumber":88,"sourceCode":"  /**\n   * Expr when pattern and replacement are literals.\n   */\n  class RegexpReplaceExpr extends BaseRegexpReplaceExpr\n  {\n    private final Expr arg;\n    private final Pattern pattern;\n    private final String replacement;\n\n    private RegexpReplaceExpr(List<Expr> args)\n    {\n      super(args);\n\n      final Expr patternExpr = args.get(1);\n      final Expr replacementExpr = args.get(2);\n\n      if (!ExprUtils.isStringLiteral(patternExpr)\n          && !(patternExpr.isLiteral() && patternExpr.getLiteralValue() == null)) {\n        throw validationFailed(\"pattern must be a string literal\");\n      }\n\n      if (!ExprUtils.isStringLiteral(replacementExpr)\n          && !(replacementExpr.isLiteral() && replacementExpr.getLiteralValue() == null)) {\n        throw validationFailed(\"replacement must be a string literal\");\n      }\n\n      final String patternString = (String) patternExpr.getLiteralValue();\n\n      this.arg = args.get(0);\n      this.pattern = patternString != null ? RegexpExprUtils.compilePattern(patternString, FN_NAME) : null;\n      this.replacement = (String) replacementExpr.getLiteralValue();\n    }\n\n    @Nonnull\n    @Override\n    public ExprEval<?> eval(final ObjectBinding bindings)\n    {","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/expression/RegexpReplaceExprMacro.java#L70-L106","documentation":"regexp_replace's RegexpReplaceExpr constructor requires the pattern argument to be a string literal (or an explicit literal NULL, which is tolerated and disables matching). Any other pattern expression fails validation because patterns are precompiled at parse time.","triggerScenarios":"REGEXP_REPLACE(expr, pattern, replacement) where pattern is a column reference, computed expression, or non-string non-null literal.","commonSituations":"Dynamic regex patterns sourced from data; forgetting quotes so the pattern parses as an identifier; generated queries with concatenated pattern fields.","solutions":["Provide the pattern as a quoted string literal","If a NULL pattern is intended, make it an explicit literal NULL (this is accepted)","Restructure the query so pattern matching happens outside the expression"],"exampleFix":"// before\nREGEXP_REPLACE(col, pat_col, 'X')\n// after\nREGEXP_REPLACE(col, '[aeiou]', 'X')","handlingStrategy":"validation","validationCode":"boolean ok = ExprUtils.isStringLiteral(patternExpr)\n    || (patternExpr.isLiteral() && patternExpr.getLiteralValue() == null);\nif (!ok) {\n  throw new IllegalArgumentException(\"pattern must be a string literal\");\n}","typeGuard":"static boolean isStringOrNullLiteral(Expr e) {\n  return ExprUtils.isStringLiteral(e) || (e.isLiteral() && e.getLiteralValue() == null);\n}","tryCatchPattern":"try {\n  return macro.apply(args);\n} catch (ExpressionValidationException e) {\n  log.error(\"regexp_replace pattern invalid: %s\", e.getMessage());\n  throw e;\n}","preventionTips":["Inline patterns as quoted strings","Only use literal NULL when null-pattern semantics are intended","Dry-run parse expressions before query execution"],"tags":["druid","regex","literal"],"backgroundTag":"invalid-regex-pattern","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"}