You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sl...@apache.org on 2014/08/25 10:10:40 UTC
git commit: Add IF [NOT] EXISTS to create/drop trigger
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 <sy...@datastax.com>
Authored: Mon Aug 25 10:08:44 2014 +0200
Committer: Sylvain Lebresne <sy...@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);
}