diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/performance/AvoidDmlStatementsInLoopsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/performance/AvoidDmlStatementsInLoopsRule.java
new file mode 100644
index 0000000000..373757216a
--- /dev/null
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/performance/AvoidDmlStatementsInLoopsRule.java
@@ -0,0 +1,85 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+package net.sourceforge.pmd.lang.apex.rule.performance;
+
+import net.sourceforge.pmd.lang.ast.AbstractNode;
+import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.apex.ast.ASTDmlDeleteStatement;
+import net.sourceforge.pmd.lang.apex.ast.ASTDmlInsertStatement;
+import net.sourceforge.pmd.lang.apex.ast.ASTDmlMergeStatement;
+import net.sourceforge.pmd.lang.apex.ast.ASTDmlUndeleteStatement;
+import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpdateStatement;
+import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpsertStatement;
+import net.sourceforge.pmd.lang.apex.ast.ASTDoLoopStatement;
+import net.sourceforge.pmd.lang.apex.ast.ASTForLoopStatement;
+import net.sourceforge.pmd.lang.apex.ast.ASTForEachStatement;
+import net.sourceforge.pmd.lang.apex.ast.ASTWhileLoopStatement;
+import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
+
+public class AvoidDmlStatementsInLoopsRule extends AbstractApexRule {
+
+ public AvoidDmlStatementsInLoopsRule() {
+ setProperty(CODECLIMATE_CATEGORIES, new String[]{ "Performance" });
+ // Note: Often more complicated as just moving the SOQL a few lines. Involves Maps...
+ setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 150);
+ setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false);
+ }
+
+ @Override
+ public Object visit(ASTDmlDeleteStatement node, Object data) {
+ if (insideLoop(node)) {
+ addViolation(data, node);
+ }
+ return data;
+ }
+ @Override
+ public Object visit(ASTDmlInsertStatement node, Object data) {
+ if (insideLoop(node)) {
+ addViolation(data, node);
+ }
+ return data;
+ }
+ @Override
+ public Object visit(ASTDmlMergeStatement node, Object data) {
+ if (insideLoop(node)) {
+ addViolation(data, node);
+ }
+ return data;
+ }
+ @Override
+ public Object visit(ASTDmlUndeleteStatement node, Object data) {
+ if (insideLoop(node)) {
+ addViolation(data, node);
+ }
+ return data;
+ }
+ @Override
+ public Object visit(ASTDmlUpdateStatement node, Object data) {
+ if (insideLoop(node)) {
+ addViolation(data, node);
+ }
+ return data;
+ }
+ @Override
+ public Object visit(ASTDmlUpsertStatement node, Object data) {
+ if (insideLoop(node)) {
+ addViolation(data, node);
+ }
+ return data;
+ }
+
+ private boolean insideLoop(AbstractNode node) {
+ Node n = node.jjtGetParent();
+
+ while (n != null) {
+ if (n instanceof ASTDoLoopStatement || n instanceof ASTWhileLoopStatement
+ || n instanceof ASTForLoopStatement || n instanceof ASTForEachStatement) {
+ return true;
+ }
+ n = n.jjtGetParent();
+ }
+
+ return false;
+ }
+}
diff --git a/pmd-apex/src/main/resources/rulesets/apex/performance.xml b/pmd-apex/src/main/resources/rulesets/apex/performance.xml
index 85d70a6ab0..3699dfe0dd 100644
--- a/pmd-apex/src/main/resources/rulesets/apex/performance.xml
+++ b/pmd-apex/src/main/resources/rulesets/apex/performance.xml
@@ -25,6 +25,28 @@ New objects created within loops should be checked to see if they can created ou
}
}
}
+]]>
+
+
+
+
+ Avoid DML statements inside loops to avoid hitting the DML governor limit. Instead, try to batch up the data into a list and invoke your DML once on that list of data outside the loop.
+ 3
+
+
diff --git a/pmd-apex/src/main/resources/rulesets/apex/ruleset.xml b/pmd-apex/src/main/resources/rulesets/apex/ruleset.xml
index 4f360cf949..6f817c148a 100644
--- a/pmd-apex/src/main/resources/rulesets/apex/ruleset.xml
+++ b/pmd-apex/src/main/resources/rulesets/apex/ruleset.xml
@@ -136,6 +136,17 @@
+
+ 3
+
+
+
+
+
+
+
+
+
3
diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/performance/PerformanceRulesTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/performance/PerformanceRulesTest.java
index cada0231e0..e1f12c5038 100644
--- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/performance/PerformanceRulesTest.java
+++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/performance/PerformanceRulesTest.java
@@ -12,5 +12,6 @@ public class PerformanceRulesTest extends SimpleAggregatorTst {
@Override
public void setUp() {
addRule(RULESET, "AvoidSoqlInLoops");
+ addRule(RULESET, "AvoidDmlStatementsInLoops");
}
}
diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/performance/xml/AvoidDmlStatementsInLoops.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/performance/xml/AvoidDmlStatementsInLoops.xml
new file mode 100644
index 0000000000..00921ba3d5
--- /dev/null
+++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/performance/xml/AvoidDmlStatementsInLoops.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+ Problematic Dml Statement in for each
+ 1
+ {1,2}) {
+ Account account = new Account();
+ insert account;
+ }
+ }
+}
+ ]]>
+
+
+
+ Problematic Dml Statement in for loop
+ 1
+
+
+
+
+ Problematic Dml Statement in While loop
+ 1
+
+
+
+
+ Problematic Dml Statement in do loop
+ 1
+
+
+
+
+ Best Practice: Batch up data into a list and invoke your DML once on that list of data.
+ 0
+ accounts = new List();
+
+ while(true) {
+ Account account = new Account();
+ accounts.add(account);
+ }
+
+ insert accounts;
+ }
+}
+ ]]>
+
+
+