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 4CA8511F81 for ; Mon, 25 Aug 2014 08:10:41 +0000 (UTC) Received: (qmail 8091 invoked by uid 500); 25 Aug 2014 08:10:41 -0000 Delivered-To: apmail-cassandra-commits-archive@cassandra.apache.org Received: (qmail 8055 invoked by uid 500); 25 Aug 2014 08:10:41 -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 8021 invoked by uid 99); 25 Aug 2014 08:10:41 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 25 Aug 2014 08:10:41 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id CC56C9CAE2B; Mon, 25 Aug 2014 08:10:40 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: slebresne@apache.org To: commits@cassandra.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: git commit: Add IF [NOT] EXISTS to create/drop trigger Date: Mon, 25 Aug 2014 08:10:40 +0000 (UTC) Repository: cassandra Updated Branches: refs/heads/cassandra-2.1 c489a4e37 -> 591a2779a Add IF [NOT] EXISTS to create/drop trigger patch by blerer; reviewed by snazy for CASSANDRA-7606 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/591a2779 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/591a2779 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/591a2779 Branch: refs/heads/cassandra-2.1 Commit: 591a2779a7f4213fd7f0e7eee8d2a844359d65bb Parents: c489a4e Author: Sylvain Lebresne Authored: Mon Aug 25 10:08:44 2014 +0200 Committer: Sylvain Lebresne Committed: Mon Aug 25 10:10:28 2014 +0200 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../org/apache/cassandra/config/CFMetaData.java | 12 +- src/java/org/apache/cassandra/cql3/Cql.g | 15 ++- .../cql3/statements/CreateTriggerStatement.java | 22 +++- .../cql3/statements/DropTriggerStatement.java | 22 ++-- .../org/apache/cassandra/cql3/CQLTester.java | 10 ++ .../cql3/CreateTriggerStatementTest.java | 115 +++++++++++++++++++ .../cassandra/triggers/TriggerExecutorTest.java | 2 +- 8 files changed, 177 insertions(+), 22 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/591a2779/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 5a1767a..a5314df 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 2.1.1 + * Add IF [NOT] EXISTS to create/drop triggers (CASSANDRA-7606) * (cqlsh) Display the current logged-in user (CASSANDRA-7785) * (cqlsh) Don't ignore CTRL-C during COPY FROM execution (CASSANDRA-7815) * (cqlsh) Order UDTs according to cross-type dependencies in DESCRIBE http://git-wip-us.apache.org/repos/asf/cassandra/blob/591a2779/src/java/org/apache/cassandra/config/CFMetaData.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/config/CFMetaData.java b/src/java/org/apache/cassandra/config/CFMetaData.java index a35357a..5247774 100644 --- a/src/java/org/apache/cassandra/config/CFMetaData.java +++ b/src/java/org/apache/cassandra/config/CFMetaData.java @@ -1976,13 +1976,19 @@ public final class CFMetaData cfDef.triggers.put(td.name, td); } - public void addTriggerDefinition(TriggerDefinition def) throws ConfigurationException + public void addTriggerDefinition(TriggerDefinition def) throws InvalidRequestException { - if (triggers.containsKey(def.name)) - throw new ConfigurationException(String.format("Cannot create trigger %s, a trigger with the same name already exists", def.name)); + if (containsTriggerDefinition(def)) + throw new InvalidRequestException( + String.format("Cannot create trigger %s, a trigger with the same name already exists", def.name)); triggers.put(def.name, def); } + public boolean containsTriggerDefinition(TriggerDefinition def) + { + return triggers.containsKey(def.name); + } + public boolean removeTrigger(String name) { return triggers.remove(name) != null; http://git-wip-us.apache.org/repos/asf/cassandra/blob/591a2779/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 9dcc268..ec24284 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -584,16 +584,21 @@ indexIdent returns [IndexTarget id] * CREATE TRIGGER triggerName ON columnFamily USING 'triggerClass'; */ createTriggerStatement returns [CreateTriggerStatement expr] - : K_CREATE K_TRIGGER (name=IDENT) K_ON cf=columnFamilyName K_USING cls=STRING_LITERAL - { $expr = new CreateTriggerStatement(cf, $name.text, $cls.text); } + @init { + boolean ifNotExists = false; + } + : K_CREATE K_TRIGGER (K_IF K_NOT K_EXISTS { ifNotExists = true; } )? (name=IDENT) + K_ON cf=columnFamilyName K_USING cls=STRING_LITERAL + { $expr = new CreateTriggerStatement(cf, $name.text, $cls.text, ifNotExists); } ; /** - * DROP TRIGGER triggerName ON columnFamily; + * DROP TRIGGER [IF EXISTS] triggerName ON columnFamily; */ dropTriggerStatement returns [DropTriggerStatement expr] - : K_DROP K_TRIGGER (name=IDENT) K_ON cf=columnFamilyName - { $expr = new DropTriggerStatement(cf, $name.text); } + @init { boolean ifExists = false; } + : K_DROP K_TRIGGER (K_IF K_EXISTS { ifExists = true; } )? (name=IDENT) K_ON cf=columnFamilyName + { $expr = new DropTriggerStatement(cf, $name.text, ifExists); } ; /** http://git-wip-us.apache.org/repos/asf/cassandra/blob/591a2779/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java index db0cc22..6ebe0d3 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java @@ -25,6 +25,7 @@ import org.apache.cassandra.config.Schema; import org.apache.cassandra.config.TriggerDefinition; import org.apache.cassandra.cql3.CFName; import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.RequestValidationException; import org.apache.cassandra.exceptions.UnauthorizedException; import org.apache.cassandra.service.ClientState; @@ -39,12 +40,14 @@ public class CreateTriggerStatement extends SchemaAlteringStatement private final String triggerName; private final String triggerClass; + private final boolean ifNotExists; - public CreateTriggerStatement(CFName name, String triggerName, String clazz) + public CreateTriggerStatement(CFName name, String triggerName, String clazz, boolean ifNotExists) { super(name); this.triggerName = triggerName; this.triggerClass = clazz; + this.ifNotExists = ifNotExists; } public void checkAccess(ClientState state) throws UnauthorizedException @@ -65,13 +68,20 @@ public class CreateTriggerStatement extends SchemaAlteringStatement } } - public boolean announceMigration(boolean isLocalOnly) throws ConfigurationException + public boolean announceMigration(boolean isLocalOnly) throws ConfigurationException, InvalidRequestException { CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), columnFamily()).copy(); - cfm.addTriggerDefinition(TriggerDefinition.create(triggerName, triggerClass)); - logger.info("Adding trigger with name {} and class {}", triggerName, triggerClass); - MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly); - return true; + + TriggerDefinition triggerDefinition = TriggerDefinition.create(triggerName, triggerClass); + + if (!ifNotExists || !cfm.containsTriggerDefinition(triggerDefinition)) + { + cfm.addTriggerDefinition(triggerDefinition); + logger.info("Adding trigger with name {} and class {}", triggerName, triggerClass); + MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly); + return true; + } + return false; } public Event.SchemaChange changeEvent() http://git-wip-us.apache.org/repos/asf/cassandra/blob/591a2779/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java index 4fdc21e..e3db1e1 100644 --- a/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java @@ -24,6 +24,7 @@ import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.Schema; import org.apache.cassandra.cql3.CFName; import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.RequestValidationException; import org.apache.cassandra.exceptions.UnauthorizedException; import org.apache.cassandra.service.ClientState; @@ -37,10 +38,13 @@ public class DropTriggerStatement extends SchemaAlteringStatement private final String triggerName; - public DropTriggerStatement(CFName name, String triggerName) + private final boolean ifExists; + + public DropTriggerStatement(CFName name, String triggerName, boolean ifExists) { super(name); this.triggerName = triggerName; + this.ifExists = ifExists; } public void checkAccess(ClientState state) throws UnauthorizedException @@ -53,14 +57,18 @@ public class DropTriggerStatement extends SchemaAlteringStatement ThriftValidation.validateColumnFamily(keyspace(), columnFamily()); } - public boolean announceMigration(boolean isLocalOnly) throws ConfigurationException + public boolean announceMigration(boolean isLocalOnly) throws ConfigurationException, InvalidRequestException { CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), columnFamily()).copy(); - if (!cfm.removeTrigger(triggerName)) - throw new ConfigurationException(String.format("Trigger %s was not found", triggerName)); - logger.info("Dropping trigger with name {}", triggerName); - MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly); - return true; + if (cfm.removeTrigger(triggerName)) + { + logger.info("Dropping trigger with name {}", triggerName); + MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly); + return true; + } + if (!ifExists) + throw new InvalidRequestException(String.format("Trigger %s was not found", triggerName)); + return false; } public Event.SchemaChange changeEvent() http://git-wip-us.apache.org/repos/asf/cassandra/blob/591a2779/test/unit/org/apache/cassandra/cql3/CQLTester.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java index 3dbff1e..426e4b8 100644 --- a/test/unit/org/apache/cassandra/cql3/CQLTester.java +++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java @@ -150,6 +150,16 @@ public abstract class CQLTester } } + protected String keyspace() + { + return KEYSPACE; + } + + protected String currentTable() + { + return currentTable; + } + protected String createType(String query) { String typeName = "type_" + seqNumber.getAndIncrement(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/591a2779/test/unit/org/apache/cassandra/cql3/CreateTriggerStatementTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/CreateTriggerStatementTest.java b/test/unit/org/apache/cassandra/cql3/CreateTriggerStatementTest.java new file mode 100644 index 0000000..1f2988c --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/CreateTriggerStatementTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.cql3; + +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.Collections; + +import org.apache.cassandra.config.CFMetaData; +import org.apache.cassandra.config.Schema; +import org.apache.cassandra.config.TriggerDefinition; +import org.apache.cassandra.db.ColumnFamily; +import org.apache.cassandra.db.Mutation; +import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.triggers.ITrigger; +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class CreateTriggerStatementTest extends CQLTester +{ + @Test + public void testCreateTrigger() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY (a))"); + execute("CREATE TRIGGER trigger_1 ON %s USING '" + TestTrigger.class.getName() + "'"); + assertTriggerExists("trigger_1", TestTrigger.class); + execute("CREATE TRIGGER trigger_2 ON %s USING '" + TestTrigger.class.getName() + "'"); + assertTriggerExists("trigger_2", TestTrigger.class); + assertInvalid("CREATE TRIGGER trigger_1 ON %s USING '" + TestTrigger.class.getName() + "'"); + } + + @Test + public void testCreateTriggerIfNotExists() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY (a, b))"); + + execute("CREATE TRIGGER IF NOT EXISTS trigger_1 ON %s USING '" + TestTrigger.class.getName() + "'"); + assertTriggerExists("trigger_1", TestTrigger.class); + + execute("CREATE TRIGGER IF NOT EXISTS trigger_1 ON %s USING '" + TestTrigger.class.getName() + "'"); + assertTriggerExists("trigger_1", TestTrigger.class); + } + + @Test + public void testDropTrigger() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY (a))"); + + execute("CREATE TRIGGER trigger_1 ON %s USING '" + TestTrigger.class.getName() + "'"); + assertTriggerExists("trigger_1", TestTrigger.class); + + execute("DROP TRIGGER trigger_1 ON %s"); + assertTriggerDoesNotExists("trigger_1", TestTrigger.class); + + execute("CREATE TRIGGER trigger_1 ON %s USING '" + TestTrigger.class.getName() + "'"); + assertTriggerExists("trigger_1", TestTrigger.class); + + assertInvalid("DROP TRIGGER trigger_2 ON %s"); + } + + @Test + public void testDropTriggerIfExists() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY (a))"); + + execute("DROP TRIGGER IF EXISTS trigger_1 ON %s"); + assertTriggerDoesNotExists("trigger_1", TestTrigger.class); + + execute("CREATE TRIGGER trigger_1 ON %s USING '" + TestTrigger.class.getName() + "'"); + assertTriggerExists("trigger_1", TestTrigger.class); + + execute("DROP TRIGGER IF EXISTS trigger_1 ON %s"); + assertTriggerDoesNotExists("trigger_1", TestTrigger.class); + } + + private void assertTriggerExists(String name, Class clazz) + { + CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), currentTable()).copy(); + assertTrue("the trigger does not exist", cfm.containsTriggerDefinition(TriggerDefinition.create(name, + clazz.getName()))); + } + + private void assertTriggerDoesNotExists(String name, Class clazz) + { + CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), currentTable()).copy(); + assertFalse("the trigger exists", cfm.containsTriggerDefinition(TriggerDefinition.create(name, + clazz.getName()))); + } + + public static class TestTrigger implements ITrigger + { + public Collection augment(ByteBuffer key, ColumnFamily update) + { + return Collections.emptyList(); + } + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/591a2779/test/unit/org/apache/cassandra/triggers/TriggerExecutorTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/triggers/TriggerExecutorTest.java b/test/unit/org/apache/cassandra/triggers/TriggerExecutorTest.java index 7d009c1..3d505c8 100644 --- a/test/unit/org/apache/cassandra/triggers/TriggerExecutorTest.java +++ b/test/unit/org/apache/cassandra/triggers/TriggerExecutorTest.java @@ -230,7 +230,7 @@ public class TriggerExecutorTest if (trigger != null) metadata.addTriggerDefinition(trigger); } - catch (ConfigurationException e) + catch (InvalidRequestException e) { throw new AssertionError(e); }