Better error for unqualified functions in authz statements

Patch by Sam Tunnicliffe; reviewed by Carl Yeksigian for CASSANDRA-12925
diff --git a/CHANGES.txt b/CHANGES.txt
index a85386b..396fa3f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.11
+ * Better error when modifying function permissions without explicit keyspace (CASSANDRA-12925)
  * Indexer is not correctly invoked when building indexes over sstables (CASSANDRA-13075)
  * Read repair is not blocking repair to finish in foreground repair (CASSANDRA-13115)
  * Stress daemon help is incorrect (CASSANDRA-12563)
diff --git a/src/java/org/apache/cassandra/auth/FunctionResource.java b/src/java/org/apache/cassandra/auth/FunctionResource.java
index 2c5b8a1..01a4de5 100644
--- a/src/java/org/apache/cassandra/auth/FunctionResource.java
+++ b/src/java/org/apache/cassandra/auth/FunctionResource.java
@@ -34,6 +34,7 @@
 import org.apache.cassandra.cql3.functions.FunctionName;
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.db.marshal.TypeParser;
+import org.apache.cassandra.exceptions.InvalidRequestException;
 
 /**
  * IResource implementation representing functions.
@@ -146,6 +147,9 @@
      */
     public static FunctionResource functionFromCql(String keyspace, String name, List<CQL3Type.Raw> argTypes)
     {
+        if (keyspace == null)
+            throw new InvalidRequestException("In this context function name must be " +
+                                              "explictly qualified by a keyspace");
         List<AbstractType<?>> abstractTypes = new ArrayList<>();
         for (CQL3Type.Raw cqlType : argTypes)
             abstractTypes.add(cqlType.prepare(keyspace).getType());
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFAuthTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFAuthTest.java
index 6993bec..e5ecc72 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UFAuthTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UFAuthTest.java
@@ -449,6 +449,31 @@
         getStatement(cql).checkAccess(clientState);
     }
 
+    @Test
+    public void grantAndRevokeSyntaxRequiresExplicitKeyspace() throws Throwable
+    {
+        setupTable("CREATE TABLE %s (k int, s int STATIC, v1 int, v2 int, PRIMARY KEY(k, v1))");
+        String functionName = shortFunctionName(createSimpleFunction());
+        assertRequiresKeyspace(String.format("GRANT EXECUTE ON FUNCTION %s() TO %s",
+                                             functionName,
+                                             role.getRoleName()));
+        assertRequiresKeyspace(String.format("REVOKE EXECUTE ON FUNCTION %s() FROM %s",
+                                             functionName,
+                                             role.getRoleName()));
+    }
+
+    private void assertRequiresKeyspace(String cql) throws Throwable
+    {
+        try
+        {
+            getStatement(cql);
+        }
+        catch (InvalidRequestException e)
+        {
+            assertEquals("In this context function name must be explictly qualified by a keyspace", e.getMessage());
+        }
+    }
+
     private void assertPermissionsOnNestedFunctions(String innerFunction, String outerFunction) throws Throwable
     {
         String cql = String.format("SELECT k, %s FROM %s WHERE k=0",