{"record":{"id":"c45b451ed8f96875","repo":"apache/cassandra","slug":"function-s-is-still-referenced-by-column-masks","errorCode":null,"errorMessage":"Function '%s' is still referenced by column masks in tables %s","messagePattern":"Function '(.+?)' is still referenced by column masks in tables (.+?)","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/DropFunctionStatement.java","lineNumber":141,"sourceCode":"\n            throw ire(\"Function '%s' doesn't exist\", name);\n        }\n\n        String dependentAggregates =\n            keyspace.userFunctions\n                    .aggregatesUsingFunction(function)\n                    .map(a -> a.name().toString())\n                    .collect(joining(\", \"));\n\n        if (!dependentAggregates.isEmpty())\n            throw ire(\"Function '%s' is still referenced by aggregates %s\", name, dependentAggregates);\n\n        String dependentTables = keyspace.tablesUsingFunction(function)\n                                         .map(table -> table.name)\n                                         .collect(joining(\", \"));\n\n        if (!dependentTables.isEmpty())\n            throw ire(\"Function '%s' is still referenced by column masks in tables %s\", name, dependentTables);\n\n        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.userFunctions.without(function)));\n    }\n\n    SchemaChange schemaChangeEvent(KeyspacesDiff diff)\n    {\n        UserFunctions dropped = diff.altered.get(0).udfs.dropped;\n        assert dropped.size() == 1;\n        return SchemaChange.forFunction(Change.DROPPED, (UDFunction) dropped.iterator().next());\n    }\n\n    public void authorize(ClientState client)\n    {\n        KeyspaceMetadata keyspace = Schema.instance.getKeyspaceMetadata(keyspaceName);\n        if (null == keyspace)\n            return;\n\n        Stream<UserFunction> functions = keyspace.userFunctions.get(new FunctionName(keyspaceName, functionName)).stream();","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/DropFunctionStatement.java#L123-L159","documentation":"DROP FUNCTION was rejected because the function is used as a column mask (dynamic data masking) on one or more tables. Cassandra refuses to drop functions still referenced by column masking definitions.","triggerScenarios":"DROP FUNCTION on a function referenced by a MASKED WITH column mask in any table of the keyspace; detected via keyspace.tablesUsingFunction(function).","commonSituations":"Forgetting to unmask column definitions before removing a masking function; security cleanup of masking functions while masking policies are still applied.","solutions":["Remove the mask from the dependent columns first (ALTER TABLE ... ALTER col ... DROP MASKED or similar).","Drop the listed tables if they are no longer needed.","Then re-run DROP FUNCTION."],"exampleFix":"// before\nDROP FUNCTION ks.mask_ssn (text);\n// after\nALTER TABLE ks.users ALTER ssn DROP MASKED;\nDROP FUNCTION ks.mask_ssn (text);","handlingStrategy":"try-catch","validationCode":"boolean masked = keyspace.tables.stream().anyMatch(t -> t.columns().stream()\n    .anyMatch(c -> c.mask() != null && c.mask().function().equals(fn)));\nif (masked) unmaskColumnsFirst();","typeGuard":null,"tryCatchPattern":"try { dropFunction(...); } catch (InvalidRequestException e) { if (e.getMessage().contains(\"referenced by column masks\")) { /* unmask listed tables, then retry */ } else throw e; }","preventionTips":["Inventory column masks (system_schema.columns) before dropping functions","Include unmasking in the function decommissioning runbook","Prefix masking functions for easy traceability"],"tags":["cql","schema","masking","udf"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}