class HasDatabasePrivilegeFunction extends HasPrivilegeFunction {

    public static final FunctionName NAME = new FunctionName(PgCatalogSchemaInfo.NAME, "has_database_privilege");

    private static final FourFunction<Roles, Role, Object, Collection<Permission>, Boolean> CHECK_BY_DB_NAME =
        (roles, user, db, permissions) -> {
            if (Constants.DB_NAME.equals(db) == false) {
                throw new IllegalArgumentException(String.format(Locale.ENGLISH,
                                                                 "database \"%s\" does not exist",
                                                                 db));
            }
            return checkPrivileges(user, permissions);
        };

    private static final FourFunction<Roles, Role, Object, Collection<Permission>, Boolean> CHECK_BY_DB_OID =
        (roles, user, db, privileges) -> {
            if (Constants.DB_OID != (Integer) db) {
                throw new IllegalArgumentException(String.format(Locale.ENGLISH,
                                                                 "database with OID \"%s\" does not exist",
                                                                 db));
            }
            return checkPrivileges(user, privileges);
        };

    private static boolean checkPrivileges(Role user, Collection<Permission> permissions) {
        if (permissions.contains(Permission.DQL)) { // CONNECT
            return true;
        }

        boolean result = true;
        if (permissions.contains(Permission.DML)) { // TEMP privilege
            result = false;
        }
        if (permissions.contains(Permission.DDL)) { // CREATE privilege
            result = hasCreatePrivilege(user);
        }
        return result;
    }

    private static boolean hasCreatePrivilege(Role user) {
        if (user.isSuperUser()) {
            return true;
        }
        for (Privilege p : user.privileges()) {
            if (p.subject().permission() == Permission.DDL &&
                (p.subject().securable() == Securable.SCHEMA || p.subject().securable() == Securable.CLUSTER)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param permissionNames is a comma separated list.
     * Valid permissionNames are 'CONNECT', 'CREATE' and 'TEMP' or `TEMPORARY` which map to DQL, DDL and DML respectively.
     * Extra whitespaces between privilege names and repetition of the valid argument are allowed.
     *
     * @see HasPrivilegeFunction#parsePermissions(String)
     */
    @Nullable
    protected Collection<Permission> parsePermissions(String permissionNames) {<FILL_FUNCTION_BODY>}

    public static void register(Functions.Builder module) {
        // Signature without user, takes user from session.
        module.add(
            Signature.scalar(
                NAME,
                DataTypes.STRING.getTypeSignature(), // Database
                DataTypes.STRING.getTypeSignature(), // Privilege
                DataTypes.BOOLEAN.getTypeSignature()
            ).withFeatures(DETERMINISTIC_ONLY),
            (signature, boundSignature) -> new HasDatabasePrivilegeFunction(signature, boundSignature, USER_BY_NAME, CHECK_BY_DB_NAME)
        );

        // Signature without user, takes user from session.
        module.add(
            Signature.scalar(
                NAME,
                DataTypes.INTEGER.getTypeSignature(), // Database
                DataTypes.STRING.getTypeSignature(),  // Privilege
                DataTypes.BOOLEAN.getTypeSignature()
            ).withFeatures(DETERMINISTIC_ONLY),
            (signature, boundSignature) -> new HasDatabasePrivilegeFunction(signature, boundSignature,
                                                                            USER_BY_NAME, CHECK_BY_DB_OID)
        );

        module.add(
            Signature.scalar(
                NAME,
                DataTypes.STRING.getTypeSignature(), // User
                DataTypes.STRING.getTypeSignature(), // Database
                DataTypes.STRING.getTypeSignature(), // Privilege
                DataTypes.BOOLEAN.getTypeSignature()
            ).withFeatures(DETERMINISTIC_ONLY),
            (signature, boundSignature) -> new HasDatabasePrivilegeFunction(signature, boundSignature,
                                                                            USER_BY_NAME, CHECK_BY_DB_NAME)
        );

        module.add(
            Signature.scalar(
                NAME,
                DataTypes.STRING.getTypeSignature(),  // User
                DataTypes.INTEGER.getTypeSignature(), // Database
                DataTypes.STRING.getTypeSignature(),  // Privilege
                DataTypes.BOOLEAN.getTypeSignature()
            ).withFeatures(DETERMINISTIC_ONLY),
            (signature, boundSignature) -> new HasDatabasePrivilegeFunction(signature, boundSignature,
                                                                            USER_BY_NAME, CHECK_BY_DB_OID)
        );

        module.add(
            Signature.scalar(
                NAME,
                DataTypes.INTEGER.getTypeSignature(), // User
                DataTypes.STRING.getTypeSignature(),  // Database
                DataTypes.STRING.getTypeSignature(),  // Privilege
                DataTypes.BOOLEAN.getTypeSignature()
            ).withFeatures(DETERMINISTIC_ONLY),
            (signature, boundSignature) -> new HasDatabasePrivilegeFunction(signature, boundSignature,
                                                                            USER_BY_OID, CHECK_BY_DB_NAME)
        );

        module.add(
            Signature.scalar(
                NAME,
                DataTypes.INTEGER.getTypeSignature(), // User
                DataTypes.INTEGER.getTypeSignature(), // Database
                DataTypes.STRING.getTypeSignature(),  // Privilege
                DataTypes.BOOLEAN.getTypeSignature()
            ).withFeatures(DETERMINISTIC_ONLY),
            (signature, boundSignature) -> new HasDatabasePrivilegeFunction(signature, boundSignature,
                                                                            USER_BY_OID, CHECK_BY_DB_OID)
        );
    }

    protected HasDatabasePrivilegeFunction(Signature signature,
                                           BoundSignature boundSignature,
                                           BiFunction<Roles, Object, Role> getUser,
                                           FourFunction<Roles, Role, Object, Collection<Permission>, Boolean> checkPrivilege) {
        super(signature, boundSignature, getUser, checkPrivilege);
    }
}

        Collection<Permission> toCheck = new HashSet<>();
        String[] permissions = permissionNames.toLowerCase(Locale.ENGLISH).split(",");
        for (String p : permissions) {
            p = p.trim();
            switch (p) {
                case "connect" -> toCheck.add(Permission.DQL);
                case "create" -> toCheck.add(Permission.DDL);
                case "temp" -> toCheck.add(Permission.DML);
                case "temporary" -> toCheck.add(Permission.DML);
                default ->
                    // Same error as PG
                    throw new IllegalArgumentException(String.format(Locale.ENGLISH,
                                                                     "Unrecognized permission: %s",
                                                                     p));
            }
        }
        return toCheck;
    
<methods>public Scalar<java.lang.Boolean,java.lang.Object> compile(List<io.crate.expression.symbol.Symbol>, java.lang.String, io.crate.role.Roles) ,public final java.lang.Boolean evaluate(io.crate.metadata.TransactionContext, io.crate.metadata.NodeContext, Input<java.lang.Object>[]) ,public io.crate.expression.symbol.Symbol normalizeSymbol(io.crate.expression.symbol.Function, io.crate.metadata.TransactionContext, io.crate.metadata.NodeContext) <variables>protected static final BiFunction<io.crate.role.Roles,java.lang.Object,io.crate.role.Role> USER_BY_NAME,protected static final BiFunction<io.crate.role.Roles,java.lang.Object,io.crate.role.Role> USER_BY_OID,private final non-sealed FourFunction<io.crate.role.Roles,io.crate.role.Role,java.lang.Object,Collection<io.crate.role.Permission>,java.lang.Boolean> checkPrivilege,private final non-sealed BiFunction<io.crate.role.Roles,java.lang.Object,io.crate.role.Role> getUser