Skip to content

Commit dddbe1a

Browse files
committed
Execution of CreateTriggerStatement should not rely on external state
patch by Maxwell Guo; reviewed by Sam Tunnicliffe for CASSANDRA-20287
1 parent ded7a59 commit dddbe1a

File tree

5 files changed

+50
-3
lines changed

5 files changed

+50
-3
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
5.1
2+
* Execution of CreateTriggerStatement should not rely on external state (CASSANDRA-20287)
23
* Support LIKE expressions in filtering queries (CASSANDRA-17198)
34
* Make legacy index rebuilds safe on Gossip -> TCM upgrades (CASSANDRA-20887)
45
* Minor improvements and hardening for IndexHints (CASSANDRA-20888)

src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
*/
1818
package org.apache.cassandra.cql3.statements.schema;
1919

20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
2023
import org.apache.cassandra.audit.AuditLogContext;
2124
import org.apache.cassandra.audit.AuditLogEntryType;
2225
import org.apache.cassandra.cql3.CQLStatement;
@@ -33,6 +36,7 @@
3336

3437
public final class CreateTriggerStatement extends AlterSchemaStatement
3538
{
39+
private static final Logger logger = LoggerFactory.getLogger(CreateTriggerStatement.class);
3640
private final String tableName;
3741
private final String triggerName;
3842
private final String triggerClass;
@@ -47,6 +51,21 @@ public CreateTriggerStatement(String keyspaceName, String tableName, String trig
4751
this.ifNotExists = ifNotExists;
4852
}
4953

54+
@Override
55+
public void validate(ClientState state)
56+
{
57+
try
58+
{
59+
TriggerExecutor.instance.loadTriggerClass(triggerClass);
60+
}
61+
catch (Exception e)
62+
{
63+
InvalidRequestException thrown = ire("Trigger class '%s' couldn't be loaded during validation. Reason : %s.", triggerClass, e.getMessage());
64+
thrown.initCause(e);
65+
throw thrown;
66+
}
67+
}
68+
5069
@Override
5170
public Keyspaces apply(ClusterMetadata metadata)
5271
{
@@ -77,9 +96,7 @@ public Keyspaces apply(ClusterMetadata metadata)
7796
}
7897
catch (Exception e)
7998
{
80-
InvalidRequestException thrown = ire("Trigger class '%s' couldn't be loaded", triggerClass);
81-
thrown.initCause(e);
82-
throw thrown;
99+
logger.warn(String.format("Trigger class '%s' couldn't be loaded at apply stage.", triggerClass));
83100
}
84101

85102
TableMetadata newTable = table.withSwapped(table.triggers.with(TriggerMetadata.create(triggerName, triggerClass)));

src/java/org/apache/cassandra/triggers/TriggerExecutor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.apache.cassandra.db.Mutation;
4141
import org.apache.cassandra.db.partitions.PartitionUpdate;
4242
import org.apache.cassandra.exceptions.CassandraException;
43+
import org.apache.cassandra.exceptions.ConfigurationException;
4344
import org.apache.cassandra.exceptions.InvalidRequestException;
4445
import org.apache.cassandra.io.util.File;
4546
import org.apache.cassandra.schema.TableId;
@@ -245,11 +246,13 @@ private List<Mutation> executeInternal(PartitionUpdate update)
245246
}
246247
List<Mutation> tmutations = Lists.newLinkedList();
247248
Thread.currentThread().setContextClassLoader(customClassLoader);
249+
String triggerClass = "";
248250
try
249251
{
250252
for (TriggerMetadata td : triggers)
251253
{
252254
ITrigger trigger = cachedTriggers.get(td.classOption);
255+
triggerClass = td.classOption;
253256
if (trigger == null)
254257
{
255258
trigger = loadTriggerInstance(td.classOption);
@@ -265,6 +268,10 @@ private List<Mutation> executeInternal(PartitionUpdate update)
265268
{
266269
throw ex;
267270
}
271+
catch (ClassNotFoundException ex)
272+
{
273+
throw new ConfigurationException("Trigger class " + triggerClass + " couldn't be found.");
274+
}
268275
catch (Exception ex)
269276
{
270277
throw new RuntimeException(String.format("Exception while executing trigger on table with ID: %s", update.metadata().id), ex);

test/unit/org/apache/cassandra/triggers/TriggerExecutorTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.cassandra.utils.FBUtilities;
3838

3939
import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
40+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
4041
import static org.junit.Assert.assertEquals;
4142
import static org.junit.Assert.assertNull;
4243

@@ -91,6 +92,15 @@ public void differentKeyColumnFamilies() throws ConfigurationException, InvalidR
9192
TriggerExecutor.instance.execute(makeCf(metadata, "k1", "v1", null));
9293
}
9394

95+
@Test
96+
public void triggerClassNotFound()
97+
{
98+
TableMetadata metadata = makeTableMetadata("ks1", "cf1", TriggerMetadata.create("test", "NotExistedTriggerClass"));
99+
assertThatExceptionOfType(ConfigurationException.class)
100+
.isThrownBy(()-> TriggerExecutor.instance.execute(makeCf(metadata, "k1", "v1", null)))
101+
.withMessageContaining("Trigger class NotExistedTriggerClass couldn't be found.");
102+
}
103+
94104
@Test
95105
public void noTriggerMutations() throws ConfigurationException, InvalidRequestException
96106
{

test/unit/org/apache/cassandra/triggers/TriggersTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.Collections;
2222
import java.util.concurrent.atomic.AtomicBoolean;
2323

24+
import org.apache.cassandra.exceptions.InvalidRequestException;
25+
2426
import org.junit.After;
2527
import org.junit.Assert;
2628
import org.junit.Before;
@@ -101,6 +103,16 @@ public void after()
101103
DatabaseDescriptor.setTriggersPolicy(originalTriggersPolicy);
102104
}
103105

106+
@Test
107+
public void testCreateNonExistTrigger()
108+
{
109+
Assertions.assertThatThrownBy(() -> {
110+
QueryProcessor.process(String.format("CREATE TRIGGER no_trigger_test ON %s.%s USING 'org.apache.cassandra.triggers.Noexisttrigger' ", ksName, cfName), ConsistencyLevel.ONE);
111+
})
112+
.isInstanceOf(InvalidRequestException.class)
113+
.hasMessageContaining("Trigger class 'org.apache.cassandra.triggers.Noexisttrigger' couldn't be loaded during validation.");
114+
}
115+
104116
@Test
105117
public void testTriggersPolicy()
106118
{

0 commit comments

Comments
 (0)