{"record":{"id":"133914f5caeec077","repo":"prestodb/presto","slug":"duplicate-null-call-clause-s","errorCode":null,"errorMessage":"Duplicate null-call clause: %s","messagePattern":"Duplicate null-call clause: (.+?)","errorType":"exception","errorClass":"ParsingException","httpStatus":null,"severity":"error","filePath":"presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java","lineNumber":3081,"sourceCode":"                if (language != null) {\n                    throw new ParsingException(format(\"Duplicate language clause: %s\", characteristic.language().getText()), getLocation(characteristic.language()));\n                }\n                if (characteristic.language().SQL() != null) {\n                    language = Language.SQL;\n                }\n                else {\n                    language = new Language(((Identifier) visit(characteristic.language().identifier())).getValue());\n                }\n            }\n            else if (characteristic.determinism() != null) {\n                if (determinism != null) {\n                    throw new ParsingException(format(\"Duplicate determinism characteristics: %s\", characteristic.determinism().getText()), getLocation(characteristic.determinism()));\n                }\n                determinism = characteristic.determinism().NOT() == null ? DETERMINISTIC : NOT_DETERMINISTIC;\n            }\n            else if (characteristic.nullCallClause() != null) {\n                if (nullCallClause != null) {\n                    throw new ParsingException(format(\"Duplicate null-call clause: %s\", characteristic.nullCallClause().getText()), getLocation(characteristic.nullCallClause()));\n                }\n                nullCallClause = characteristic.nullCallClause().CALLED() != null ? CALLED_ON_NULL_INPUT : RETURNS_NULL_ON_NULL_INPUT;\n            }\n            else {\n                throw new IllegalArgumentException(format(\"Unsupported RoutineCharacteristic: %s\", characteristic.getText()));\n            }\n        }\n\n        return new RoutineCharacteristics(\n                Optional.ofNullable(language),\n                Optional.ofNullable(determinism),\n                Optional.ofNullable(nullCallClause));\n    }\n\n    private AlterRoutineCharacteristics getAlterRoutineCharacteristics(SqlBaseParser.AlterRoutineCharacteristicsContext context)\n    {\n        if (context.alterRoutineCharacteristic().isEmpty()) {\n            throw new ParsingException(\"No alter routine characteristics specified\");","sourceCodeStart":3063,"sourceCodeEnd":3099,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java#L3063-L3099","documentation":"Duplicate-clause guard in the routine-characteristics loop of AstBuilder: a CREATE FUNCTION statement specifies the NULL ON INPUT / RETURNS NULL ON NULL INPUT / CALLED ON NULL INPUT clause more than once. The second null-call clause triggers a ParsingException at its location.","triggerScenarios":"Parsing a CREATE FUNCTION whose routine_characteristic list contains two null-call clauses (e.g. 'RETURNS NULL ON NULL INPUT CALLED ON NULL INPUT' or the same clause twice).","commonSituations":"Generated function DDL that emits a default null-call clause plus a user-specified one, or concatenated SQL fragments each carrying the clause.","solutions":["Emit only one CALLED ON NULL INPUT or RETURNS NULL ON NULL INPUT clause.","Deduplicate characteristics when composing function DDL.","Pre-validate the characteristic list before parsing.","Fix DDL generation to replace, not append, the null-call setting."],"exampleFix":"// before\nCREATE FUNCTION f() RETURNS int CALLED ON NULL INPUT RETURNS NULL ON NULL INPUT RETURN 1;\n// after\nCREATE FUNCTION f() RETURNS int RETURNS NULL ON NULL INPUT RETURN 1;","handlingStrategy":"validation","validationCode":"int nullCallClauses = countMatches(sql, \"(?i)\\\\b(CALLED\\\\s+ON\\\\s+NULL\\\\s+INPUT|RETURNS\\\\s+NULL\\\\s+ON\\\\s+NULL\\\\s+INPUT)\\\\b\");\nif (nullCallClauses > 1) {\n    throw new IllegalArgumentException(\"Only one null-call clause allowed\");\n}","typeGuard":"boolean hasSingleNullCallClause(String sql) {\n    Matcher m = Pattern.compile(\"(?i)(CALLED ON NULL INPUT|RETURNS NULL ON NULL INPUT)\").matcher(sql);\n    return count(m) <= 1;\n}","tryCatchPattern":"try {\n    parser.createStatement(sql);\n} catch (ParsingException e) {\n    if (e.getMessage().startsWith(\"Duplicate null-call clause\")) {\n        throw new IllegalArgumentException(\"Keep only one CALLED/RETURNS NULL ON NULL INPUT clause\", e);\n    }\n    throw e;\n}","preventionTips":["Emit the null-call clause from a single nullable field, defaulting to null (omit).","Deduplicate characteristic fragments when composing DDL.","Cover function DDL generation with parser round-trip tests."],"tags":["sql-parser","create-function","duplicate-clause"],"backgroundTag":"duplicate-clause","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"}