cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From slebre...@apache.org
Subject git commit: Add IF [NOT] EXISTS to create/drop trigger
Date Mon, 25 Aug 2014 08:10:40 GMT
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 <sylvain@datastax.com>
Authored: Mon Aug 25 10:08:44 2014 +0200
Committer: Sylvain Lebresne <sylvain@datastax.com>
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<Mutation> 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);
         }


Mime
View raw message