prestodb/presto · error · PrestoException

NOT_SUPPORTED

NOT_SUPPORTED

Error message

Invoking a dynamically registered function in SQL function body is not supported

What it means

CREATE FUNCTION bodies may only call functions resolvable at creation time. If semantic analysis of the function body yields handles that are instances of SqlFunctionHandle — i.e. dynamically (session-level) registered SQL functions — CreateFunctionTask throws PrestoException NOT_SUPPORTED.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/execution/CreateFunctionTask.java:98

    @Override
    public String explain(CreateFunction statement, List<Expression> parameters)
    {
        return format("CREATE %sFUNCTION %s", statement.isTemporary() ? "TEMPORARY " : "", statement.getFunctionName());
    }

    @Override
    public ListenableFuture<?> execute(CreateFunction statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List<Expression> parameters, String query)
    {
        Map<NodeRef<com.facebook.presto.sql.tree.Parameter>, Expression> parameterLookup = parameterExtractor(statement, parameters);
        Session session = stateMachine.getSession();
        Analyzer analyzer = new Analyzer(session, metadata, sqlParser, accessControl, Optional.empty(), parameters, parameterLookup, stateMachine.getWarningCollector(), query, new ViewDefinitionReferences());
        Analysis analysis = analyzer.analyzeSemantic(statement, false);
        checkAccessPermissions(analysis.getAccessControlReferences(), analysis.getViewDefinitionReferences(), query, session.getPreparedStatements(), session.getIdentity(), accessControl, session.getAccessControlContext());

        if (analysis.getFunctionHandles().values().stream()
                .anyMatch(SqlFunctionHandle.class::isInstance)) {
            throw new PrestoException(NOT_SUPPORTED, "Invoking a dynamically registered function in SQL function body is not supported");
        }

        SqlInvokedFunction function = createSqlInvokedFunction(statement, metadata, analysis);
        if (statement.isTemporary()) {
            stateMachine.addSessionFunction(new SqlFunctionId(function.getSignature().getName(), function.getSignature().getArgumentTypes()), function);
        }
        else {
            metadata.getFunctionAndTypeManager().createFunction(function, statement.isReplace());
        }

        return immediateFuture(null);
    }

    @Override
    public void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query, Map<String, String> preparedStatements, Map<QualifiedObjectName, ViewDefinition> viewDefinitions, Map<QualifiedObjectName, MaterializedViewDefinition> materializedViewDefinitions) {}

    private SqlInvokedFunction createSqlInvokedFunction(CreateFunction statement, Metadata metadata, Analysis analysis)
    {

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Remove the call to the dynamically registered function from the function body
  2. Register the dependency as a persistent catalog function so the body resolves a static handle
  3. Inline the dependent function's logic into the new function body
  4. Restructure to define the dependency first as a permanent function in the same catalog

Example fix

// before
CREATE FUNCTION my.lib.f(x bigint) RETURNS bigint AS $$ SELECT my.session_helper(x) + 1 $$; -- session_helper is dynamic
// after
CREATE FUNCTION my.lib.helper(x bigint) RETURNS bigint AS $$ ... $$; -- permanent
CREATE FUNCTION my.lib.f(x bigint) RETURNS bigint AS $$ SELECT my.lib.helper(x) + 1 $$;
Defensive patterns

Strategy: validation

Validate before calling

// ensure function bodies only reference persistent functions
Set<String> dynamicFunctions = sessionFunctions.keySet();
for (String callee : referencedFunctions(body)) {
    if (dynamicFunctions.contains(callee)) {
        throw new IllegalStateException("Body references dynamic function: " + callee);
    }
}

Prevention

When it happens

Trigger: Defining a SQL function whose body calls a function registered dynamically in the session (e.g. via a prior temporary CREATE FUNCTION in the same session); nesting dynamic function references inside new function definitions.

Common situations: Scripted sessions that register helper functions and then build more functions on top; session-scoped temporary functions referenced from persistent catalog functions; migration scripts chaining function definitions.

Understand the failure class

Background: Presto NOT_SUPPORTED error: what "not supported" means and how to fix it — this error's family across 3 libraries.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/403bb73132c1a3d2. Report an issue: GitHub.