{"record":{"id":"30af37ea3e01a321","repo":"prestodb/presto","slug":"original-function-call-and-substitute-must-both-be","errorCode":null,"errorMessage":"Original function call and substitute must both be specified, %s.","messagePattern":"Original function call and substitute must both be specified, (.+?)\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"presto-verifier/src/main/java/com/facebook/presto/verifier/rewrite/FunctionCallRewriter.java","lineNumber":98,"sourceCode":"            return Optional.empty();\n        }\n        checkState(typeManager instanceof FunctionAndTypeManager, \"FunctionAndTypeManager is required for FunctionCallRewriter.\");\n        return Optional.of(new FunctionCallRewriter(functionCallSubstitutes, (FunctionAndTypeManager) typeManager));\n    }\n\n    public static Multimap<String, FunctionCallSubstitute> validateAndConstructFunctionCallSubstituteMap(String functionCallSubstitutes)\n    {\n        ImmutableMultimap.Builder<String, FunctionCallSubstitute> map = ImmutableMultimap.builder();\n        if (functionCallSubstitutes == null) {\n            return map.build();\n        }\n\n        Splitter commaSplitter = Splitter.on(\"/,/\").omitEmptyStrings().trimResults();\n        Splitter slashSplitter = Splitter.on('/').omitEmptyStrings().trimResults();\n        for (String substitute : commaSplitter.split(functionCallSubstitutes)) {\n            List<String> specs = slashSplitter.splitToList(substitute);\n            if (specs.size() != 2) {\n                throw new IllegalArgumentException(String.format(\"Original function call and substitute must both be specified, %s.\", substitute));\n            }\n            Expression originalExpression = parseOriginalFunctionCall(specs.get(0));\n            Expression substituteExpression = parseSubstituteExpression(specs.get(1));\n\n            if (originalExpression instanceof FunctionCall) {\n                FunctionCall originalFunction = (FunctionCall) originalExpression;\n                map.put(originalFunction.getName().getSuffix(), new FunctionCallSubstitute(originalExpression, substituteExpression));\n            }\n            else if (originalExpression instanceof CurrentTime) {\n                CurrentTime originalFunction = (CurrentTime) originalExpression;\n                map.put(originalFunction.getFunction().getName(), new FunctionCallSubstitute(originalExpression, substituteExpression));\n            }\n        }\n\n        return map.build();\n    }\n\n    public RewriterResult rewrite(Statement root)","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-verifier/src/main/java/com/facebook/presto/verifier/rewrite/FunctionCallRewriter.java#L80-L116","documentation":"The Verifier's function-call substitution config (function-call-substitutes) expects each substitute entry to be a spec of exactly two parts: the original function call and the replacement expression, separated by '/'. When an entry does not split into exactly two slash-delimited parts, validateAndConstructFunctionCallSubstituteMap throws this IllegalArgumentException. It is a fail-fast config validation error.","triggerScenarios":"Passing a functionCallSubstitutes config string where any comma-separated entry contains zero or more than one '/' separator, e.g. 'myfunc(x)' (no substitute) or 'myfunc(x)/abs(x)/extra' (three parts). Note empty segments are omitted, so a double slash yields one part and also fails.","commonSituations":"Typos in the verifier config properties file; forgetting the second half of a substitute; using a literal '/' inside a function argument without realizing it is the spec separator; copy-pasting entries that contain multiple slashes.","solutions":["Fix the config entry so it has exactly one '/' separating original and substitute, e.g. 'original_fn(x)/replacement_fn(x)'","Escape or remove any literal '/' characters appearing inside function arguments in the spec","Split complex entries: each comma-separated element must contain exactly one pair; move extra mappings to their own entry"],"exampleFix":"// before (config value)\nfunctionCallSubstitutes = myfunc(x)\n// after (config value)\nfunctionCallSubstitutes = myfunc(x)/myfunc_new(x)","handlingStrategy":"validation","validationCode":"// Validate each comma-separated entry has exactly one '/' before configuring\nGuavaSplitter.on(\",\").omitEmptyStrings().trimResults().split(configValue).forEach(entry -> {\n    long slashes = entry.chars().filter(c -> c == '/').count();\n    if (slashes != 1) {\n        throw new IllegalArgumentException(\"Entry must be 'original/substitute': \" + entry);\n    }\n});","typeGuard":"boolean isValidSubstituteSpec(String entry) {\n    return entry != null && entry.indexOf('/') == entry.lastIndexOf('/') && entry.indexOf('/') >= 0;\n}","tryCatchPattern":"try {\n    rewriter = FunctionCallRewriter.getInstance(functionCallSubstitutes, typeManager);\n} catch (IllegalArgumentException e) {\n    LOG.error(\"Bad function-call-substitutes config, skipping rewrites: %s\", e.getMessage());\n    rewriter = Optional.empty();\n}","preventionTips":["Keep function arguments free of '/' or document how the spec separator collides","Lint verifier config entries for exactly one '/' per entry at startup","Keep each original/substitute pair as its own comma-separated entry"],"tags":["config","validation","illegal-argument","verifier"],"backgroundTag":"invalid-config-format","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"}