Return-Path: X-Original-To: apmail-cassandra-commits-archive@www.apache.org Delivered-To: apmail-cassandra-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id C6C0A11612 for ; Wed, 3 Sep 2014 15:27:12 +0000 (UTC) Received: (qmail 13621 invoked by uid 500); 3 Sep 2014 15:27:11 -0000 Delivered-To: apmail-cassandra-commits-archive@cassandra.apache.org Received: (qmail 13595 invoked by uid 500); 3 Sep 2014 15:27:11 -0000 Mailing-List: contact commits-help@cassandra.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cassandra.apache.org Delivered-To: mailing list commits@cassandra.apache.org Received: (qmail 13575 invoked by uid 99); 3 Sep 2014 15:27:11 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 03 Sep 2014 15:27:11 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 79449A06361; Wed, 3 Sep 2014 15:27:11 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: tylerhobbs@apache.org To: commits@cassandra.apache.org Message-Id: <8e52f0900fd746fea839e8236e985891@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: git commit: Require arg types to disambiguate UDF drops Date: Wed, 3 Sep 2014 15:27:11 +0000 (UTC) Repository: cassandra Updated Branches: refs/heads/trunk ed0532c64 -> a88a78380 Require arg types to disambiguate UDF drops Patch by Robert Stupp; reviewed by Tyler Hobbs for CASSANDRA-7812 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/a88a7838 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/a88a7838 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/a88a7838 Branch: refs/heads/trunk Commit: a88a78380ea26a25457a58a115195eb944bf0420 Parents: ed0532c Author: Robert Stupp Authored: Wed Sep 3 10:26:30 2014 -0500 Committer: Tyler Hobbs Committed: Wed Sep 3 10:26:30 2014 -0500 ---------------------------------------------------------------------- CHANGES.txt | 1 + src/java/org/apache/cassandra/cql3/Cql.g | 13 +++- .../cassandra/cql3/functions/UDFunction.java | 12 ++-- .../cql3/statements/DropFunctionStatement.java | 68 +++++++++++++++++--- .../cassandra/service/MigrationManager.java | 10 ++- test/unit/org/apache/cassandra/cql3/UFTest.java | 23 ++++++- 6 files changed, 105 insertions(+), 22 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/a88a7838/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index babfa0e..281b63b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0 + * Require arg types to disambiguate UDF drops (CASSANDRA-7812) * Do anticompaction in groups (CASSANDRA-6851) * Verify that UDF class methods are static (CASSANDRA-7781) * Support pure user-defined functions (CASSANDRA-7395, 7740) http://git-wip-us.apache.org/repos/asf/cassandra/blob/a88a7838/src/java/org/apache/cassandra/cql3/Cql.g ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g index af2c239..8c40885 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -527,11 +527,22 @@ createFunctionStatement returns [CreateFunctionStatement expr] dropFunctionStatement returns [DropFunctionStatement expr] @init { boolean ifExists = false; + List argsTypes = new ArrayList<>(); + boolean argsPresent = false; } : K_DROP K_FUNCTION (K_IF K_EXISTS { ifExists = true; } )? fn=functionName - { $expr = new DropFunctionStatement(fn, ifExists); } + ( + '(' + ( + v=comparatorType { argsTypes.add(v); } + ( ',' v=comparatorType { argsTypes.add(v); } )* + )? + ')' + { argsPresent = true; } + )? + { $expr = new DropFunctionStatement(fn, argsTypes, argsPresent, ifExists); } ; /** http://git-wip-us.apache.org/repos/asf/cassandra/blob/a88a7838/src/java/org/apache/cassandra/cql3/functions/UDFunction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java index 558fb72..aeefe41 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java @@ -138,11 +138,15 @@ public abstract class UDFunction extends AbstractFunction return new Mutation(Keyspace.SYSTEM_KS, kv.decompose(name.namespace, name.name)); } - // TODO: we should allow removing just one function, not all functions having a given name - public static Mutation dropFromSchema(long timestamp, FunctionName fun) + public Mutation toSchemaDrop(long timestamp) { - Mutation mutation = makeSchemaMutation(fun); - mutation.delete(SystemKeyspace.SCHEMA_FUNCTIONS_CF, timestamp); + Mutation mutation = makeSchemaMutation(name); + ColumnFamily cf = mutation.addOrGet(SystemKeyspace.SCHEMA_FUNCTIONS_CF); + + Composite prefix = CFMetaData.SchemaFunctionsCf.comparator.make(computeSignature(argTypes)); + int ldt = (int) (System.currentTimeMillis() / 1000); + cf.addAtom(new RangeTombstone(prefix, prefix.end(), timestamp, ldt)); + return mutation; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/a88a7838/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java index 4c963a8..78c8607 100644 --- a/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java @@ -17,10 +17,13 @@ */ package org.apache.cassandra.cql3.statements; +import java.util.ArrayList; import java.util.List; import org.apache.cassandra.auth.Permission; +import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.cql3.functions.*; +import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.RequestValidationException; import org.apache.cassandra.exceptions.UnauthorizedException; @@ -35,10 +38,17 @@ public final class DropFunctionStatement extends SchemaAlteringStatement { private final FunctionName functionName; private final boolean ifExists; + private final List argRawTypes; + private final boolean argsPresent; - public DropFunctionStatement(FunctionName functionName, boolean ifExists) + public DropFunctionStatement(FunctionName functionName, + List argRawTypes, + boolean argsPresent, + boolean ifExists) { this.functionName = functionName; + this.argRawTypes = argRawTypes; + this.argsPresent = argsPresent; this.ifExists = ifExists; } @@ -68,18 +78,58 @@ public final class DropFunctionStatement extends SchemaAlteringStatement public boolean announceMigration(boolean isLocalOnly) throws RequestValidationException { List olds = Functions.find(functionName); - if (olds == null || olds.isEmpty()) + + if (!argsPresent && olds != null && olds.size() > 1) + throw new InvalidRequestException(String.format("'DROP FUNCTION %s' matches multiple function definitions; " + + "specify the argument types by issuing a statement like " + + "'DROP FUNCTION %s (type, type, ...)'. Hint: use cqlsh " + + "'DESCRIBE FUNCTION %s' command to find all overloads", + functionName, functionName, functionName)); + + List> argTypes = new ArrayList<>(argRawTypes.size()); + for (CQL3Type.Raw rawType : argRawTypes) + { + // We have no proper keyspace to give, which means that this will break (NPE currently) + // for UDT: #7791 is open to fix this + argTypes.add(rawType.prepare(null).getType()); + } + + Function old; + if (argsPresent) + { + old = Functions.find(functionName, argTypes); + if (old == null) + { + if (ifExists) + return false; + // just build a nicer error message + StringBuilder sb = new StringBuilder(); + for (CQL3Type.Raw rawType : argRawTypes) + { + if (sb.length() > 0) + sb.append(", "); + sb.append(rawType); + } + throw new InvalidRequestException(String.format("Cannot drop non existing function '%s(%s)'", + functionName, sb)); + } + } + else { - if (ifExists) - return false; - throw new InvalidRequestException(String.format("Cannot drop non existing function '%s'", functionName)); + if (olds == null || olds.isEmpty()) + { + if (ifExists) + return false; + throw new InvalidRequestException(String.format("Cannot drop non existing function '%s'", functionName)); + } + old = olds.get(0); } - for (Function f : olds) - if (f.isNative()) - throw new InvalidRequestException(String.format("Cannot drop function '%s' because it has native overloads", functionName)); + if (old.isNative()) + throw new InvalidRequestException(String.format("Cannot drop function '%s' because it is a " + + "native (built-in) function", functionName)); - MigrationManager.announceFunctionDrop(functionName, isLocalOnly); + MigrationManager.announceFunctionDrop((UDFunction)old, isLocalOnly); return true; } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/a88a7838/src/java/org/apache/cassandra/service/MigrationManager.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/service/MigrationManager.java b/src/java/org/apache/cassandra/service/MigrationManager.java index b0cf79d..4f03483 100644 --- a/src/java/org/apache/cassandra/service/MigrationManager.java +++ b/src/java/org/apache/cassandra/service/MigrationManager.java @@ -38,14 +38,11 @@ import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.KSMetaData; import org.apache.cassandra.config.UTMetaData; import org.apache.cassandra.config.Schema; -import org.apache.cassandra.cql3.functions.FunctionName; import org.apache.cassandra.cql3.functions.UDFunction; import org.apache.cassandra.db.*; import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.exceptions.AlreadyExistsException; import org.apache.cassandra.exceptions.ConfigurationException; -import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.gms.*; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataOutputPlus; @@ -374,10 +371,11 @@ public class MigrationManager announce(addSerializedKeyspace(UTMetaData.dropFromSchema(droppedType, FBUtilities.timestampMicros()), droppedType.keyspace), announceLocally); } - public static void announceFunctionDrop(FunctionName fun, boolean announceLocally) + public static void announceFunctionDrop(UDFunction udf, boolean announceLocally) { - logger.info(String.format("Drop Function '%s'", fun)); - announce(UDFunction.dropFromSchema(FBUtilities.timestampMicros(), fun), announceLocally); + Mutation mutation = udf.toSchemaDrop(FBUtilities.timestampMicros()); + logger.info(String.format("Drop Function overload '%s' args '%s'", udf.name(), udf.argTypes())); + announce(mutation, announceLocally); } public static void announceNewFunction(UDFunction udf, boolean announceLocally) http://git-wip-us.apache.org/repos/asf/cassandra/blob/a88a7838/test/unit/org/apache/cassandra/cql3/UFTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/UFTest.java b/test/unit/org/apache/cassandra/cql3/UFTest.java index 84161b2..60a7a68 100644 --- a/test/unit/org/apache/cassandra/cql3/UFTest.java +++ b/test/unit/org/apache/cassandra/cql3/UFTest.java @@ -19,8 +19,6 @@ package org.apache.cassandra.cql3; import org.junit.Test; -import org.apache.cassandra.exceptions.InvalidRequestException; - public class UFTest extends CQLTester { public static Double sin(Double val) @@ -141,6 +139,10 @@ public class UFTest extends CQLTester assertInvalid("DROP FUNCTION foo::sin"); // but don't complain with "IF EXISTS" execute("DROP FUNCTION IF EXISTS foo::sin"); + + // can't drop native functions + assertInvalid("DROP FUNCTION dateof"); + assertInvalid("DROP FUNCTION uuid"); } @Test @@ -186,5 +188,22 @@ public class UFTest extends CQLTester assertEmpty(execute("SELECT v FROM %s WHERE k = overloaded((ascii)?)", "foo")); // And since varchar == text, this should work too assertEmpty(execute("SELECT v FROM %s WHERE k = overloaded((varchar)?)", "foo")); + + // no such functions exist... + assertInvalid("DROP FUNCTION overloaded(boolean)"); + assertInvalid("DROP FUNCTION overloaded(bigint)"); + + // 'overloaded' has multiple overloads - so it has to fail (CASSANDRA-7812) + assertInvalid("DROP FUNCTION overloaded"); + execute("DROP FUNCTION overloaded(varchar)"); + assertInvalid("SELECT v FROM %s WHERE k = overloaded((text)?)", "foo"); + execute("DROP FUNCTION overloaded(text, text)"); + assertInvalid("SELECT v FROM %s WHERE k = overloaded((text)?,(text)?)", "foo", "bar"); + execute("DROP FUNCTION overloaded(ascii)"); + assertInvalid("SELECT v FROM %s WHERE k = overloaded((ascii)?)", "foo"); + // single-int-overload must still work + assertRows(execute("SELECT v FROM %s WHERE k = overloaded((int)?)", 3), row(1)); + // overloaded has just one overload now - so the following DROP FUNCTION is not ambigious (CASSANDRA-7812) + execute("DROP FUNCTION overloaded"); } }