{"record":{"id":"2c0e1e5dd6cc0106","repo":"apache/druid","slug":"pattern-must-be-a-string-literal","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/RegexpExtractExprMacro.java","lineNumber":53,"sourceCode":"  private static final String FN_NAME = \"regexp_extract\";\n\n  @Override\n  public String name()\n  {\n    return FN_NAME;\n  }\n\n  @Override\n  public Expr apply(final List<Expr> args)\n  {\n    validationHelperCheckAnyOfArgumentCount(args, 2, 3);\n\n    final Expr arg = args.get(0);\n    final Expr patternExpr = args.get(1);\n    final Expr indexExpr = args.size() > 2 ? args.get(2) : null;\n\n    if (!ExprUtils.isStringLiteral(patternExpr)) {\n      throw validationFailed(\"pattern must be a string literal\");\n    }\n\n    if (indexExpr != null && (!indexExpr.isLiteral() || !(indexExpr.getLiteralValue() instanceof Number))) {\n      throw validationFailed(\"index must be a numeric literal\");\n    }\n\n    // Precompile the pattern.\n    final Pattern pattern = RegexpExprUtils.compilePattern((String) patternExpr.getLiteralValue(), FN_NAME);\n\n    final int index = indexExpr == null ? 0 : ((Number) indexExpr.getLiteralValue()).intValue();\n\n    class RegexpExtractExpr extends ExprMacroTable.BaseScalarMacroFunctionExpr\n    {\n      private RegexpExtractExpr(List<Expr> args)\n      {\n        super(RegexpExtractExprMacro.this, args);\n      }\n","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/expression/RegexpExtractExprMacro.java#L35-L71","documentation":"The regexp_extract expression macro requires its pattern argument (second argument) to be a compile-time string literal, because it precompiles the regex when the expression is parsed. A non-literal pattern (column, arithmetic expression, or non-string literal) cannot be precompiled, so validation fails.","triggerScenarios":"Calling REGEXP_EXTRACT(expr, pattern) where pattern is a column reference, a concatenation expression, a number, or NULL instead of a quoted string literal.","commonSituations":"Passing a regex stored in a table column; forgetting quotes around the pattern so it parses as an identifier; generating expressions dynamically with non-literal patterns.","solutions":["Inline the regex as a quoted string literal, e.g. REGEXP_EXTRACT(x, '([a-z]+)')","Move variable patterns into a different construct (e.g. LOOKUP or application-side matching)","Verify the literal is a string, not a number or NULL"],"exampleFix":"// before\nREGEXP_EXTRACT(col, pattern_column)\n// after\nREGEXP_EXTRACT(col, '([0-9]+)')","handlingStrategy":"validation","validationCode":"if (!(patternExpr instanceof LiteralExpr) || !(((LiteralExpr) patternExpr).getValue() instanceof String)) {\n  throw new IllegalArgumentException(\"pattern must be a string literal\");\n}","typeGuard":"static boolean isStringLiteral(Expr e) {\n  return ExprUtils.isStringLiteral(e);\n}","tryCatchPattern":"try {\n  return macro.apply(args);\n} catch (ExpressionValidationException e) {\n  return ExprEval.of(null);\n}","preventionTips":["Always quote regex patterns in native expressions","Never reference columns for the pattern argument","Validate expressions in a dry-run parse before executing queries"],"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"}