AQLPlus Refactoring 1: add a new rule controller
- Add a new rule controller that is to be used for FuzzyJoinRuleCollection.
If the first rule is not fired in the first iteration, no additional rules
in the given collection will be checked. If the first rule is fired, then
it behaves like SequentialFixpointRuleController - run rules sequentially
until one iteration over all rules produces no change.
- This rule controller is added to make sure that fuzzy-join framework does
not interfere with non-fuzzy-join queries.
Change-Id: I2742e891339e5aba37a00f77d7f18cb3c09bcfe2
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1432
Reviewed-by: Yingyi Bu <buyingyi@gmail.com>
Sonar-Qube: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
BAD: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
diff --git a/hyracks-fullstack/algebricks/algebricks-common/src/main/java/org/apache/hyracks/algebricks/common/exceptions/AlgebricksException.java b/hyracks-fullstack/algebricks/algebricks-common/src/main/java/org/apache/hyracks/algebricks/common/exceptions/AlgebricksException.java
index 6e53e2a..92bc076 100644
--- a/hyracks-fullstack/algebricks/algebricks-common/src/main/java/org/apache/hyracks/algebricks/common/exceptions/AlgebricksException.java
+++ b/hyracks-fullstack/algebricks/algebricks-common/src/main/java/org/apache/hyracks/algebricks/common/exceptions/AlgebricksException.java
@@ -20,6 +20,7 @@
import java.io.Serializable;
+import org.apache.hyracks.api.exceptions.ErrorCode;
import org.apache.hyracks.api.util.ErrorMessageUtil;
public class AlgebricksException extends Exception {
@@ -41,6 +42,10 @@
this.params = params;
}
+ public static AlgebricksException create(int errorCode, Serializable... params) {
+ return new AlgebricksException(ErrorCode.HYRACKS, errorCode, ErrorCode.getErrorMessage(errorCode), params);
+ }
+
public AlgebricksException(String message) {
this(ErrorMessageUtil.NONE, UNKNOWN, message, null, null);
}
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/pom.xml b/hyracks-fullstack/algebricks/algebricks-compiler/pom.xml
index 951fe0f..505b268 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/pom.xml
+++ b/hyracks-fullstack/algebricks/algebricks-compiler/pom.xml
@@ -66,5 +66,34 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.10.19</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-api-mockito</artifactId>
+ <version>1.6.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-core</artifactId>
+ <version>1.6.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-module-junit4</artifactId>
+ <version>1.6.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/rewriter/rulecontrollers/SequentialFirstRuleCheckFixpointRuleController.java b/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/rewriter/rulecontrollers/SequentialFirstRuleCheckFixpointRuleController.java
new file mode 100644
index 0000000..9446756
--- /dev/null
+++ b/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/rewriter/rulecontrollers/SequentialFirstRuleCheckFixpointRuleController.java
@@ -0,0 +1,91 @@
+/*
+ * 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.hyracks.algebricks.compiler.rewriter.rulecontrollers;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.rewriter.base.AbstractRuleController;
+import org.apache.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
+import org.apache.hyracks.api.exceptions.ErrorCode;
+
+/**
+ * If the first rule in the given collection is fired during the first iteration, it also runs the other rules
+ * sequentially (round-robin) until one iteration over all rules produces no change. Except the case where the first
+ * rule in the first iteration fails, all rules will be checked for each iteration.
+ * An example scenario:
+ * Suppose there are three rules - R1, R2, and R3.
+ * During the first iteration, if R1, the first rule, is not fired, then R2 and R3 will not be checked.
+ * If R1, the first rule, is fired, then R2 and R3 will be checked. Since the first iteration returns at least one true
+ * (the first rule), there will be an another iteration. In the second iteration, we don't care whether R1 is fired or
+ * not. This enforcement of 'first rule returns true' check is only executed in the first iteration.
+ * So, if any of rules in the collection (R1, R2, and R3) is fired, then there will be another iteration(s) until
+ * an iteration doesn't produce any change (no true from all rules).
+ */
+public class SequentialFirstRuleCheckFixpointRuleController extends AbstractRuleController {
+
+ private boolean fullDfs;
+
+ public SequentialFirstRuleCheckFixpointRuleController(boolean fullDfs) {
+ super();
+ this.fullDfs = fullDfs;
+ }
+
+ @Override
+ public boolean rewriteWithRuleCollection(Mutable<ILogicalOperator> root,
+ Collection<IAlgebraicRewriteRule> ruleCollection) throws AlgebricksException {
+ List<IAlgebraicRewriteRule> rules;
+
+ // This rule controller can only be applied for a list since it needs to enforce the "first" rule check.
+ if (ruleCollection instanceof List) {
+ rules = (List<IAlgebraicRewriteRule>) ruleCollection;
+ } else {
+ throw AlgebricksException.create(ErrorCode.COMPILATION_RULECOLLECTION_NOT_INSTANCE_OF_LIST,
+ this.getClass().getName());
+ }
+
+ if (rules.isEmpty()) {
+ return false;
+ }
+
+ boolean anyRuleFired = false;
+ boolean anyChange;
+ boolean firstRuleChecked = false;
+ do {
+ anyChange = false;
+ for (int i = 0; i < rules.size(); i++) {
+ boolean ruleFired = rewriteOperatorRef(root, rules.get(i), true, fullDfs);
+ // If the first rule returns false in the first iteration, stops applying the rules at all.
+ if (!firstRuleChecked && i == 0 && !ruleFired) {
+ return ruleFired;
+ }
+ if (ruleFired) {
+ anyChange = true;
+ anyRuleFired = true;
+ }
+ }
+ firstRuleChecked = true;
+ } while (anyChange);
+
+ return anyRuleFired;
+ }
+}
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/test/java/org/apache/hyracks/algebricks/compiler/rewriter/rulecontrollers/SequentialFirstRuleCheckFixpointRuleControllerTest.java b/hyracks-fullstack/algebricks/algebricks-compiler/src/test/java/org/apache/hyracks/algebricks/compiler/rewriter/rulecontrollers/SequentialFirstRuleCheckFixpointRuleControllerTest.java
new file mode 100644
index 0000000..339cb15
--- /dev/null
+++ b/hyracks-fullstack/algebricks/algebricks-compiler/src/test/java/org/apache/hyracks/algebricks/compiler/rewriter/rulecontrollers/SequentialFirstRuleCheckFixpointRuleControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.hyracks.algebricks.compiler.rewriter.rulecontrollers;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
+import org.apache.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ SequentialFirstRuleCheckFixpointRuleController.class, AbstractLogicalOperator.class })
+public class SequentialFirstRuleCheckFixpointRuleControllerTest {
+
+ @Test
+ public void testRewriteWithRuleCollection() throws Exception {
+ SequentialFirstRuleCheckFixpointRuleController ruleController =
+ new SequentialFirstRuleCheckFixpointRuleController(true);
+
+ // Specifies three rules - R1, R2, and R3.
+ IAlgebraicRewriteRule firstRule = mock(IAlgebraicRewriteRule.class);
+ IAlgebraicRewriteRule secondRule = mock(IAlgebraicRewriteRule.class);
+ IAlgebraicRewriteRule thirdRule = mock(IAlgebraicRewriteRule.class);
+ List<IAlgebraicRewriteRule> ruleList = new LinkedList<>();
+ ruleList.add(firstRule);
+ ruleList.add(secondRule);
+ ruleList.add(thirdRule);
+
+ @SuppressWarnings("unchecked")
+ Mutable<ILogicalOperator> root = PowerMockito.mock(Mutable.class);
+ AbstractLogicalOperator rootOp = PowerMockito.mock(AbstractLogicalOperator.class);
+ List<Mutable<ILogicalOperator>> emptyList = new ArrayList<>();
+ PowerMockito.when(root.getValue()).thenReturn(rootOp);
+ PowerMockito.when(rootOp.getInputs()).thenReturn(emptyList);
+
+ // Case 1: the first rule returns true in the first iteration.
+ // Iteration1: R1 true, R2 false, R3 false
+ // Iteration2: R1 false, R2 false, R3 false
+ PowerMockito.when(firstRule.rewritePre(any(), any())).thenReturn(true).thenReturn(false);
+ PowerMockito.when(secondRule.rewritePre(any(), any())).thenReturn(false);
+ PowerMockito.when(thirdRule.rewritePre(any(), any())).thenReturn(false);
+ ruleController.rewriteWithRuleCollection(root, ruleList);
+ // The count should be two for all rules.
+ verify(firstRule, times(2)).rewritePre(any(), any());
+ verify(secondRule, times(2)).rewritePre(any(), any());
+ verify(thirdRule, times(2)).rewritePre(any(), any());
+
+ // Case 2: the first rule returns false in the first iteration.
+ // Iteration1: R1 false (R2 and R3 should not be invoked.)
+ reset(firstRule);
+ reset(secondRule);
+ reset(thirdRule);
+ PowerMockito.when(firstRule.rewritePre(any(), any())).thenReturn(false);
+ PowerMockito.when(secondRule.rewritePre(any(), any())).thenReturn(true);
+ PowerMockito.when(thirdRule.rewritePre(any(), any())).thenReturn(true);
+ ruleController.rewriteWithRuleCollection(root, ruleList);
+ // The count should be one for the first rule.
+ verify(firstRule, times(1)).rewritePre(any(), any());
+ verify(secondRule, times(0)).rewritePre(any(), any());
+ verify(thirdRule, times(0)).rewritePre(any(), any());
+
+ // Case 3: a mixture of returning true/false.
+ // Iteration1: R1 true, R2 true, R3 false
+ // Iteration2: R1 false, R2 true, R3 false
+ // Iteration3: R1 false, R2 false, R3 false
+ // So, the iteration should be stopped after the iteration 3.
+ reset(firstRule);
+ reset(secondRule);
+ reset(thirdRule);
+ PowerMockito.when(firstRule.rewritePre(any(), any())).thenReturn(true).thenReturn(false);
+ PowerMockito.when(secondRule.rewritePre(any(), any())).thenReturn(true).thenReturn(true).thenReturn(false);
+ PowerMockito.when(thirdRule.rewritePre(any(), any())).thenReturn(false);
+ ruleController.rewriteWithRuleCollection(root, ruleList);
+ // The count should be three for all rules.
+ verify(firstRule, times(3)).rewritePre(any(), any());
+ verify(secondRule, times(3)).rewritePre(any(), any());
+ verify(thirdRule, times(3)).rewritePre(any(), any());
+ }
+
+}
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java
index 888f82a..5a67188 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/exceptions/ErrorCode.java
@@ -25,7 +25,10 @@
import org.apache.hyracks.api.util.ErrorMessageUtil;
/**
- * A registry of runtime error codes
+ * A registry of runtime/compile error codes
+ * Error code:
+ * 0 --- 999: runtime errors
+ * 1000 ---- 1999: compilation errors
*/
public class ErrorCode {
private static final String RESOURCE_PATH = "errormsg" + File.separator + "en.properties";
@@ -36,6 +39,7 @@
public static final int FAILURE_ON_NODE = 3;
public static final int RUNTIME_FILE_WITH_ABSOULTE_PATH_NOT_WITHIN_ANY_IO_DEVICE = 4;
public static final int RUNTIME_FULLTEXT_PHRASE_FOUND = 5;
+ public static final int COMPILATION_RULECOLLECTION_NOT_INSTANCE_OF_LIST = 1001;
// Loads the map that maps error codes to error message templates.
private static Map<Integer, String> errorMessageMap = null;
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties b/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties
index 8ebafef..52367ee 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/resources/errormsg/en.properties
@@ -17,7 +17,9 @@
# under the License.
#
-1=Unsupported operation %1$s in %2$s operator
-2=Error in processing tuple %1$s in a frame
-4=The file with absolute path %1$s is not within any of the current IO devices
-5=Phrase search in Full-text is not supported. An expression should include only one word
\ No newline at end of file
+1 = Unsupported operation %1$s in %2$s operator
+2 = Error in processing tuple %1$s in a frame
+4 = The file with absolute path %1$s is not within any of the current IO devices
+5 = Phrase search in Full-text is not supported. An expression should include only one word
+
+1001 = The given rule collection %1$s is not an instance of the List class.
\ No newline at end of file