├── .gitignore ├── LICENSE ├── README.md ├── pom.xml ├── src ├── main │ └── java │ │ ├── AlgeNode │ │ ├── AggNode.java │ │ ├── AlgeNode.java │ │ ├── AlgeNodeHelper.java │ │ ├── EmptyNode.java │ │ ├── SPJNode.java │ │ ├── SymbolicAggPair.java │ │ ├── TableNode.java │ │ └── UnionNode.java │ │ ├── AlgeNodeParser │ │ ├── AggParser.java │ │ ├── AlgeNodeParser.java │ │ ├── AlgeNodeParserPair.java │ │ ├── FilterParser.java │ │ ├── JoinParser.java │ │ ├── ProjectParser.java │ │ ├── TableParser.java │ │ └── UnionParser.java │ │ ├── AlgeRule │ │ ├── AggregateMerge.java │ │ ├── AlgeRule.java │ │ ├── AlgeRuleBase.java │ │ ├── CleanEmpty.java │ │ ├── ConditionPushAgg.java │ │ ├── DummyRule.java │ │ ├── JoinToProject.java │ │ └── SPJ2Empty.java │ │ ├── RexNodeHelper │ │ ├── NotIn.java │ │ └── RexNodeHelper.java │ │ ├── SymbolicRexNode │ │ ├── ArithmeticExpr.java │ │ ├── ArithmeticPredicate.java │ │ ├── BoolPredicate.java │ │ ├── CaseNode.java │ │ ├── Constant.java │ │ ├── DumpRexNode.java │ │ ├── NullPredicate.java │ │ ├── RexInputRefConstraints.java │ │ ├── RexNodeBase.java │ │ ├── RexNodeConverter.java │ │ ├── RexNodeUtility.java │ │ ├── RexNotIn.java │ │ ├── SymbolicColumn.java │ │ └── UserDeFun.java │ │ └── Z3Helper │ │ └── z3Utility.java └── test │ └── java │ ├── SimpleQueryTests │ ├── JoinEqualOuterJoin.java │ ├── SimpleAnalysis.java │ ├── SimpleEmptyTable.java │ ├── SimpleLeftOuterJoin.java │ ├── SingleAnalysis.java │ ├── simpleAgg.java │ ├── simpleExists.java │ ├── simpleFilter.java │ ├── simpleJoin.java │ ├── simpleParser.java │ ├── simpleTest.java │ ├── tableSchema │ │ ├── ACCOUNT.java │ │ ├── BONUS.java │ │ ├── DEPT.java │ │ └── EMP.java │ └── testOftest.java │ └── z3Test1.java └── testData └── calcite_tests.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQL Query Equivalence 2 | A tool for checking equivalence of SQL queries. 3 | 4 | # z3 update 5 | needs to build on z3 4.6 version: 6 | github link: https://github.com/Z3Prover/z3/releases 7 | 8 | # test case 9 | 1. check SimpleTest in test/java/SimpleQuery/Tests. 10 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | GT 8 | equitas 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 8 17 | 8 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.apache.calcite 26 | calcite-core 27 | 1.19.0 28 | 29 | 30 | org.hamcrest 31 | hamcrest 32 | 2.1 33 | test 34 | 35 | 36 | com.google.code.gson 37 | gson 38 | 2.8.5 39 | 40 | 41 | junit 42 | junit 43 | 4.12 44 | test 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/AlgeNode/AggNode.java: -------------------------------------------------------------------------------- 1 | package AlgeNode; 2 | 3 | import SymbolicRexNode.SymbolicColumn; 4 | import Z3Helper.z3Utility; 5 | import com.microsoft.z3.BoolExpr; 6 | import com.microsoft.z3.Context; 7 | import org.apache.calcite.rel.core.AggregateCall; 8 | import org.apache.calcite.rel.type.RelDataType; 9 | import org.apache.calcite.rex.RexInputRef; 10 | import org.apache.calcite.rex.RexNode; 11 | import org.apache.calcite.sql.SqlKind; 12 | 13 | import java.util.*; 14 | 15 | public class AggNode extends AlgeNode{ 16 | static public SqlKind[] distinctInsensitive = {SqlKind.MAX, SqlKind.MIN}; 17 | static public SqlKind[] appendableAgg = {SqlKind.MAX, SqlKind.MIN,SqlKind.SUM,SqlKind.COUNT}; 18 | private List groupByList; 19 | private List aggregateCallList; 20 | public AggNode(List groupByList, List aggregateCallList, List inputTypes, AlgeNode input, Context z3Context){ 21 | List inputs = new ArrayList<>(); 22 | inputs.add(input); 23 | List outputExpr = new ArrayList<>(); 24 | for(int i=0;i()); 30 | 31 | this.groupByList = groupByList; 32 | this.aggregateCallList = aggregateCallList; 33 | 34 | } 35 | 36 | public AlgeNode getInput(){ 37 | return this.getInputs().get(0); 38 | } 39 | 40 | public void setAggregateCallList(List aggregateCallList){ 41 | this.aggregateCallList = aggregateCallList; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | StringBuilder result = new StringBuilder("Agg Node: \n Group By:("); 47 | for (Integer integer : groupByList) { 48 | result.append(integer).append(","); 49 | } 50 | result.append(")\n AggCallSet: ("); 51 | for (AggregateCall aggregateCall : aggregateCallList) { 52 | result.append(aggregateCall.toString()).append(","); 53 | } 54 | result.append(")\n").append(super.toString()); 55 | return result.toString(); 56 | } 57 | 58 | public boolean constructQPSR (AlgeNode node) { 59 | if (node instanceof AggNode) { 60 | AggNode aggNode = (AggNode) node; 61 | if (aggNode.getInput() instanceof EmptyNode) { 62 | if (this.getInput() instanceof EmptyNode){ 63 | this.emptyTableSymbolicColumn(); 64 | aggNode.emptyTableSymbolicColumn(); 65 | return true; 66 | } 67 | } 68 | if (this.isCartesianEq(aggNode)){ 69 | List symbolicAggPairs = new ArrayList<>(); 70 | this.constructSymbolicColumns(symbolicAggPairs); 71 | aggNode.constructSymbolicColumns(symbolicAggPairs); 72 | return true; 73 | } 74 | } 75 | return false; 76 | } 77 | 78 | public void emptyTableSymbolicColumn(){ 79 | this.symbolicColumns = new ArrayList<>(); 80 | for (AggregateCall aggregateCall : this.aggregateCallList){ 81 | if (aggregateCall.getAggregation().getKind().equals(SqlKind.COUNT)){ 82 | SymbolicColumn value0 = new SymbolicColumn(z3Context.mkInt(0),z3Context.mkFalse(),z3Context); 83 | symbolicColumns.add(value0); 84 | }else{ 85 | SymbolicColumn nullValue = new SymbolicColumn(z3Utility.mkDumpValue(aggregateCall.getType(),z3Context),z3Context.mkTrue(),z3Context); 86 | symbolicColumns.add(nullValue); 87 | } 88 | } 89 | } 90 | 91 | public boolean isCartesianEq(AggNode aggNode) { 92 | if (groupByEq(aggNode)) { 93 | return true; 94 | } 95 | return false; 96 | } 97 | 98 | private boolean groupByEq(AggNode node){ 99 | AlgeNode input1 = this.getInput(); 100 | AlgeNode input2 = node.getInput(); 101 | if(input1.constructQPSR(input2)){ 102 | this.setVariableConstraints(); 103 | node.setVariableConstraints(); 104 | return groupByColumnBijective(node); 105 | } 106 | return false; 107 | } 108 | 109 | public void constructSymbolicColumns(List symbolicAggPairs){ 110 | this.symbolicColumns = new ArrayList<>(); 111 | this.symbolicColumns.addAll(this.getSymbolicGroupByColumns()); 112 | this.symbolicColumns.addAll(this.constructSymbolicAggCalls(symbolicAggPairs)); 113 | } 114 | 115 | private List constructSymbolicAggCalls(List symbolicAggPairs){ 116 | List symbolicAggCalls = new ArrayList<>(); 117 | AlgeNode input = this.getInput(); 118 | for (AggregateCall aggCall : this.aggregateCallList) { 119 | boolean findMatch = symbolicAggPairs.stream().anyMatch( 120 | (SymbolicAggPair pair) -> { 121 | if (pair.isEqualAggCall(input, aggCall)) { 122 | symbolicAggCalls.add(pair.getSymbolicColumn()); 123 | return true; 124 | } else { 125 | return false; 126 | }}); 127 | if (!findMatch) { 128 | SymbolicColumn newColumn = SymbolicColumn.mkNewSymbolicColumn(z3Context, aggCall.getType()); 129 | symbolicAggCalls.add(newColumn); 130 | symbolicAggPairs.add(new SymbolicAggPair(aggCall, newColumn, input)); 131 | } 132 | } 133 | return symbolicAggCalls; 134 | } 135 | 136 | public List getGroupByList(){ 137 | return this.groupByList; 138 | } 139 | 140 | public List getGroupByVariables() { 141 | return this.outputExpr.subList(0,this.groupByList.size()); 142 | } 143 | 144 | public List getAggregateCallList(){ 145 | return this.aggregateCallList; 146 | } 147 | 148 | public List getSymbolicGroupByColumns(){ 149 | List symbolicGroupByColumns = new ArrayList<>(); 150 | for (int index : this.groupByList) { 151 | symbolicGroupByColumns.add(this.getInput().getSymbolicColumns().get(index)); 152 | } 153 | return symbolicGroupByColumns; 154 | } 155 | 156 | private boolean groupByColumnBijective(AggNode node){ 157 | List groupByColumn1 = this.getSymbolicGroupByColumns(); 158 | List groupByColumn2 = node.getSymbolicGroupByColumns(); 159 | if(groupByColumn1.isEmpty() && groupByColumn2.isEmpty()){ 160 | return true; 161 | } 162 | BoolExpr env = constructFreshEnv(node); 163 | return groupBySymbolicDecide(env,groupByColumn1, groupByColumn2); 164 | 165 | } 166 | 167 | private boolean groupBySymbolicDecide(BoolExpr env, List groupByColumn1, List groupByColumn2) { 168 | BoolExpr eq1 = freshEq(groupByColumn1); 169 | BoolExpr eq2 = freshEq(groupByColumn2); 170 | BoolExpr Eq1DecideEq2 = z3Context.mkAnd(env,eq1,z3Context.mkNot(eq2)); 171 | if(z3Utility.isUnsat(Eq1DecideEq2,z3Context)){ 172 | BoolExpr Eq2DecideEq1 = z3Context.mkAnd(env,eq2,z3Context.mkNot(eq1)); 173 | return z3Utility.isUnsat(Eq2DecideEq1,z3Context); 174 | } 175 | return false; 176 | } 177 | 178 | private BoolExpr freshEq(List groupByColumn){ 179 | List freshColumn = SymbolicColumn.constructFreshColumns(groupByColumn,z3Context); 180 | BoolExpr[] columnEqs1 = new BoolExpr[groupByColumn.size()]; 181 | for(int i=0;i columnTypes = new ArrayList<>(); 198 | for (RexNode outputExpr : this.outputExpr){ 199 | columnTypes.add(outputExpr.getType()); 200 | } 201 | List newGroupByList = new ArrayList<>(); 202 | for (Integer value : this.groupByList){ 203 | int index = value; 204 | newGroupByList.add(index); 205 | } 206 | List newAggregateCallList = new ArrayList<>(); 207 | for (AggregateCall oldCall : this.aggregateCallList){ 208 | List newArgList = new ArrayList<>(); 209 | for (Integer oldArg : oldCall.getArgList()){ 210 | int arg = oldArg; 211 | newArgList.add(arg); 212 | } 213 | AggregateCall newCall = oldCall.copy(newArgList,oldCall.filterArg,oldCall.collation); 214 | newAggregateCallList.add(newCall); 215 | } 216 | return (new AggNode(newGroupByList,newAggregateCallList,columnTypes,newInput,this.z3Context)); 217 | } 218 | } -------------------------------------------------------------------------------- /src/main/java/AlgeNode/AlgeNode.java: -------------------------------------------------------------------------------- 1 | package AlgeNode; 2 | 3 | import RexNodeHelper.NotIn; 4 | import SymbolicRexNode.SymbolicColumn; 5 | import Z3Helper.z3Utility; 6 | import com.microsoft.z3.BoolExpr; 7 | import com.microsoft.z3.Context; 8 | import org.apache.calcite.rel.type.RelDataType; 9 | import org.apache.calcite.rex.RexNode; 10 | 11 | import java.util.*; 12 | 13 | abstract public class AlgeNode { 14 | protected Context z3Context; 15 | protected List inputTypes; 16 | protected List inputs; 17 | protected List outputExpr; 18 | protected Set conditions; 19 | 20 | protected List symbolicColumns; 21 | protected SymbolicColumn symbolicCondition; 22 | 23 | protected BoolExpr variableConstraints; 24 | 25 | private boolean rewritable; 26 | 27 | 28 | protected void setBasicFields(Context z3Context, List inputTypes, List inputs, List outputExpr, Set conditions){ 29 | this.z3Context = z3Context; 30 | this.inputTypes = inputTypes; 31 | this.inputs = inputs; 32 | this.outputExpr = outputExpr; 33 | this.conditions = conditions; 34 | this.variableConstraints = z3Context.mkTrue(); 35 | this.rewritable = true; 36 | } 37 | 38 | public Context getZ3Context() { return this.z3Context; } 39 | 40 | // get basic output types 41 | public List getInputTypes(){ 42 | return this.inputTypes; 43 | } 44 | 45 | // get the algebraic nodes inputs 46 | public List getInputs(){ 47 | return this.inputs; 48 | } 49 | 50 | 51 | public void setInputs(List inputs){ 52 | this.inputs = inputs; 53 | } 54 | // set the output expr 55 | public void setOutputExpr(List outputExpr) { 56 | this.outputExpr = outputExpr; 57 | } 58 | 59 | // return the output expression 60 | public List getOutputExpr(){ 61 | return this.outputExpr; 62 | } 63 | 64 | // return the set of filter conditions 65 | public Set getConditions(){ 66 | return this.conditions; 67 | } 68 | 69 | //set the filter conditions 70 | public void setConditions(Set conditions){ 71 | this.conditions = conditions; 72 | } 73 | 74 | // add one condition into the filter condition 75 | public void addCondition(RexNode condition) { 76 | this.conditions.add(condition); 77 | } 78 | 79 | public void addConditions(Collection conditions) { 80 | this.conditions.addAll(conditions); 81 | } 82 | 83 | // return the symbolic output tuple 84 | public List getSymbolicColumns(){ 85 | return this.symbolicColumns; 86 | } 87 | 88 | // check if two nodes symbolic outputs are equivalent with default match 89 | public boolean checkSymbolicOutput(AlgeNode node){ 90 | if(this == node){ 91 | return true; 92 | } 93 | if(this.getOutputExpr().isEmpty() && node.getOutputExpr().isEmpty()){ 94 | return true; 95 | } 96 | List symbolicOutputs1 = this.getSymbolicColumns(); 97 | List symbolicOutputs2 = node.getSymbolicColumns(); 98 | boolean result = z3Utility.symbolicOutputEqual(buildOutputEnv(node),symbolicOutputs1,symbolicOutputs2,z3Context); 99 | return result; 100 | } 101 | 102 | public boolean checkSymbolicOutput(AlgeNode node, Map columnPairs){ 103 | Map simplifyColumnPairs = eliminateMatches(node,columnPairs); 104 | if(simplifyColumnPairs.isEmpty()){ 105 | return true; 106 | } 107 | 108 | List symbolicTuple1 = new ArrayList<>(); 109 | List symbolicTuple2 = new ArrayList<>(); 110 | List symbolicOutputs1 = this.getSymbolicColumns(); 111 | List symbolicOutputs2 = node.getSymbolicColumns(); 112 | 113 | for(Map.Entry columnPair:simplifyColumnPairs.entrySet()){ 114 | symbolicTuple1.add(symbolicOutputs1.get(columnPair.getKey())); 115 | symbolicTuple2.add(symbolicOutputs2.get(columnPair.getValue())); 116 | } 117 | 118 | return z3Utility.symbolicOutputEqual(buildOutputEnv(node),symbolicTuple1,symbolicTuple2,z3Context); 119 | } 120 | 121 | private Map eliminateMatches(AlgeNode node,Map columnPairs){ 122 | if(this == node) { 123 | Map newColumnPairs = new HashMap<>(); 124 | for (Map.Entry columnPair : columnPairs.entrySet()) { 125 | int key = columnPair.getKey(); 126 | int value = columnPair.getValue(); 127 | if (key != value) { 128 | newColumnPairs.put(key, value); 129 | } 130 | } 131 | return newColumnPairs; 132 | }else{ 133 | return columnPairs; 134 | } 135 | } 136 | 137 | private BoolExpr buildOutputEnv(AlgeNode node){ 138 | List env = new ArrayList<>(); 139 | env.add(this.getVariableConstraints()); 140 | env.add(node.getVariableConstraints()); 141 | return z3Utility.mkAnd(env,z3Context); 142 | } 143 | 144 | public boolean isEq(AlgeNode node) { 145 | if(this.constructQPSR(node)){ 146 | return checkSymbolicOutput(node); 147 | } 148 | return false; 149 | } 150 | 151 | public boolean isRewritable() { 152 | return rewritable; 153 | } 154 | 155 | public void disableRewrite(){ 156 | this.rewritable = false; 157 | } 158 | public void enableRewrite(){ 159 | this.rewritable = true; 160 | } 161 | 162 | public BoolExpr getVariableConstraints () { 163 | return this.variableConstraints; 164 | } 165 | 166 | public void setVariableConstraints () { 167 | List childrenAssign = new ArrayList<>(); 168 | for (AlgeNode child : this.inputs){ 169 | childrenAssign.add(child.variableConstraints); 170 | } 171 | this.variableConstraints = z3Utility.mkAnd(childrenAssign,z3Context); 172 | } 173 | 174 | abstract public boolean constructQPSR(AlgeNode node); 175 | 176 | @Override 177 | public String toString() { 178 | StringBuilder result = new StringBuilder("outputExpr: ("); 179 | for (RexNode rexNode : outputExpr) { 180 | result.append(rexNode.toString()); 181 | } 182 | result.append(")\nConditions : ("); 183 | for(RexNode condition:conditions){ 184 | if (condition instanceof NotIn){ 185 | result.append(((NotIn) condition).print()); 186 | }else { 187 | result.append("[").append(condition.toString()).append("] , "); 188 | } 189 | } 190 | result.append(")\n inputTables:\n"); 191 | for (AlgeNode input : inputs) { 192 | result.append(input.toString()).append("\n"); 193 | } 194 | return result.toString(); 195 | } 196 | abstract public AlgeNode clone (); 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/AlgeNode/AlgeNodeHelper.java: -------------------------------------------------------------------------------- 1 | package AlgeNode; 2 | 3 | import java.util.*; 4 | 5 | public class AlgeNodeHelper { 6 | 7 | static Map constructListQPSR (List inputs1, List inputs2,boolean isEq){ 8 | if(inputs1.size()==inputs2.size()){ 9 | Map result = new HashMap<>(); 10 | if (inputs1.size() >= 100){ 11 | if (checkSimpleMatch(inputs1,inputs2,result,isEq)){ 12 | return result; 13 | } 14 | }else { 15 | if (checkInputMatch(inputs1, 0, inputs2, new HashSet<>(), result, isEq)) { 16 | return result; 17 | } 18 | } 19 | } 20 | return null; 21 | } 22 | 23 | static private boolean checkSimpleMatch(List inputs1, List inputs2,Map result,boolean isEq){ 24 | for (int i = 0; i inputs1, int index, List inputs2, Set used, Map inputMatches,boolean isEq){ 43 | if(index(),new ArrayList<>(),new ArrayList<>(),new HashSet<>()); 11 | } 12 | 13 | public boolean constructQPSR(AlgeNode node) { 14 | return (node instanceof EmptyNode); 15 | } 16 | 17 | public AlgeNode clone(){ 18 | return (new EmptyNode(this.z3Context)); 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return "EmptyTable"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/AlgeNode/SPJNode.java: -------------------------------------------------------------------------------- 1 | package AlgeNode; 2 | 3 | import RexNodeHelper.RexNodeHelper; 4 | import SymbolicRexNode.*; 5 | import Z3Helper.z3Utility; 6 | import com.microsoft.z3.BoolExpr; 7 | import com.microsoft.z3.Context; 8 | import org.apache.calcite.rel.type.RelDataType; 9 | import org.apache.calcite.rex.RexNode; 10 | 11 | import java.util.*; 12 | 13 | public class SPJNode extends AlgeNode{ 14 | private List inputSymbolicColumns; 15 | 16 | public SPJNode(List outputExpr,Set conditions, List inputs, Context z3Context){ 17 | List types = new ArrayList<>(); 18 | for(AlgeNode input: inputs){ 19 | types.addAll(input.getInputTypes()); 20 | } 21 | setBasicFields(z3Context,types,inputs,outputExpr,conditions); 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "SPJ Node: " + "\n" + super.toString(); 27 | } 28 | 29 | 30 | public boolean constructQPSR (AlgeNode node) { 31 | if (node instanceof SPJNode) { 32 | SPJNode spjNode = (SPJNode) node; 33 | if (this.isCartesianEq(spjNode)){ 34 | RexNodeUtility.reset(); 35 | this.constructSR(); 36 | spjNode.constructSR(); 37 | RexNodeUtility.reset(); 38 | return true; 39 | } 40 | } 41 | return false; 42 | } 43 | 44 | public boolean isCartesianEq(SPJNode spjNode) { 45 | Map inputMatches = checkInputMatch(spjNode); 46 | if(inputMatches!=null){ 47 | this.constructSymbolicColumns(); 48 | spjNode.constructSymbolicColumns(); 49 | this.setVariableConstraints(); 50 | spjNode.setVariableConstraints(); 51 | RexNodeUtility.reset(); 52 | boolean result = checkSymbolicCondition(spjNode); 53 | RexNodeUtility.reset(); 54 | return result; 55 | } 56 | return false; 57 | } 58 | 59 | // construct the symbolic conditions based the symbolic inputs 60 | public SymbolicColumn constructSymbolicCondition(){ 61 | List assign = new ArrayList<>(); 62 | if(this.conditions.isEmpty()){ 63 | this.symbolicCondition = new SymbolicColumn(z3Context.mkTrue(),z3Context.mkFalse(),z3Context); 64 | }else{ 65 | this.symbolicCondition = BoolPredicate.getAndNodeSymbolicColumn(this.conditions,this.inputSymbolicColumns,assign,z3Context); 66 | } 67 | assign.add(this.variableConstraints); 68 | this.variableConstraints = z3Utility.mkAnd(assign,z3Context); 69 | return this.symbolicCondition; 70 | } 71 | 72 | /** 73 | * Check if the symbolic condition for this node is logically equivalent to that of another node. 74 | */ 75 | private boolean checkSymbolicCondition(SPJNode node){ 76 | SymbolicColumn condition1 = this.constructSymbolicCondition(); 77 | SymbolicColumn condition2 = node.constructSymbolicCondition(); 78 | List env = new ArrayList<>(); 79 | env.add(this.variableConstraints); 80 | env.add(node.getVariableConstraints()); 81 | if (z3Utility.isConditionEq(env, condition1.isValueTrue(), condition2.isValueTrue(), z3Context)){ 82 | this.joinCondition(condition1.isValueTrue()); 83 | node.joinCondition(condition2.isValueTrue()); 84 | return true; 85 | } 86 | return false; 87 | } 88 | 89 | public void joinCondition(BoolExpr condition){ 90 | this.variableConstraints = z3Context.mkAnd(this.variableConstraints,condition); 91 | } 92 | 93 | public void constructSR () { 94 | List assign = new ArrayList<>(); 95 | this.symbolicColumns = new ArrayList<>(); 96 | for (RexNode rexNode : this.outputExpr) { 97 | RexNodeBase converter = RexNodeConverter.rexConstraints(this.inputSymbolicColumns, rexNode, z3Context); 98 | this.symbolicColumns.add(converter.getOutput()); 99 | assign.addAll(converter.getAssignConstrains()); 100 | } 101 | assign.add(this.variableConstraints); 102 | this.variableConstraints = z3Utility.mkAnd(assign,z3Context); 103 | } 104 | 105 | public void constructSymbolicColumns () { 106 | this.inputSymbolicColumns = new ArrayList<>(); 107 | for (AlgeNode input : this.inputs ){ 108 | for (SymbolicColumn symbolicColumn : input.getSymbolicColumns()){ 109 | this.inputSymbolicColumns.add(symbolicColumn); 110 | } 111 | } 112 | } 113 | 114 | private Map checkInputMatch(SPJNode node){ 115 | List inputs1 = this.inputs; 116 | List inputs2 = node.getInputs(); 117 | return AlgeNodeHelper.constructListQPSR(inputs1,inputs2,false); 118 | } 119 | 120 | public AlgeNode clone () { 121 | List newInputs = new ArrayList<>(); 122 | for (AlgeNode input : this.inputs){ 123 | newInputs.add(input.clone()); 124 | } 125 | List newInputExprs = new ArrayList<>(); 126 | int offSize = 0; 127 | for (AlgeNode input : newInputs){ 128 | for (RexNode inputExpr : input.getOutputExpr()){ 129 | newInputExprs.add(RexNodeHelper.addOffSize(inputExpr,offSize)); 130 | } 131 | offSize = offSize+input.getOutputExpr().size(); 132 | } 133 | List newOutputExprs = new ArrayList<>(); 134 | for (RexNode oldOutputExpr : this.outputExpr){ 135 | newOutputExprs.add(RexNodeHelper.substitute(oldOutputExpr,newInputExprs)); 136 | } 137 | Set newConditions = new HashSet<>(); 138 | for (RexNode oldCondition : this.conditions){ 139 | newConditions.add(RexNodeHelper.substitute(oldCondition,newInputExprs)); 140 | } 141 | return (new SPJNode(newOutputExprs,newConditions,newInputs,this.z3Context)); 142 | 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/AlgeNode/SymbolicAggPair.java: -------------------------------------------------------------------------------- 1 | package AlgeNode; 2 | 3 | import SymbolicRexNode.SymbolicColumn; 4 | import org.apache.calcite.rel.core.AggregateCall; 5 | import org.apache.calcite.sql.SqlAggFunction; 6 | import org.apache.calcite.sql.SqlKind; 7 | 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public class SymbolicAggPair { 13 | 14 | private AggregateCall aggCall; 15 | private SymbolicColumn symbolicColumn; 16 | private AlgeNode input; 17 | 18 | public SymbolicAggPair(AggregateCall aggCall, SymbolicColumn symbolicColumn, AlgeNode input) { 19 | this.aggCall = aggCall; 20 | this.symbolicColumn = symbolicColumn; 21 | this.input = input; 22 | } 23 | 24 | public boolean isEqualAggCall(AlgeNode cpInput, AggregateCall aggCall2) { 25 | if(isCallEqual(this.aggCall,aggCall2)){ 26 | List args1 = this.aggCall.getArgList(); 27 | List args2 = aggCall2.getArgList(); 28 | if(args1.size()==args2.size()) { 29 | return input.checkSymbolicOutput(cpInput, constructColumnPairs(args1, args2)); 30 | } 31 | } 32 | return false; 33 | } 34 | 35 | public SymbolicColumn getSymbolicColumn() { 36 | return symbolicColumn; 37 | } 38 | 39 | private Map constructColumnPairs(List args1, List args2) { 40 | Map columnPairs = new HashMap<>(); 41 | for(int i=0;i columnTypes, Context z3Context) { 14 | List columns = constructColumns(columnTypes); 15 | List emptyInputTables = new ArrayList<>(); 16 | Set emptyFilterCondition = new HashSet<>(); 17 | setBasicFields(z3Context, columnTypes, emptyInputTables, columns, emptyFilterCondition); 18 | this.name = name; 19 | } 20 | 21 | private List constructColumns (List columnTypes){ 22 | List columns = new ArrayList<>(); 23 | int count = 0; 24 | for (RelDataType columnType:columnTypes){ 25 | RexInputRef expr = new RexInputRef(count, columnType); 26 | columns.add(expr); 27 | count++; 28 | } 29 | return columns; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "Table: " + name + "\n" + super.toString(); 35 | } 36 | 37 | public String getName(){ 38 | return this.name; 39 | } 40 | 41 | public boolean constructQPSR(AlgeNode node){ 42 | if (node instanceof TableNode){ 43 | TableNode tableNode = (TableNode) node; 44 | if (this.isCartesianEq(tableNode)){ 45 | this.constructQPSR(tableNode); 46 | this.variableConstraintTrue(); 47 | tableNode.variableConstraintTrue(); 48 | return true; 49 | } 50 | } 51 | return false; 52 | } 53 | 54 | public boolean isCartesianEq(TableNode tableNode) { 55 | if (this.name.equals(tableNode.getName())) { 56 | return true; 57 | } 58 | return false; 59 | } 60 | 61 | 62 | private void constructQPSR(TableNode node){ 63 | List symbolicTuple = SymbolicColumn.constructSymbolicTuple(inputTypes,z3Context); 64 | this.setSymbolicColumns(symbolicTuple); 65 | node.setSymbolicColumns(symbolicTuple); 66 | } 67 | 68 | public void setSymbolicColumns(List symbolicTuple){ 69 | this.symbolicColumns = symbolicTuple; 70 | } 71 | 72 | public void variableConstraintTrue () { 73 | this.variableConstraints = z3Context.mkTrue(); 74 | } 75 | 76 | public AlgeNode clone () { 77 | List columnsTypes = new ArrayList<>(); 78 | for (RexNode expr : this.outputExpr ){ 79 | columnsTypes.add(expr.getType()); 80 | } 81 | return (new TableNode(this.name,columnsTypes,z3Context)); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/AlgeNode/UnionNode.java: -------------------------------------------------------------------------------- 1 | package AlgeNode; 2 | 3 | import RexNodeHelper.RexNodeHelper; 4 | import SymbolicRexNode.SymbolicColumn; 5 | import Z3Helper.z3Utility; 6 | import com.microsoft.z3.BoolExpr; 7 | import com.microsoft.z3.Context; 8 | import org.apache.calcite.rel.type.RelDataType; 9 | import org.apache.calcite.rex.RexInputRef; 10 | import org.apache.calcite.rex.RexNode; 11 | 12 | import java.util.*; 13 | 14 | public class UnionNode extends AlgeNode{ 15 | public UnionNode(List inputTables, Context z3Context, List inputTypes){ 16 | Set conditions = new HashSet<>(); 17 | List outputExpr = new ArrayList<>(); 18 | for(int i=0;i inputMatches = findEqMatches(unionNode); 38 | if (inputMatches != null){ 39 | this.constructQPSR(unionNode); 40 | return true; 41 | } 42 | } 43 | return false; 44 | } 45 | 46 | public boolean constructQPSR1 (AlgeNode node){ 47 | if (node instanceof UnionNode){ 48 | UnionNode unionNode = (UnionNode) node; 49 | Map inputMatches = findQPSRMatches(unionNode); 50 | if (inputMatches != null){ 51 | List boolPredicates = new ArrayList<>(); 52 | int count = 0; 53 | while (count leftPredicates = new ArrayList<>(); 58 | List rightPreicates = new ArrayList<>(); 59 | for (Map.Entry pairs : inputMatches.entrySet()){ 60 | leftPredicates.add(boolPredicates.get(pairs.getKey())); 61 | rightPreicates.add(boolPredicates.get(pairs.getValue())); 62 | } 63 | this.constructSR(leftPredicates); 64 | unionNode.constructSR(rightPreicates); 65 | return true; 66 | } 67 | } 68 | return false; 69 | } 70 | 71 | private void constructSR(List boolPredicates) { 72 | List outputTypes = new ArrayList<>(); 73 | for (RexNode outputExpr : this.inputs.get(0).getOutputExpr()){ 74 | outputTypes.add(outputExpr.getType()); 75 | } 76 | this.symbolicColumns = SymbolicColumn.constructSymbolicTuple(outputTypes,z3Context); 77 | List columnsEqs = new ArrayList<>(); 78 | List predicateRelations = new ArrayList<>(); 79 | for (int i=0;i variableConstraints = new ArrayList<>(); 86 | BoolExpr orColumnsEqs = z3Utility.mkAnd(columnsEqs,z3Context); 87 | BoolExpr orFalse = z3Utility.mkAnd(predicateRelations,z3Context); 88 | BoolExpr onHolds = z3Utility.mkOr(boolPredicates,z3Context); 89 | variableConstraints.add(orColumnsEqs); 90 | variableConstraints.add(orFalse); 91 | variableConstraints.add(onHolds); 92 | this.setVariableConstraints(); 93 | variableConstraints.add(this.variableConstraints); 94 | this.variableConstraints = z3Utility.mkAnd(variableConstraints,z3Context); 95 | } 96 | 97 | private BoolExpr exceptOneFalse(List boolPredicates, int index){ 98 | if (boolPredicates.size() > 1) { 99 | BoolExpr truePredicate = boolPredicates.get(index); 100 | BoolExpr[] allFalse = new BoolExpr[boolPredicates.size() - 1]; 101 | int count = 0; 102 | for (int i = 0; i < boolPredicates.size(); i++) { 103 | if (i != index){ 104 | allFalse[count] = z3Context.mkNot(boolPredicates.get(i)); 105 | count++; 106 | } 107 | } 108 | return z3Context.mkImplies(truePredicate,z3Context.mkAnd(allFalse)); 109 | }else{ 110 | return z3Context.mkTrue(); 111 | } 112 | } 113 | 114 | private void constructQPSR (UnionNode unionNode){ 115 | List outputTypes = new ArrayList<>(); 116 | for (RexNode outputExpr : this.inputs.get(0).getOutputExpr()){ 117 | outputTypes.add(outputExpr.getType()); 118 | } 119 | this.symbolicColumns = SymbolicColumn.constructSymbolicTuple(outputTypes,z3Context); 120 | unionNode.setSymbolicColumns(this.symbolicColumns); 121 | this.variableConstraintTrue(); 122 | unionNode.variableConstraintTrue(); 123 | } 124 | 125 | public void setSymbolicColumns(List symbolicTuple){ 126 | this.symbolicColumns = symbolicTuple; 127 | } 128 | 129 | public void variableConstraintTrue () { 130 | this.variableConstraints = z3Context.mkTrue(); 131 | } 132 | 133 | private Map findEqMatches(UnionNode node){ 134 | List inputs1 = this.getInputs(); 135 | List inputs2 = node.getInputs(); 136 | return AlgeNodeHelper.constructListQPSR(inputs1,inputs2,true); 137 | } 138 | 139 | private Map findQPSRMatches(UnionNode node){ 140 | List inputs1 = this.getInputs(); 141 | List inputs2 = node.getInputs(); 142 | return AlgeNodeHelper.constructListQPSR(inputs1,inputs2,false); 143 | } 144 | 145 | public AlgeNode clone () { 146 | List newInputs = new ArrayList<>(); 147 | for (AlgeNode input : this.inputs){ 148 | newInputs.add(input.clone()); 149 | } 150 | List columnTypes = new ArrayList<>(); 151 | for (RexNode outputExpr : this.outputExpr){ 152 | columnTypes.add(outputExpr.getType()); 153 | } 154 | return (new UnionNode(newInputs,this.z3Context,columnTypes)); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/AlgeNodeParser/AggParser.java: -------------------------------------------------------------------------------- 1 | package AlgeNodeParser; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNode.AggNode; 5 | import AlgeNode.TableNode; 6 | import AlgeNode.SPJNode; 7 | import AlgeRule.JoinToProject; 8 | import SymbolicRexNode.SymbolicColumn; 9 | 10 | import com.microsoft.z3.Context; 11 | 12 | import org.apache.calcite.plan.RelOptUtil; 13 | import org.apache.calcite.rel.RelNode; 14 | import org.apache.calcite.rel.core.AggregateCall; 15 | import org.apache.calcite.rel.logical.LogicalAggregate; 16 | import org.apache.calcite.rel.type.RelDataType; 17 | import org.apache.calcite.rex.RexNode; 18 | 19 | import java.util.ArrayList; 20 | import java.util.HashSet; 21 | import java.util.List; 22 | import java.util.Set; 23 | 24 | 25 | public class AggParser extends AlgeNodeParser{ 26 | public AlgeNode constructRelNode(RelNode input, Context z3Context){ 27 | LogicalAggregate aggregate = (LogicalAggregate) input; 28 | AlgeNode inputNode = AlgeNodeParserPair.constructAlgeNode(aggregate.getInput(),z3Context); 29 | if (trivialAgg(aggregate, inputNode)){ 30 | return inputNode; 31 | } 32 | List groupByList = aggregate.getGroupSet().asList(); 33 | ArrayList columnTypes = new ArrayList<>(); 34 | for(int i=0;i aggregateCallList = aggregate.getAggCallList(); 40 | for(int i=0;i groupByList = aggregate.getGroupSet().asList(); 50 | if (aggregate.getAggCallList().isEmpty()){ 51 | if (groupByList.size() == 1){ 52 | int index = groupByList.get(0); 53 | List subInputs = input.getInputs(); 54 | int count = 0; 55 | for (AlgeNode subInput : subInputs){ 56 | if (count <= index && index < count + subInput.getOutputExpr().size()){ 57 | int fixIndex = index - count; 58 | if (subInput instanceof TableNode){ 59 | String tableName = ((TableNode)subInput).getName(); 60 | if (JoinToProject.checkKeyIndex(tableName) == fixIndex){ 61 | if (subInputs.size() == 1) { 62 | return true; 63 | }else{ 64 | System.out.println(RelOptUtil.toString(aggregate)); 65 | } 66 | } 67 | } 68 | }else{ 69 | count = count + subInput.getOutputExpr().size(); 70 | } 71 | } 72 | } 73 | } 74 | return false; 75 | } 76 | 77 | static public AlgeNode distinctToAgg (AlgeNode input){ 78 | List groupByList = new ArrayList<>(); 79 | for (int i=0;i columnTypes = new ArrayList<>(); 83 | for(int i=0;i aggregateCallList = new ArrayList<>(); 87 | AggNode newAggNode = new AggNode(groupByList,aggregateCallList,columnTypes,input,input.getZ3Context()); 88 | return JoinParser.wrapBySPJ(newAggNode,input.getZ3Context()); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/AlgeNodeParser/AlgeNodeParser.java: -------------------------------------------------------------------------------- 1 | package AlgeNodeParser; 2 | 3 | import AlgeNode.AlgeNode; 4 | import com.microsoft.z3.Context; 5 | import org.apache.calcite.rel.RelNode; 6 | 7 | public abstract class AlgeNodeParser { 8 | abstract public AlgeNode constructRelNode(RelNode input, Context z3Context); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/AlgeNodeParser/AlgeNodeParserPair.java: -------------------------------------------------------------------------------- 1 | package AlgeNodeParser; 2 | 3 | import AlgeNode.AlgeNode; 4 | import com.microsoft.z3.Context; 5 | import org.apache.calcite.rel.RelNode; 6 | import org.apache.calcite.rel.core.TableScan; 7 | import org.apache.calcite.rel.logical.*; 8 | 9 | public enum AlgeNodeParserPair { 10 | Projection(LogicalProject.class,ProjectParser.class), 11 | Table(TableScan.class,TableParser.class), 12 | Filter(LogicalFilter.class,FilterParser.class), 13 | Join(LogicalJoin.class, JoinParser.class), 14 | Aggregate(LogicalAggregate.class,AggParser.class), 15 | Union(LogicalUnion.class,UnionParser.class) 16 | ; 17 | private final Class relNode; 18 | private final Class parserClass; 19 | 20 | AlgeNodeParserPair(Class relNode, Class converterClass) { 21 | this.relNode = relNode; 22 | this.parserClass = converterClass; 23 | } 24 | 25 | public Class getRelNode() { 26 | return this.relNode; 27 | } 28 | 29 | public Class getParserClass() { 30 | return this.parserClass; 31 | } 32 | 33 | public static AlgeNode constructAlgeNode(RelNode input,Context z3Context) { 34 | for (AlgeNodeParserPair parserPair : AlgeNodeParserPair.values()) { 35 | if (parserPair.getRelNode().isInstance(input)) { 36 | AlgeNodeParser parser = null; 37 | try { 38 | parser = (AlgeNodeParser) parserPair.getParserClass().getDeclaredConstructor().newInstance(); 39 | } catch (Exception e) { 40 | System.out.println("here is an exception"); 41 | System.out.println(e); 42 | } 43 | if (parser != null) { 44 | return parser.constructRelNode(input,z3Context); 45 | } 46 | } 47 | } 48 | System.out.println("this class: "+input.getClass()+" has not been handled in construct algeNode"); 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/AlgeNodeParser/FilterParser.java: -------------------------------------------------------------------------------- 1 | package AlgeNodeParser; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNode.SPJNode; 5 | import AlgeNode.UnionNode; 6 | import RexNodeHelper.RexNodeHelper; 7 | import com.microsoft.z3.Context; 8 | import org.apache.calcite.rel.RelNode; 9 | import org.apache.calcite.rel.logical.LogicalFilter; 10 | import org.apache.calcite.rex.RexCall; 11 | import org.apache.calcite.rex.RexNode; 12 | import org.apache.calcite.sql.SqlKind; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public class FilterParser extends AlgeNodeParser{ 18 | public AlgeNode constructRelNode(RelNode input, Context z3Context){ 19 | LogicalFilter filter = (LogicalFilter)input; 20 | AlgeNode inputNode = AlgeNodeParserPair.constructAlgeNode(filter.getInput(),z3Context); 21 | if (inputNode instanceof UnionNode){ 22 | return distributeCondition((UnionNode) inputNode, filter.getCondition()); 23 | } 24 | if (inputNode instanceof SPJNode){ 25 | return SPJNode(inputNode,filter.getCondition()); 26 | } 27 | else{ 28 | System.out.println("error in filter parser"+inputNode.toString()); 29 | return inputNode; 30 | } 31 | } 32 | private AlgeNode SPJNode(AlgeNode spjNode,RexNode condition){ 33 | RexNode newCondition = RexNodeHelper.substitute(condition,spjNode.getOutputExpr()); 34 | spjNode.addConditions(conjunctiveForm(newCondition)); 35 | return spjNode; 36 | } 37 | 38 | private UnionNode distributeCondition(UnionNode unionNode, RexNode condition){ 39 | for(AlgeNode input: unionNode.getInputs()){ 40 | RexNode newCondition = RexNodeHelper.substitute(condition,input.getOutputExpr()); 41 | input.addConditions(conjunctiveForm(newCondition)); 42 | } 43 | return unionNode; 44 | } 45 | 46 | static public List conjunctiveForm(RexNode condition){ 47 | if(condition instanceof RexCall){ 48 | RexCall rexCall = (RexCall) condition; 49 | if(rexCall.isA(SqlKind.AND)){ 50 | return rexCall.getOperands(); 51 | } 52 | } 53 | List result = new ArrayList<>(); 54 | result.add(condition); 55 | return result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/AlgeNodeParser/JoinParser.java: -------------------------------------------------------------------------------- 1 | package AlgeNodeParser; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNode.SPJNode; 5 | import AlgeNode.UnionNode; 6 | import AlgeNode.AggNode; 7 | import AlgeRule.AlgeRule; 8 | import RexNodeHelper.RexNodeHelper; 9 | import RexNodeHelper.NotIn; 10 | import com.microsoft.z3.Context; 11 | import org.apache.calcite.jdbc.JavaTypeFactoryImpl; 12 | import org.apache.calcite.rel.RelNode; 13 | import org.apache.calcite.rel.core.JoinRelType; 14 | import org.apache.calcite.rel.logical.LogicalJoin; 15 | import org.apache.calcite.rel.type.RelDataType; 16 | import org.apache.calcite.rel.type.RelDataTypeFactory; 17 | import org.apache.calcite.rel.type.RelDataTypeSystem; 18 | import org.apache.calcite.rex.RexBuilder; 19 | import org.apache.calcite.rex.RexCall; 20 | import org.apache.calcite.rex.RexLiteral; 21 | import org.apache.calcite.rex.RexNode; 22 | import org.apache.calcite.sql.SqlKind; 23 | 24 | import java.util.ArrayList; 25 | import java.util.HashSet; 26 | import java.util.List; 27 | import java.util.Set; 28 | 29 | public class JoinParser extends AlgeNodeParser{ 30 | static RelDataTypeFactory typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT); 31 | static RexBuilder nullBuilder = new RexBuilder(typeFactory); 32 | public AlgeNode constructRelNode(RelNode input, Context z3Context){ 33 | LogicalJoin joinNode = (LogicalJoin) input; 34 | AlgeNode leftNode = AlgeNodeParserPair.constructAlgeNode(joinNode.getLeft(),z3Context); 35 | AlgeNode rightNode = AlgeNodeParserPair.constructAlgeNode(joinNode.getRight(),z3Context); 36 | RexNode joinCondition = joinNode.getCondition(); 37 | List result = innerJoinAll(leftNode,rightNode,z3Context,joinCondition); 38 | if(joinNode.getJoinType() == JoinRelType.INNER) { 39 | return constructNode(result,z3Context); 40 | } 41 | if (joinCondition.getKind() == SqlKind.IS_TRUE) { 42 | return constructNode(result,z3Context); 43 | } 44 | if (joinCondition.getKind() == SqlKind.LITERAL) { 45 | RexLiteral maybeTrue = (RexLiteral) joinCondition; 46 | if (maybeTrue.isAlwaysTrue()){ 47 | return constructNode(result,z3Context); 48 | } 49 | } 50 | if(joinNode.getJoinType() == JoinRelType.LEFT){ 51 | result.addAll(leftJoinAll(leftNode,rightNode,joinCondition,z3Context)); 52 | return constructNode(result,z3Context); 53 | } 54 | if(joinNode.getJoinType() == JoinRelType.RIGHT){ 55 | result.addAll(rightJoinAll(leftNode,rightNode,joinCondition,z3Context)); 56 | return constructNode(result,z3Context); 57 | } 58 | if(joinNode.getJoinType() == JoinRelType.FULL){ 59 | result.addAll(leftJoinAll(leftNode,rightNode,joinCondition,z3Context)); 60 | result.addAll(rightJoinAll(leftNode,rightNode,joinCondition,z3Context)); 61 | return constructNode(result,z3Context); 62 | } 63 | else{ 64 | System.out.println("what is the join type? "+joinNode.getJoinType().toString()); 65 | System.exit(1); 66 | } 67 | return null; 68 | } 69 | 70 | private AlgeNode constructNode (List result,Context z3Context){ 71 | if (result.size() == 1) { 72 | return result.get(0); 73 | }else{ 74 | List inputTypes = new ArrayList<>(); 75 | for(RexNode column:result.get(0).getOutputExpr()){ 76 | inputTypes.add(column.getType()); 77 | } 78 | return new UnionNode(result,z3Context,inputTypes); 79 | } 80 | } 81 | 82 | private List innerJoinAll (AlgeNode leftNode, AlgeNode rightNode, Context z3Context, RexNode joinCondition) { 83 | List leftInputs = new ArrayList<>(); 84 | if (leftNode instanceof UnionNode) { 85 | leftInputs.addAll(leftNode.getInputs()); 86 | }else{ 87 | leftInputs.add(leftNode); 88 | } 89 | List rightInputs = new ArrayList<>(); 90 | if (rightNode instanceof UnionNode) { 91 | rightInputs.addAll(rightNode.getInputs()); 92 | }else{ 93 | rightInputs.add(rightNode); 94 | } 95 | List result = new ArrayList<>(); 96 | for (AlgeNode left : leftInputs) { 97 | for (AlgeNode right: rightInputs){ 98 | result.add(innerJoin(left,right,z3Context,joinCondition)); 99 | } 100 | } 101 | return result; 102 | } 103 | 104 | private SPJNode innerJoin (AlgeNode leftNode, AlgeNode rightNode ,Context z3Context, RexNode joinCondition) { 105 | 106 | // getInput tables 107 | List inputs = new ArrayList<>(); 108 | addInputs(leftNode,inputs); 109 | int offSize = 0; 110 | for (AlgeNode leftInput : inputs) { 111 | offSize = offSize+leftInput.getOutputExpr().size(); 112 | } 113 | addInputs(rightNode,inputs); 114 | 115 | // build new output expr; 116 | List newOutputExpr = new ArrayList<>(leftNode.getOutputExpr()); 117 | 118 | List rightOutputExpr = rightNode.getOutputExpr(); 119 | for (RexNode rexNode : rightOutputExpr) { 120 | newOutputExpr.add(RexNodeHelper.addOffSize(rexNode, offSize)); 121 | } 122 | 123 | // build new condition; 124 | Set newCondition = new HashSet<>(leftNode.getConditions()); 125 | 126 | Set rightConditions = rightNode.getConditions(); 127 | for(RexNode rightCondition:rightConditions){ 128 | newCondition.add(RexNodeHelper.addOffSize(rightCondition,offSize)); 129 | } 130 | RexNode newJoinCondition = RexNodeHelper.substitute(joinCondition,newOutputExpr); 131 | newCondition.add(newJoinCondition); 132 | 133 | 134 | return new SPJNode(newOutputExpr,newCondition,inputs,z3Context); 135 | } 136 | 137 | private List leftJoinAll (AlgeNode leftNode, AlgeNode rightNode,RexNode joinCondition,Context z3Context){ 138 | List leftInputs = new ArrayList<>(); 139 | if (leftNode instanceof UnionNode) { 140 | leftInputs.addAll(leftNode.getInputs()); 141 | }else{ 142 | leftInputs.add(leftNode); 143 | } 144 | List result = new ArrayList<>(); 145 | for (AlgeNode left : leftInputs) { 146 | result.add(leftJoin(left,rightNode,joinCondition,z3Context)); 147 | } 148 | return result; 149 | } 150 | 151 | private AlgeNode leftJoin(AlgeNode leftNode, AlgeNode rightNode, RexNode joinCondition,Context z3Context) { 152 | 153 | // build new output expr; 154 | 155 | List newOutputExpr = new ArrayList<>(leftNode.getOutputExpr()); 156 | 157 | List rightOutputExpr = rightNode.getOutputExpr(); 158 | for (RexNode rexNode : rightOutputExpr) { 159 | RexLiteral nullValue = nullBuilder.makeNullLiteral(rexNode.getType()); 160 | newOutputExpr.add(nullValue); 161 | } 162 | 163 | // build new condition; 164 | Set newConditions = new HashSet<>(leftNode.getConditions()); 165 | 166 | RexNode inCondition = inCondition(leftNode,rightNode,joinCondition,true); 167 | newConditions.add(inCondition); 168 | return new SPJNode(newOutputExpr,newConditions,leftNode.getInputs(),z3Context); 169 | } 170 | 171 | private List rightJoinAll (AlgeNode leftNode, AlgeNode rightNode,RexNode joinCondition,Context z3Context){ 172 | List rightInputs = new ArrayList<>(); 173 | if (rightNode instanceof UnionNode) { 174 | rightInputs.addAll(rightNode.getInputs()); 175 | }else{ 176 | rightInputs.add(rightNode); 177 | } 178 | List result = new ArrayList<>(); 179 | for (AlgeNode right: rightInputs){ 180 | result.add(rightJoin(leftNode,right,joinCondition,z3Context)); 181 | } 182 | return result; 183 | } 184 | 185 | private AlgeNode rightJoin(AlgeNode leftNode, AlgeNode rightNode, RexNode joinCondition,Context z3Context){ 186 | // build new output expr; 187 | List newOutputExpr = new ArrayList<>(); 188 | for (RexNode rexNode : leftNode.getOutputExpr()){ 189 | RexLiteral nullValue = nullBuilder.makeNullLiteral(rexNode.getType()); 190 | newOutputExpr.add(nullValue); 191 | } 192 | 193 | newOutputExpr.addAll(rightNode.getOutputExpr()); 194 | 195 | // build new condition; 196 | Set newConditions = new HashSet<>(); 197 | newConditions.addAll(rightNode.getConditions()); 198 | RexNode inCondition = inCondition(leftNode,rightNode,joinCondition,false); 199 | newConditions.add(inCondition); 200 | return new SPJNode(newOutputExpr,newConditions,rightNode.getInputs(),z3Context); 201 | 202 | } 203 | 204 | private RexNode inCondition (AlgeNode leftNode, AlgeNode rightNode, RexNode joinCondition, boolean isLeft){ 205 | List leftJoinColumns = new ArrayList<>(); 206 | List rightJoinColumns = new ArrayList<>(); 207 | separateJoinCondition(joinCondition,leftJoinColumns,rightJoinColumns,leftNode.getOutputExpr().size()); 208 | if (isLeft) { 209 | AlgeNode newRightNode = rightNode.clone(); 210 | updateNode(newRightNode, rightJoinColumns); 211 | List properLeftJoinColumns = new ArrayList<>(); 212 | for (RexNode leftJoinColumn : leftJoinColumns){ 213 | properLeftJoinColumns.add(RexNodeHelper.substitute(leftJoinColumn,leftNode.getOutputExpr())); 214 | } 215 | newRightNode = AlgeRule.normalize(newRightNode); 216 | return (new NotIn(newRightNode,properLeftJoinColumns)); 217 | }else{ 218 | AlgeNode newLeftNode = leftNode.clone(); 219 | updateNode(newLeftNode,leftJoinColumns); 220 | List properRightJoinColumns = new ArrayList<>(); 221 | for (RexNode rightJoinColumn : rightJoinColumns){ 222 | properRightJoinColumns.add(RexNodeHelper.substitute(rightJoinColumn,rightNode.getOutputExpr())); 223 | } 224 | newLeftNode = AlgeRule.normalize(newLeftNode); 225 | return (new NotIn(newLeftNode,properRightJoinColumns)); 226 | } 227 | } 228 | 229 | private void separateJoinCondition (RexNode joinCondition, List leftJoinColumns, List rightJoinColumns,int offSize){ 230 | if (joinCondition.getKind() == SqlKind.EQUALS) { 231 | RexCall callNode = (RexCall) joinCondition; 232 | leftJoinColumns.add(callNode.getOperands().get(0)); 233 | RexNode rightColumn = callNode.getOperands().get(1); 234 | rightJoinColumns.add(RexNodeHelper.minusOffSize(rightColumn,offSize)); 235 | return; 236 | } 237 | if (joinCondition.getKind() == SqlKind.AND ) { 238 | RexCall callNode = (RexCall) joinCondition; 239 | for (RexNode newJoinCondition : callNode.getOperands()){ 240 | separateJoinCondition(newJoinCondition,leftJoinColumns,rightJoinColumns,offSize); 241 | } 242 | return; 243 | } 244 | System.out.println("the SQL kind IS NOT support in separateJoinCondition:"+joinCondition.getKind()); 245 | return; 246 | } 247 | 248 | private void updateNode (AlgeNode node, List joinColumns){ 249 | if (node instanceof SPJNode){ 250 | updateSPJ((SPJNode)node,joinColumns); 251 | }else{ 252 | for (AlgeNode input : node.getInputs()){ 253 | updateSPJ((SPJNode)input,joinColumns); 254 | } 255 | } 256 | 257 | } 258 | 259 | private void updateSPJ (SPJNode spjNode,List joinColumns){ 260 | List newOutputExprs = new ArrayList<>(); 261 | for (RexNode joinColumn : joinColumns){ 262 | newOutputExprs.add(RexNodeHelper.substitute(joinColumn,spjNode.getOutputExpr())); 263 | } 264 | spjNode.setOutputExpr(newOutputExprs); 265 | } 266 | 267 | private void addInputs(AlgeNode child,List inputs){ 268 | if(child instanceof SPJNode){ 269 | inputs.addAll(child.getInputs()); 270 | }else{ 271 | inputs.add(child); 272 | } 273 | } 274 | 275 | static public SPJNode wrapBySPJ (AggNode aggNode, Context z3Context){ 276 | Set emptyCondition = new HashSet<>(); 277 | List inputs = new ArrayList(); 278 | inputs.add(aggNode); 279 | return (new SPJNode(aggNode.getOutputExpr(),emptyCondition,inputs,z3Context)); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/main/java/AlgeNodeParser/ProjectParser.java: -------------------------------------------------------------------------------- 1 | package AlgeNodeParser; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNode.UnionNode; 5 | import AlgeNode.SPJNode; 6 | import RexNodeHelper.RexNodeHelper; 7 | import com.microsoft.z3.Context; 8 | import org.apache.calcite.rel.RelNode; 9 | import org.apache.calcite.rel.logical.LogicalProject; 10 | import org.apache.calcite.rex.RexNode; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class ProjectParser extends AlgeNodeParser{ 16 | public AlgeNode constructRelNode(RelNode input, Context z3Context){ 17 | LogicalProject project = (LogicalProject) input; 18 | AlgeNode inputNode = AlgeNodeParserPair.constructAlgeNode(project.getInput(),z3Context); 19 | if (inputNode instanceof UnionNode){ 20 | updateUnion((UnionNode) inputNode,project.getProjects()); 21 | return inputNode; 22 | } 23 | if (inputNode instanceof SPJNode){ 24 | updateSPJ(inputNode,project.getProjects()); 25 | return inputNode; 26 | } 27 | else { 28 | System.out.println("error in project parser:"+inputNode.toString()); 29 | return inputNode; 30 | } 31 | } 32 | private void updateSPJ (AlgeNode spjNode, List columns){ 33 | updateOutputExprs(spjNode,columns); 34 | } 35 | private void updateUnion (UnionNode unionNode,List columns){ 36 | for (AlgeNode input:unionNode.getInputs()){ 37 | updateOutputExprs(input,columns); 38 | } 39 | } 40 | private void updateOutputExprs(AlgeNode inputNode,List columns){ 41 | List inputExprs = inputNode.getOutputExpr(); 42 | List newOutputExpr = new ArrayList<>(); 43 | for (RexNode column:columns){ 44 | newOutputExpr.add(RexNodeHelper.substitute(column,inputExprs)); 45 | } 46 | inputNode.setOutputExpr(newOutputExpr); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/AlgeNodeParser/TableParser.java: -------------------------------------------------------------------------------- 1 | package AlgeNodeParser; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNode.TableNode; 5 | import AlgeNode.SPJNode; 6 | import com.microsoft.z3.Context; 7 | import org.apache.calcite.adapter.enumerable.EnumerableTableScan; 8 | import org.apache.calcite.plan.RelOptTable; 9 | import org.apache.calcite.rel.RelNode; 10 | import org.apache.calcite.rel.type.RelDataType; 11 | import org.apache.calcite.rel.type.RelDataTypeField; 12 | import org.apache.calcite.rex.RexInputRef; 13 | import org.apache.calcite.rex.RexNode; 14 | 15 | import java.util.ArrayList; 16 | import java.util.HashSet; 17 | import java.util.List; 18 | import java.util.Set; 19 | 20 | 21 | public class TableParser extends AlgeNodeParser{ 22 | public AlgeNode constructRelNode(RelNode input, Context z3Context){ 23 | EnumerableTableScan tableScan = (EnumerableTableScan) input; 24 | RelOptTable table = tableScan.getTable(); 25 | String tableName = table.getQualifiedName().get(0); 26 | List columns = tableScan.getRowType().getFieldList(); 27 | ArrayList columnTypes = new ArrayList<>(); 28 | for (RelDataTypeField column:columns){ 29 | columnTypes.add(column.getType()); 30 | } 31 | TableNode tableNode = new TableNode(tableName,columnTypes,z3Context); 32 | return wrapBySPJ(tableNode,z3Context); 33 | 34 | } 35 | 36 | private SPJNode wrapBySPJ (TableNode tableNode, Context z3Context){ 37 | Set emptyCondition = new HashSet<>(); 38 | List inputs = new ArrayList(); 39 | inputs.add(tableNode); 40 | return (new SPJNode(tableNode.getOutputExpr(),emptyCondition,inputs,z3Context)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/AlgeNodeParser/UnionParser.java: -------------------------------------------------------------------------------- 1 | package AlgeNodeParser; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNode.UnionNode; 5 | import com.microsoft.z3.Context; 6 | import org.apache.calcite.rel.RelNode; 7 | import org.apache.calcite.rel.logical.LogicalUnion; 8 | import org.apache.calcite.rel.type.RelDataType; 9 | import org.apache.calcite.rel.type.RelDataTypeField; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class UnionParser extends AlgeNodeParser{ 14 | public AlgeNode constructRelNode(RelNode input, Context z3Context){ 15 | LogicalUnion logicalUnion = (LogicalUnion) input; 16 | List inputs = new ArrayList(); 17 | for(int i=0;i inputTypes = new ArrayList<>(); 22 | for(RelDataTypeField field:logicalUnion.getRowType().getFieldList()){ 23 | inputTypes.add(field.getType()); 24 | } 25 | UnionNode unionNode = new UnionNode(inputs,z3Context,inputTypes); 26 | if (logicalUnion.all){ 27 | return unionNode; 28 | }else{ 29 | return AggParser.distinctToAgg(unionNode); 30 | } 31 | } 32 | public List normalizeNodes (AlgeNode input){ 33 | List result = new ArrayList<>(); 34 | if (input instanceof UnionNode){ 35 | result.addAll(input.getInputs()); 36 | }else{ 37 | result.add(input); 38 | } 39 | return result; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/AlgeRule/AggregateMerge.java: -------------------------------------------------------------------------------- 1 | package AlgeRule; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNode.AggNode; 5 | import AlgeNode.SPJNode; 6 | import AlgeNode.UnionNode; 7 | import RexNodeHelper.RexNodeHelper; 8 | import org.apache.calcite.rel.core.AggregateCall; 9 | import org.apache.calcite.rex.RexInputRef; 10 | import org.apache.calcite.rex.RexNode; 11 | import org.apache.calcite.sql.SqlKind; 12 | 13 | import java.util.*; 14 | 15 | public class AggregateMerge extends AlgeRuleBase{ 16 | private List newCalls; 17 | public AggregateMerge(AlgeNode input){ 18 | this.input = input; 19 | } 20 | public boolean preCondition() { 21 | if(this.input instanceof AggNode){ 22 | AggNode aggNode = (AggNode) this.input; 23 | AlgeNode subInput = aggNode.getInput(); 24 | if(subInput instanceof SPJNode){ 25 | SPJNode parent = (SPJNode) subInput; 26 | if (parent.getInputs().size() == 1) { 27 | AlgeNode child = parent.getInputs().get(0); 28 | if (child instanceof AggNode){ 29 | return canMerge(aggNode,(AggNode) child,parent); 30 | } 31 | } 32 | } 33 | } 34 | return false; 35 | } 36 | 37 | private boolean canMerge (AggNode input, AggNode subInput, SPJNode parent) { 38 | if (GroupByEntail(input,subInput,parent.getOutputExpr())){ 39 | if (conditionsNoAgg(subInput,parent.getConditions())){ 40 | return isMergeAggCalls(input,subInput,parent.getOutputExpr()); 41 | } 42 | } 43 | return false; 44 | } 45 | 46 | private boolean isMergeAggCalls(AggNode input,AggNode subInput,List outputExprs){ 47 | this.newCalls = new ArrayList<>(); 48 | for(AggregateCall aggregateCall:input.getAggregateCallList()){ 49 | if(!isMergeAggCall(aggregateCall,subInput,outputExprs)){ 50 | return false; 51 | } 52 | } 53 | return true; 54 | } 55 | 56 | private AggregateCall setDistinct(AggregateCall call){ 57 | return AggregateCall.create(call.getAggregation(),true,call.isApproximate(),call.getArgList(),call.filterArg,call.getCollation(),call.getType(),call.getName()); 58 | } 59 | 60 | private boolean isMergeAggCall(AggregateCall call,AggNode subInput, List outputExprs){ 61 | if(isAppendableAgg(call)){ 62 | //TODO: currently only support aggCall take one operands 63 | Integer operand = call.getArgList().get(0); 64 | RexNode inputColumn = outputExprs.get(operand); 65 | int groupBySize = subInput.getGroupByList().size(); 66 | if(inputColumn instanceof RexInputRef){ 67 | int index = ((RexInputRef)inputColumn).getIndex(); 68 | if(index >= groupBySize){ 69 | AggregateCall inputAggCall = subInput.getAggregateCallList().get(index - groupBySize); 70 | if(inputAggCall.getAggregation().getKind().equals(call.getAggregation().getKind())){ 71 | if ((!inputAggCall.isDistinct()) && (!inputAggCall.isApproximate()) && (!call.isDistinct()) && (!call.isApproximate())){ 72 | this.newCalls.add(call); 73 | return true; 74 | } 75 | }else{ 76 | return false; 77 | } 78 | }else{ 79 | if((index==0) && (groupBySize == 1)){ 80 | this.newCalls.add(setDistinct(call)); 81 | }else{ 82 | this.newCalls.add(call); 83 | } 84 | return true; 85 | } 86 | } 87 | } 88 | return false; 89 | } 90 | private boolean isAppendableAgg(AggregateCall call){ 91 | for(SqlKind sqlKind : AggNode.appendableAgg){ 92 | if(call.getAggregation().getKind().equals(sqlKind)){ 93 | return true; 94 | } 95 | } 96 | return false; 97 | } 98 | 99 | private boolean GroupByEntail (AggNode input, AggNode subInput, List outputExprs){ 100 | List subInputGroups = subInput.getGroupByVariables(); 101 | for (int index: input.getGroupByList()){ 102 | RexNode outputExpr = outputExprs.get(index); 103 | if(!subInputGroups.containsAll(RexNodeHelper.collectVariables(outputExpr))){ 104 | return false; 105 | } 106 | } 107 | return true; 108 | } 109 | 110 | private boolean conditionsNoAgg(AggNode input,Set conditions){ 111 | List groupByVariables = input.getGroupByVariables(); 112 | for(RexNode condition:conditions){ 113 | if(!groupByVariables.containsAll(RexNodeHelper.collectVariables(condition))){ 114 | return false; 115 | } 116 | } 117 | return true; 118 | } 119 | 120 | public AlgeNode transformation(){ 121 | AggNode aggNode = (AggNode) input; 122 | SPJNode parent = (SPJNode) aggNode.getInput(); 123 | AggNode child = (AggNode) parent.getInputs().get(0); 124 | AlgeNode childInput = child.getInput(); 125 | if (childInput instanceof SPJNode){ 126 | normalizeSPJNode((SPJNode) childInput,child); 127 | updateSPJCode((SPJNode)childInput,parent.getOutputExpr(),parent.getConditions()); 128 | } 129 | if (childInput instanceof UnionNode){ 130 | normalizeUnionNode((UnionNode) childInput,child); 131 | updateUnionNode((UnionNode)childInput,parent.getOutputExpr(),parent.getConditions()); 132 | } 133 | List newInput = new ArrayList<>(); 134 | newInput.add(childInput); 135 | aggNode.setInputs(newInput); 136 | aggNode.setAggregateCallList(this.newCalls); 137 | return aggNode; 138 | } 139 | 140 | private void normalizeSPJNode (SPJNode input, AggNode aggNode){ 141 | List newOutputExpr = new ArrayList<>(); 142 | for (Integer columnIndex:aggNode.getGroupByList()){ 143 | newOutputExpr.add(input.getOutputExpr().get(columnIndex)); 144 | } 145 | for (AggregateCall aggregateCall : aggNode.getAggregateCallList()){ 146 | List argList = aggregateCall.getArgList(); 147 | if (argList.size() == 1){ 148 | newOutputExpr.add(input.getOutputExpr().get(argList.get(0))); 149 | } 150 | } 151 | input.setOutputExpr(newOutputExpr); 152 | } 153 | 154 | private void normalizeUnionNode (UnionNode input, AggNode aggNode){ 155 | for (AlgeNode spj : input.getInputs()){ 156 | normalizeSPJNode((SPJNode) spj,aggNode); 157 | } 158 | } 159 | 160 | private void updateSPJCode (SPJNode input, List outputExprs, Set conditions){ 161 | List newOutputExprs = new ArrayList<>(); 162 | for (RexNode oldExpr : outputExprs){ 163 | newOutputExprs.add(RexNodeHelper.substitute(oldExpr,input.getOutputExpr())); 164 | } 165 | input.setOutputExpr(newOutputExprs); 166 | for (RexNode condition: conditions){ 167 | input.addCondition(RexNodeHelper.substitute(condition,input.getOutputExpr())); 168 | } 169 | } 170 | 171 | private void updateUnionNode (UnionNode input, List outputExprs, Set conditions){ 172 | for (AlgeNode spj : input.getInputs()){ 173 | updateSPJCode((SPJNode) spj, outputExprs,conditions); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/AlgeRule/AlgeRule.java: -------------------------------------------------------------------------------- 1 | package AlgeRule; 2 | 3 | import AlgeNode.AlgeNode; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class AlgeRule { 9 | public static AlgeNode normalize(AlgeNode node){ 10 | return pushDownNormalize(pullUpNormalize(node)); 11 | } 12 | 13 | private static AlgeNode pullUpNormalize(AlgeNode node){ 14 | List pullUpSimplifyRules = new ArrayList<>(); 15 | pullUpSimplifyRules.add(SPJ2Empty.class); 16 | pullUpSimplifyRules.add(CleanEmpty.class); 17 | List newInputs = new ArrayList<>(); 18 | for (AlgeNode input : node.getInputs()) { 19 | newInputs.add(pullUpNormalize(input)); 20 | } 21 | node.setInputs(newInputs); 22 | node = simplifyBaseRules(node,pullUpSimplifyRules); 23 | return node; 24 | } 25 | 26 | private static AlgeNode simplifyBaseRules(AlgeNode node,List simplifyRules) { 27 | boolean canBeRewrite = true; 28 | while (canBeRewrite) { 29 | canBeRewrite = false; 30 | for (Class rule : simplifyRules) { 31 | AlgeRuleBase rulePerform = new DummyRule(node); 32 | try { 33 | rulePerform = (AlgeRuleBase) rule.getDeclaredConstructor(AlgeNode.class).newInstance(node); 34 | } catch (Exception e) { 35 | System.out.println(e); 36 | } 37 | if (rulePerform.preCondition()) { 38 | node = rulePerform.transformation(); 39 | node.enableRewrite(); 40 | canBeRewrite = true; 41 | } 42 | } 43 | } 44 | return node; 45 | } 46 | 47 | private static AlgeNode pushDownNormalize(AlgeNode node){ 48 | List pushDownSimplifyRules = new ArrayList<>(); 49 | pushDownSimplifyRules.add(AggregateMerge.class); 50 | pushDownSimplifyRules.add(ConditionPushAgg.class); 51 | pushDownSimplifyRules.add(JoinToProject.class); 52 | node = simplifyBaseRules(node,pushDownSimplifyRules); 53 | List newInputs = new ArrayList<>(); 54 | for (AlgeNode input : node.getInputs()) { 55 | newInputs.add(pushDownNormalize(input)); 56 | } 57 | node.setInputs(newInputs); 58 | return node; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/AlgeRule/AlgeRuleBase.java: -------------------------------------------------------------------------------- 1 | package AlgeRule; 2 | 3 | import AlgeNode.AlgeNode; 4 | 5 | public abstract class AlgeRuleBase { 6 | protected AlgeNode input; 7 | public abstract boolean preCondition(); 8 | public abstract AlgeNode transformation(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/AlgeRule/CleanEmpty.java: -------------------------------------------------------------------------------- 1 | package AlgeRule; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNode.UnionNode; 5 | import AlgeNode.AggNode; 6 | import AlgeNode.SPJNode; 7 | import AlgeNode.EmptyNode; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class CleanEmpty extends AlgeRuleBase{ 13 | private List unionInputs; 14 | 15 | public CleanEmpty(AlgeNode input) { this.input = input; } 16 | 17 | @Override 18 | public boolean preCondition() { 19 | if (input instanceof UnionNode) { 20 | unionInputs = new ArrayList<>(); 21 | for (AlgeNode input : input.getInputs()){ 22 | if (!(input instanceof EmptyNode)) { 23 | unionInputs.add(input); 24 | } 25 | } 26 | return (unionInputs.size()==1)||(unionInputs.size() != input.getInputs().size()); 27 | } 28 | if (input instanceof AggNode){ 29 | AggNode aggNode = (AggNode) input; 30 | if (aggNode.getGroupByList().isEmpty()) { 31 | if (aggNode.getInput() instanceof EmptyNode) { 32 | return true; 33 | } 34 | } 35 | } 36 | if (input instanceof SPJNode){ 37 | for (AlgeNode input : input.getInputs()){ 38 | if (input instanceof EmptyNode){ 39 | return true; 40 | } 41 | } 42 | } 43 | return false; 44 | } 45 | 46 | @Override 47 | public AlgeNode transformation() { 48 | if (this.input instanceof UnionNode){ 49 | if (this.unionInputs.isEmpty()){ 50 | return (new EmptyNode(this.input.getZ3Context())); 51 | } 52 | if (this.unionInputs.size() == 1) { 53 | return this.unionInputs.get(0); 54 | }else{ 55 | this.input.setInputs(this.unionInputs); 56 | return (this.input); 57 | } 58 | } 59 | if (this.input instanceof SPJNode || this.input instanceof AggNode){ 60 | return (new EmptyNode(this.input.getZ3Context())); 61 | } 62 | return this.input; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/AlgeRule/ConditionPushAgg.java: -------------------------------------------------------------------------------- 1 | package AlgeRule; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNode.SPJNode; 5 | import AlgeNode.UnionNode; 6 | import AlgeNode.AggNode; 7 | import AlgeNodeParser.FilterParser; 8 | import RexNodeHelper.RexNodeHelper; 9 | import org.apache.calcite.rex.RexNode; 10 | 11 | import java.util.*; 12 | 13 | public class ConditionPushAgg extends AlgeRuleBase{ 14 | private Set pushDownConditions; 15 | private Set nonPushedConditions; 16 | class pushDownCondition { 17 | private int tableIndex; 18 | private RexNode condition; 19 | public pushDownCondition (int tableIndex,RexNode condition){ 20 | this.tableIndex = tableIndex; 21 | this.condition = condition; 22 | } 23 | 24 | public int getTableIndex() { 25 | return tableIndex; 26 | } 27 | 28 | public RexNode getCondition() { 29 | return condition; 30 | } 31 | } 32 | public ConditionPushAgg (AlgeNode input){ 33 | this.input = input; 34 | } 35 | public boolean preCondition(){ 36 | this.pushDownConditions = new HashSet<>(); 37 | this.nonPushedConditions = new HashSet<>(); 38 | boolean result = false; 39 | if (this.input instanceof SPJNode) { 40 | SPJNode spjNode = (SPJNode) this.input; 41 | Set conditions = spjNode.getConditions(); 42 | int offSize = 0; 43 | int tableIndex = 0; 44 | for (AlgeNode input : spjNode.getInputs()) { 45 | if (input instanceof AggNode) { 46 | AggNode aggNode = (AggNode) input; 47 | for (RexNode condition : conditions) { 48 | if (isPushDown(aggNode, condition,offSize)) { 49 | result = true; 50 | RexNode newCondition = offsetCondition(condition, offSize); 51 | this.pushDownConditions.add(new pushDownCondition(tableIndex, newCondition)); 52 | }else{ 53 | this.nonPushedConditions.add(condition); 54 | } 55 | } 56 | } 57 | offSize = offSize + input.getOutputExpr().size(); 58 | tableIndex++; 59 | } 60 | } 61 | return result; 62 | } 63 | 64 | private RexNode offsetCondition (RexNode condition, int offsize) { 65 | return RexNodeHelper.minusOffSize(condition,offsize); 66 | } 67 | 68 | private boolean isPushDown(AggNode aggNode, RexNode condition,int offsize){ 69 | List groupByVariables = aggNode.getGroupByVariables(); 70 | List newGroupByVariables = new ArrayList<>(); 71 | for (RexNode groupByVariable : groupByVariables){ 72 | newGroupByVariables.add(RexNodeHelper.addOffSize(groupByVariable,offsize)); 73 | } 74 | Set variables = new HashSet<>(); 75 | RexNodeHelper.collectVariables(condition,variables); 76 | return groupByVariables.containsAll(variables); 77 | } 78 | 79 | public AlgeNode transformation () { 80 | for (pushDownCondition c : pushDownConditions){ 81 | AggNode aggNode = (AggNode) input.getInputs().get(c.getTableIndex()); 82 | pushDown(aggNode,c.getCondition()); 83 | } 84 | this.input.setConditions(this.nonPushedConditions); 85 | return input; 86 | } 87 | 88 | private AlgeNode pushDown (AggNode aggNode, RexNode condition){ 89 | AlgeNode inputNode = aggNode.getInput(); 90 | if (inputNode instanceof UnionNode) { 91 | UnionNode unionNode = (UnionNode) inputNode; 92 | for (AlgeNode input : unionNode.getInputs()){ 93 | RexNode newCondition = RexNodeHelper.substitute(condition,input.getOutputExpr()); 94 | input.addConditions(FilterParser.conjunctiveForm(newCondition)); 95 | } 96 | } 97 | if (inputNode instanceof SPJNode) { 98 | RexNode newCondition = RexNodeHelper.substitute(condition,inputNode.getOutputExpr()); 99 | inputNode.addConditions(FilterParser.conjunctiveForm(newCondition)); 100 | } 101 | return aggNode; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/AlgeRule/DummyRule.java: -------------------------------------------------------------------------------- 1 | package AlgeRule; 2 | 3 | import AlgeNode.AlgeNode; 4 | 5 | public class DummyRule extends AlgeRuleBase{ 6 | public DummyRule(AlgeNode node) {this.input = node;}; 7 | 8 | @Override 9 | public boolean preCondition() { 10 | return false; 11 | } 12 | 13 | @Override 14 | public AlgeNode transformation() { 15 | return this.input; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/AlgeRule/JoinToProject.java: -------------------------------------------------------------------------------- 1 | package AlgeRule; 2 | 3 | import org.apache.calcite.rex.RexCall; 4 | import org.apache.calcite.rex.RexInputRef; 5 | import org.apache.calcite.rex.RexNode; 6 | import org.apache.calcite.rex.RexVisitorImpl; 7 | import org.apache.calcite.sql.SqlKind; 8 | 9 | import com.microsoft.z3.BoolExpr; 10 | import com.microsoft.z3.Context; 11 | 12 | import AlgeNode.AlgeNode; 13 | import AlgeNode.SPJNode; 14 | import AlgeNode.TableNode; 15 | import RexNodeHelper.RexNodeHelper; 16 | import SymbolicRexNode.BoolPredicate; 17 | import SymbolicRexNode.SymbolicColumn; 18 | import Z3Helper.z3Utility; 19 | 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.HashSet; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.Set; 26 | 27 | public class JoinToProject extends AlgeRuleBase{ 28 | static Map keys; 29 | 30 | private Map> duplicateIndexes; 31 | static public int checkKeyIndex(String name){ 32 | if (keys == null){ 33 | keys = new HashMap<>(); 34 | keys.put("EMP", 0); 35 | keys.put("DEPT", 0); 36 | } 37 | if (keys.containsKey(name)){ 38 | return keys.get(name); 39 | }else{ 40 | return -1; 41 | } 42 | } 43 | public JoinToProject(AlgeNode input){ 44 | this.input = input; 45 | } 46 | public boolean preCondition() { 47 | if (this.input instanceof SPJNode){ 48 | SPJNode spjNode = (SPJNode) this.input; 49 | List inputs = spjNode.getInputs(); 50 | calculateDuplicateSet(inputs); 51 | // System.out.println("it is here"); 52 | // System.out.println(this.input.toString()); 53 | return !this.duplicateIndexes.isEmpty(); 54 | } 55 | return false; 56 | } 57 | 58 | private void calculateDuplicateSet(List inputs){ 59 | Map> nameToTableIndex = new HashMap<>(); 60 | int tableIndex = 0; 61 | for (AlgeNode input : inputs){ 62 | if (input instanceof TableNode){ 63 | TableNode tableNode = (TableNode) input; 64 | String tableName = tableNode.getName(); 65 | if (nameToTableIndex.containsKey(tableName)){ 66 | Set indexes = nameToTableIndex.get(tableName); 67 | indexes.add(tableIndex); 68 | }else{ 69 | Set indexes = new HashSet<>(); 70 | indexes.add(tableIndex); 71 | nameToTableIndex.put(tableName,indexes); 72 | } 73 | } 74 | tableIndex++; 75 | } 76 | // System.out.println("first result"); 77 | // System.out.println(nameToTableIndex); 78 | this.duplicateIndexes = new HashMap<>(); 79 | for (Map.Entry> tableIndexes : nameToTableIndex.entrySet()){ 80 | String tableName = tableIndexes.getKey(); 81 | Set duplicateIndexes = verifyDuplicateJoin(tableName, tableIndexes.getValue()); 82 | // System.out.println("duplicate index:"); 83 | // System.out.println(duplicateIndexes); 84 | if (duplicateIndexes.size() > 1){ 85 | this.duplicateIndexes.put(tableName, duplicateIndexes); 86 | } 87 | } 88 | } 89 | 90 | private Set verifyDuplicateJoin(String tableName, Set tableIndexes){ 91 | SPJNode spjNode = (SPJNode) this.input; 92 | Set conditions = spjNode.getConditions(); 93 | List startIndex = new ArrayList<>(); 94 | int count = 0; 95 | for (AlgeNode input : spjNode.getInputs()){ 96 | startIndex.add(count); 97 | count = count + input.getOutputExpr().size(); 98 | } 99 | Set duplicateIndex = new HashSet<>(); 100 | int columnIndex = checkKeyIndex(tableName); 101 | if (columnIndex == -1){ 102 | return new HashSet<>(); 103 | } 104 | for (Integer i1 : tableIndexes){ 105 | for (Integer i2 : tableIndexes){ 106 | if (!duplicateIndex.contains(i1) || !duplicateIndex.contains(i2)){ 107 | int columnIndex1 = startIndex.get(i1) + columnIndex; 108 | int columnIndex2 = startIndex.get(i2) + columnIndex; 109 | if (checkEquivalent(columnIndex1, columnIndex2, conditions)){ 110 | duplicateIndex.add(i1); 111 | duplicateIndex.add(i2); 112 | } 113 | } 114 | } 115 | } 116 | return duplicateIndex; 117 | } 118 | 119 | static class EqHelper extends RexVisitorImpl { 120 | boolean isEq = false; 121 | int index1; 122 | int index2; 123 | protected EqHelper (int index1, int index2) { 124 | super(false); 125 | this.index1 = index1; 126 | this.index2 = index2; 127 | } 128 | 129 | public boolean isEq(){ 130 | return isEq; 131 | } 132 | 133 | @Override public Void visitCall(RexCall call) { 134 | if (call.getKind().equals(SqlKind.EQUALS)){ 135 | RexNode left = call.getOperands().get(0); 136 | RexNode right = call.getOperands().get(1); 137 | if ((left instanceof RexInputRef) && (right instanceof RexInputRef)){ 138 | int leftIndex = ((RexInputRef)left).getIndex(); 139 | int rightIndex = ((RexInputRef)right).getIndex(); 140 | isEq = (leftIndex == index1 && rightIndex == index2) || (rightIndex == index1 && leftIndex == index2); 141 | } 142 | }else { 143 | for (RexNode operand : call.operands) { 144 | operand.accept(this); 145 | } 146 | } 147 | return null; 148 | } 149 | 150 | } 151 | 152 | private boolean checkEquivalent(int index1, int index2, Set conditions){ 153 | List symbolicColumns = new ArrayList<>(); 154 | Context z3Context = this.input.getZ3Context(); 155 | for (int i=0;i assign = new ArrayList<>(); 162 | SymbolicColumn symbolicCondition = BoolPredicate.getAndNodeSymbolicColumn(conditions, symbolicColumns ,assign,z3Context); 163 | BoolExpr conditionHold = symbolicCondition.isValueTrue(); 164 | List column1 = new ArrayList<>(); 165 | column1.add(symbolicColumns.get(index1)); 166 | List column2 = new ArrayList<>(); 167 | column2.add(symbolicColumns.get(index2)); 168 | return z3Utility.symbolicOutputEqual(conditionHold, column1, column2, z3Context); 169 | } 170 | 171 | public AlgeNode transformation() { 172 | // System.out.println("what is repeat"); 173 | // System.out.println(this.duplicateIndexes); 174 | // System.out.println("something needs to be done"); 175 | Map minTableIndex = new HashMap<>(); 176 | for (Map.Entry> duplicateIndex : this.duplicateIndexes.entrySet()){ 177 | minTableIndex.put(duplicateIndex.getKey(),getMin(duplicateIndex.getValue())); 178 | } 179 | List newInputs = new ArrayList(); 180 | int tableIndex = 0; 181 | Map columnIndexSub = new HashMap<>(); 182 | List removedTable = new ArrayList<>(); 183 | List newInputStartIndex = new ArrayList<>(); 184 | int removedInputs = 0; 185 | int startIndex = 0; 186 | for (AlgeNode input : this.input.getInputs()){ 187 | removedTable.add(removedInputs); 188 | newInputStartIndex.add(startIndex); 189 | if (isRemove(input, tableIndex)){ 190 | removedInputs++; 191 | buildMap(tableIndex,removedTable, newInputStartIndex, columnIndexSub); 192 | }else{ 193 | buildMap(tableIndex, startIndex, columnIndexSub); 194 | startIndex = startIndex + input.getOutputExpr().size(); 195 | newInputs.add(input); 196 | } 197 | tableIndex++; 198 | } 199 | List newOutputExprs = new ArrayList<>(); 200 | for (RexNode outputExpr : this.input.getOutputExpr()){ 201 | newOutputExprs.add(RexNodeHelper.substitute(outputExpr,columnIndexSub)); 202 | } 203 | Set newConditions = new HashSet<>(); 204 | for (RexNode condition : this.input.getConditions()){ 205 | newConditions.add(RexNodeHelper.substitute(condition, columnIndexSub)); 206 | } 207 | // System.out.println("after transformation"); 208 | // System.out.println(new SPJNode(newOutputExprs, newConditions, newInputs, this.input.getZ3Context())); 209 | return new SPJNode(newOutputExprs, newConditions, newInputs, this.input.getZ3Context()); 210 | } 211 | 212 | private void buildMap(int tableIndex, int startIndex, Map columnIndexSub){ 213 | int oldStartIndex = getOldStartIndex(tableIndex); 214 | int bound = this.input.getInputs().get(tableIndex).getOutputExpr().size(); 215 | for (int i=0; i < bound; i++){ 216 | columnIndexSub.put(oldStartIndex+i, startIndex+i); 217 | } 218 | } 219 | 220 | // TODO 221 | private void buildMap(int removeIndex, List tableOffSet, List startIndexes, Map columnIndexSub){ 222 | TableNode removeTable = (TableNode) this.input.getInputs().get(removeIndex); 223 | // System.out.println("show again" + this.duplicateIndexes); 224 | // System.out.println("table name:"+removeTable.getName()); 225 | // System.out.println(); 226 | int minIndex = this.getMin(this.duplicateIndexes.get(removeTable.getName())); 227 | // System.out.println("minIndex:"+minIndex); 228 | // System.out.println("tableOffSet:"+tableOffSet.get(minIndex)); 229 | int currentTableIndex = minIndex - tableOffSet.get(minIndex); 230 | int startIndex = startIndexes.get(currentTableIndex); 231 | int oldStartIndex = getOldStartIndex(removeIndex); 232 | for (int i=0; i < removeTable.getOutputExpr().size(); i++){ 233 | columnIndexSub.put(oldStartIndex+i, startIndex+i); 234 | } 235 | } 236 | 237 | private int getOldStartIndex(int removeIndex){ 238 | int count = 0; 239 | for (int i=0; i copyIndex = this.duplicateIndexes.get(tableNode.getName()); 251 | if (copyIndex.contains(tableIndex)){ 252 | int minIndex = getMin(copyIndex); 253 | if (minIndex != tableIndex){ 254 | return true; 255 | } 256 | } 257 | } 258 | } 259 | return false; 260 | } 261 | 262 | private int getMin (Set indexes){ 263 | int min = Integer.MAX_VALUE; 264 | for (Integer index : indexes){ 265 | int value = index.intValue(); 266 | if ( value < min){ 267 | min = value; 268 | } 269 | } 270 | return min; 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/main/java/AlgeRule/SPJ2Empty.java: -------------------------------------------------------------------------------- 1 | package AlgeRule; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNode.SPJNode; 5 | import AlgeNode.EmptyNode; 6 | import SymbolicRexNode.BoolPredicate; 7 | import SymbolicRexNode.RexNodeUtility; 8 | import SymbolicRexNode.RexNotIn; 9 | import SymbolicRexNode.SymbolicColumn; 10 | import Z3Helper.z3Utility; 11 | import com.microsoft.z3.BoolExpr; 12 | import com.microsoft.z3.Context; 13 | import org.apache.calcite.rex.RexNode; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.Set; 18 | 19 | public class SPJ2Empty extends AlgeRuleBase{ 20 | public SPJ2Empty (AlgeNode input) { this.input = input ;} 21 | 22 | @Override 23 | public boolean preCondition() { 24 | if (this.input instanceof SPJNode) { 25 | SPJNode spjNode = (SPJNode) this.input; 26 | List assign = new ArrayList<>(); 27 | Set conditions = spjNode.getConditions(); 28 | if(!conditions.isEmpty()) { 29 | Context z3Context = this.input.getZ3Context(); 30 | List inputSymbolicColumns = inputSymbolicColumns(spjNode,z3Context); 31 | RexNodeUtility.reset(); 32 | SymbolicColumn symbolicCondition = BoolPredicate.getAndNodeSymbolicColumn(conditions,inputSymbolicColumns,assign,z3Context); 33 | RexNodeUtility.reset(); 34 | assign.add(symbolicCondition.isValueTrue()); 35 | if(z3Utility.isUnsat(z3Utility.mkAnd(assign,z3Context),z3Context)) { 36 | return true; 37 | } 38 | } 39 | } 40 | return false; 41 | } 42 | 43 | private List inputSymbolicColumns (SPJNode node,Context z3Context) { 44 | List inputColumns = new ArrayList<>(); 45 | for (AlgeNode input : node.getInputs()){ 46 | for (RexNode outputExpr : input.getOutputExpr()){ 47 | inputColumns.add(SymbolicColumn.mkNewSymbolicColumn(z3Context,outputExpr)); 48 | } 49 | } 50 | return inputColumns; 51 | } 52 | 53 | @Override 54 | public AlgeNode transformation() { 55 | return (new EmptyNode(this.input.getZ3Context())); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/RexNodeHelper/NotIn.java: -------------------------------------------------------------------------------- 1 | package RexNodeHelper; 2 | 3 | import AlgeNode.AlgeNode; 4 | import org.apache.calcite.rex.RexCall; 5 | import org.apache.calcite.rex.RexNode; 6 | import org.apache.calcite.sql.SqlBinaryOperator; 7 | import org.apache.calcite.sql.SqlKind; 8 | import org.apache.calcite.sql.SqlOperator; 9 | import org.apache.calcite.sql.type.InferTypes; 10 | import org.apache.calcite.sql.type.ReturnTypes; 11 | 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class NotIn extends RexCall { 17 | private AlgeNode subQuery; 18 | private List columns; 19 | static SqlOperator NotIn = new SqlBinaryOperator(SqlKind.NOT_IN.sql, SqlKind.NOT_IN, 20 | 32, 21 | true, 22 | ReturnTypes.BOOLEAN_NULLABLE, 23 | InferTypes.FIRST_KNOWN, 24 | null); 25 | 26 | public NotIn (AlgeNode subQuery,List columns){ 27 | super(RexNodeHelper.typeFactory.createJavaType(Boolean.class),NotIn,new ArrayList<>()); 28 | this.subQuery = subQuery; 29 | this.columns = columns; 30 | } 31 | 32 | public AlgeNode getSubQuery(){ 33 | return this.subQuery; 34 | } 35 | 36 | public List getColumns(){ 37 | return this.columns; 38 | } 39 | 40 | public String print () { 41 | StringBuilder result = new StringBuilder("SpeicalNotIn: ("); 42 | for (RexNode rexNode : this.columns) { 43 | result.append(rexNode.toString()); 44 | } 45 | result.append(")\n Subquery: ("); 46 | result.append(this.subQuery.toString()+")"); 47 | return result.toString(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/RexNodeHelper/RexNodeHelper.java: -------------------------------------------------------------------------------- 1 | package RexNodeHelper; 2 | 3 | import org.apache.calcite.adapter.java.JavaTypeFactory; 4 | import org.apache.calcite.jdbc.JavaTypeFactoryImpl; 5 | import org.apache.calcite.rel.type.RelDataTypeSystem; 6 | import org.apache.calcite.rex.RexCall; 7 | import org.apache.calcite.rex.RexInputRef; 8 | import org.apache.calcite.rex.RexLiteral; 9 | import org.apache.calcite.rex.RexNode; 10 | 11 | import java.util.HashSet; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Set; 15 | import java.util.function.Function; 16 | import java.util.stream.Collectors; 17 | 18 | public class RexNodeHelper { 19 | public static final JavaTypeFactory typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT); 20 | /** 21 | * Recursively descend over the node, applying the input function to each sub-node in a call. 22 | * @param f The function to apply 23 | * @param node The input node 24 | * @return The new node, resulting from applying the function to the original node. 25 | */ 26 | private static RexNode mapNode(Function f, RexNode node) { 27 | if (node instanceof RexCall) { 28 | if (node instanceof NotIn){ 29 | NotIn notIn = (NotIn) node; 30 | List columns = notIn.getColumns().stream() 31 | .map((RexNode operand) -> mapNode(f, operand)) 32 | .collect(Collectors.toList()); 33 | return (new NotIn(notIn.getSubQuery(),columns)); 34 | } 35 | RexCall rexCall = (RexCall) node; 36 | List operands = rexCall.getOperands().stream() 37 | .map((RexNode operand) -> mapNode(f, operand)) 38 | .collect(Collectors.toList()); 39 | return rexCall.clone(rexCall.getType(), operands); 40 | } else { 41 | return f.apply(node); 42 | } 43 | } 44 | 45 | private static RexNode unhandled(RexNode node, String procedureName) { 46 | System.out.println("This rex node is not handled in `" + procedureName + "`: " + node.getClass().toString()); 47 | return node; 48 | } 49 | 50 | public static RexNode substitute(RexNode input, List outputExpr) { 51 | return mapNode((RexNode node) -> { 52 | if (node instanceof RexInputRef) { 53 | RexInputRef ref = (RexInputRef) node; 54 | return outputExpr.get(ref.getIndex()); 55 | } else if (node instanceof RexLiteral) { 56 | return node; 57 | } else { 58 | return unhandled(node, "substitute"); 59 | } 60 | }, input); 61 | } 62 | 63 | public static RexNode substitute(RexNode input, Map indexMap) { 64 | return mapNode((RexNode node) -> { 65 | if (node instanceof RexInputRef) { 66 | RexInputRef ref = (RexInputRef) node; 67 | int index = ref.getIndex(); 68 | if (indexMap.containsKey(index)){ 69 | return new RexInputRef(indexMap.get(index), ref.getType()); 70 | }else{ 71 | return ref; 72 | } 73 | } else if (node instanceof RexLiteral) { 74 | return node; 75 | } else { 76 | return unhandled(node, "substitute"); 77 | } 78 | }, input); 79 | } 80 | 81 | public static RexNode addOffSize(RexNode input,int offSize){ 82 | return mapNode((RexNode node) -> { 83 | if (node instanceof RexInputRef) { 84 | RexInputRef ref = (RexInputRef) node; 85 | int nexIndex = ref.getIndex() + offSize; 86 | RexInputRef newRef = new RexInputRef(nexIndex,ref.getType()); 87 | return newRef; 88 | } else if (node instanceof RexLiteral) { 89 | return node; 90 | } else { 91 | return unhandled(node, "addTableIndex"); 92 | } 93 | }, input); 94 | } 95 | 96 | public static RexNode minusOffSize(RexNode input,int offSize){ 97 | return mapNode((RexNode node) -> { 98 | if (node instanceof RexInputRef) { 99 | RexInputRef ref = (RexInputRef) node; 100 | int nexIndex = ref.getIndex() - offSize; 101 | RexInputRef newRef = new RexInputRef(nexIndex,ref.getType()); 102 | return newRef; 103 | } else if (node instanceof RexLiteral) { 104 | return node; 105 | } else { 106 | return unhandled(node, "addTableIndex"); 107 | } 108 | }, input); 109 | } 110 | 111 | public static Set collectVariables(RexNode input){ 112 | Set result = new HashSet<>(); 113 | collectVariables(input,result); 114 | return result; 115 | } 116 | 117 | public static void collectVariables(RexNode input, Set result){ 118 | if(input instanceof RexInputRef){ 119 | result.add(input); 120 | } else if(input instanceof RexCall) { 121 | ((RexCall) input).getOperands().forEach((RexNode operand) -> collectVariables(operand, result)); 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/ArithmeticExpr.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import com.microsoft.z3.ArithExpr; 4 | import com.microsoft.z3.BoolExpr; 5 | import com.microsoft.z3.Context; 6 | import com.microsoft.z3.Expr; 7 | import org.apache.calcite.rex.RexCall; 8 | import org.apache.calcite.rex.RexNode; 9 | import org.apache.calcite.sql.SqlKind; 10 | 11 | import java.util.List; 12 | 13 | public class ArithmeticExpr extends RexNodeBase { 14 | public ArithmeticExpr(List inputs, RexNode node, Context z3Context){ 15 | super(inputs,node,z3Context); 16 | buildExpr(node); 17 | } 18 | private void buildExpr(RexNode node){ 19 | RexCall callNode = (RexCall) node; 20 | RexNode left = callNode.getOperands().get(0); 21 | RexNode right = callNode.getOperands().get(1); 22 | RexNodeBase leftConverter = RexNodeConverter.rexConstraints(inputs,left,z3Context); 23 | RexNodeBase rightConverter =RexNodeConverter.rexConstraints(inputs,right,z3Context); 24 | // build the output value based on the input value 25 | Expr value = convertArithmetic(leftConverter.getOutputValue(),rightConverter.getOutputValue(),callNode); 26 | // the output value is null if one of the input value is null 27 | BoolExpr isNull = (BoolExpr) z3Context.mkOr(leftConverter.getOutputNull(),rightConverter.getOutputNull()).simplify(); 28 | this.assignConstraints.addAll(leftConverter.getAssignConstrains()); 29 | this.assignConstraints.addAll(rightConverter.getAssignConstrains()); 30 | 31 | this.output = new SymbolicColumn(value,isNull,z3Context); 32 | } 33 | private Expr convertArithmetic(Expr leftExpr, Expr rightExpr, RexCall callNode){ 34 | SqlKind sqlKind = callNode.getKind(); 35 | ArithExpr constraint1 = (ArithExpr)leftExpr; 36 | ArithExpr constraint2 = (ArithExpr)rightExpr; 37 | switch (sqlKind){ 38 | case PLUS: 39 | return z3Context.mkAdd(constraint1,constraint2); 40 | case MINUS: 41 | return z3Context.mkSub(constraint1,constraint2); 42 | //TODO understand the semantic of divide, integer division, low or high round 43 | case DIVIDE: 44 | return z3Context.mkDiv(constraint1,constraint2); 45 | case TIMES: 46 | return z3Context.mkMul(constraint1,constraint2); 47 | default:{ 48 | return null; 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/ArithmeticPredicate.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import com.microsoft.z3.ArithExpr; 4 | import com.microsoft.z3.BoolExpr; 5 | import com.microsoft.z3.Context; 6 | import com.microsoft.z3.Expr; 7 | import org.apache.calcite.rex.RexCall; 8 | import org.apache.calcite.rex.RexNode; 9 | import org.apache.calcite.sql.SqlKind; 10 | 11 | import java.util.List; 12 | 13 | public class ArithmeticPredicate extends RexNodeBase { 14 | public ArithmeticPredicate(List inputs, RexNode node, Context z3Context){ 15 | super(inputs,node,z3Context); 16 | buildExpr(node); 17 | } 18 | private void buildExpr(RexNode node){ 19 | RexCall callNode = (RexCall) node; 20 | RexNode left = callNode.getOperands().get(0); 21 | RexNode right = callNode.getOperands().get(1); 22 | RexNodeBase leftConverter = RexNodeConverter.rexConstraints(inputs, left, z3Context); 23 | RexNodeBase rightConverter = RexNodeConverter.rexConstraints(inputs, right, z3Context); 24 | 25 | this.assignConstraints.addAll(leftConverter.getAssignConstrains()); 26 | this.assignConstraints.addAll(rightConverter.getAssignConstrains()); 27 | 28 | Expr outputValue = buildCompareResult(leftConverter.getOutputValue(), rightConverter.getOutputValue(), callNode); 29 | BoolExpr outputNull = (BoolExpr) z3Context.mkOr(leftConverter.getOutputNull(), rightConverter.getOutputNull()).simplify(); 30 | 31 | this.output = new SymbolicColumn(outputValue,outputNull,z3Context); 32 | } 33 | 34 | private BoolExpr buildCompareResult(Expr leftExpr, Expr rightExpr, RexCall node){ 35 | SqlKind sqlKind = node.getKind(); 36 | ArithExpr leftAExpr = (ArithExpr)leftExpr; 37 | ArithExpr rightAExpr = (ArithExpr)rightExpr; 38 | switch (sqlKind){ 39 | case LESS_THAN: 40 | return z3Context.mkLt(leftAExpr, rightAExpr); 41 | case LESS_THAN_OR_EQUAL: 42 | return z3Context.mkLe(leftAExpr, rightAExpr); 43 | case GREATER_THAN: 44 | return z3Context.mkGt(leftAExpr, rightAExpr); 45 | case GREATER_THAN_OR_EQUAL: 46 | return z3Context.mkGe(leftAExpr, rightAExpr); 47 | case EQUALS: 48 | return z3Context.mkEq(leftExpr, rightExpr); 49 | case NOT_EQUALS: 50 | return z3Context.mkNot(z3Context.mkEq(leftExpr, rightExpr)); 51 | default:{ 52 | // 53 | } 54 | 55 | } 56 | return null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/BoolPredicate.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import Z3Helper.z3Utility; 4 | import com.microsoft.z3.BoolExpr; 5 | import com.microsoft.z3.Context; 6 | import com.microsoft.z3.Expr; 7 | import org.apache.calcite.rex.RexCall; 8 | import org.apache.calcite.rex.RexNode; 9 | import org.apache.calcite.sql.SqlKind; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Collection; 13 | import java.util.List; 14 | 15 | public class BoolPredicate extends RexNodeBase { 16 | 17 | public BoolPredicate(List inputs, RexNode node, Context z3Context){ 18 | super(inputs,node,z3Context); 19 | buildExpr(node); 20 | } 21 | private void buildExpr(RexNode node){ 22 | RexCall callNode = (RexCall) node; 23 | SqlKind sqlKind = node.getKind(); 24 | switch (sqlKind){ 25 | case NOT: { 26 | buildNotExpr(callNode); 27 | break; 28 | } 29 | case AND: { 30 | List operands = callNode.getOperands(); 31 | this.output = getAndNodeSymbolicColumn(operands,inputs,assignConstraints,z3Context); 32 | break; 33 | } 34 | case OR: { 35 | List operands = callNode.getOperands(); 36 | this.output = getOrNodeSymbolicColumn(operands,inputs,assignConstraints,z3Context); 37 | break; 38 | } 39 | /* 40 | case IN: { 41 | buildINExpr(callNode); 42 | break; 43 | }*/ 44 | default:{ 45 | break; 46 | } 47 | } 48 | } 49 | private void buildNotExpr(RexCall node){ 50 | RexNode operand = node.getOperands().get(0); 51 | RexNodeBase converter = RexNodeConverter.rexConstraints(inputs,operand,z3Context); 52 | this.assignConstraints.addAll(converter.getAssignConstrains()); 53 | //based on the sql standard to decide the relations of output value between input value 54 | this.output = new SymbolicColumn(z3Context.mkNot((BoolExpr)converter.getOutputValue()).simplify(),converter.getOutputNull(),z3Context); 55 | } 56 | static public SymbolicColumn getAndNodeSymbolicColumn(Collection operands, List inputs, List assignConstraints, Context z3Context){ 57 | List resultValues = new ArrayList<>(); 58 | List nullValues = new ArrayList<>(); 59 | List nullSymbol = new ArrayList<>(); 60 | for(RexNode operand:operands){ 61 | RexNodeBase converter = RexNodeConverter.rexConstraints(inputs,operand,z3Context); 62 | BoolExpr result = (BoolExpr)converter.getOutputValue(); 63 | BoolExpr symbolicNull = converter.getOutputNull(); 64 | resultValues.add(result); 65 | nullValues.add(z3Context.mkOr(symbolicNull,result)); 66 | nullSymbol.add(symbolicNull); 67 | assignConstraints.addAll(converter.getAssignConstrains()); 68 | } 69 | BoolExpr outputValue = z3Utility.mkAnd(resultValues,z3Context); 70 | BoolExpr outputNull = (BoolExpr) z3Context.mkAnd(z3Utility.mkAnd(nullValues,z3Context),z3Utility.mkOr(nullSymbol,z3Context)).simplify(); 71 | return (new SymbolicColumn(outputValue,outputNull,z3Context)); 72 | } 73 | static public SymbolicColumn getOrNodeSymbolicColumn(Collection operands, List inputs, List assignConstraints, Context z3Context){ 74 | List resultValues = new ArrayList<>(); 75 | List nullValues = new ArrayList<>(); 76 | List nullSymbol = new ArrayList<>(); 77 | for(RexNode operand:operands){ 78 | RexNodeBase converter = RexNodeConverter.rexConstraints(inputs,operand,z3Context); 79 | BoolExpr result = (BoolExpr)converter.getOutputValue(); 80 | BoolExpr symbolicNull = converter.getOutputNull(); 81 | resultValues.add(result); 82 | nullValues.add(z3Context.mkOr(symbolicNull,z3Context.mkNot(result))); 83 | nullSymbol.add(symbolicNull); 84 | 85 | assignConstraints.addAll(converter.getAssignConstrains()); 86 | } 87 | BoolExpr outputValue = z3Utility.mkOr(resultValues,z3Context); 88 | BoolExpr outputNull = (BoolExpr) z3Context.mkAnd(z3Utility.mkAnd(nullValues,z3Context),z3Utility.mkOr(nullSymbol,z3Context)).simplify(); 89 | return (new SymbolicColumn(outputValue,outputNull,z3Context)); 90 | 91 | } 92 | 93 | private void buildINExpr(RexCall node){ 94 | List operands = node.getOperands(); 95 | RexNodeBase exprConverter = RexNodeConverter.rexConstraints(inputs,operands.get(0),z3Context); 96 | Expr compareExpr = exprConverter.getOutputNull(); 97 | this.assignConstraints.addAll(exprConverter.getAssignConstrains()); 98 | List valueConstrains = new ArrayList<>(); 99 | List nullValues = new ArrayList<>(); 100 | List nullSymbol = new ArrayList<>(); 101 | for(int i=1;i inputs, RexNode node, Context z3Context){ 13 | super(inputs,node,z3Context); 14 | this.output = SymbolicColumn.mkNewSymbolicColumn(z3Context,node); 15 | buildExpr(node); 16 | } 17 | private void buildExpr(RexNode node){ 18 | RexCall callNode = (RexCall) node; 19 | List operands = callNode.getOperands(); 20 | int count = 0; 21 | BoolExpr currentCondition = z3Context.mkFalse(); 22 | BoolExpr[] possibleValue = new BoolExpr[(operands.size()+1)/2]; 23 | while (count < operands.size()-1){ 24 | RexNode condition = operands.get(count); 25 | RexNodeBase conditionConverter = RexNodeConverter.rexConstraints(inputs,condition,z3Context); 26 | this.assignConstraints.addAll(conditionConverter.getAssignConstrains()); 27 | BoolExpr whenCondition = conditionConverter.getOutput().isValueTrue(); 28 | BoolExpr chooseConstrains = z3Context.mkAnd(z3Context.mkNot(currentCondition),whenCondition); 29 | currentCondition = z3Context.mkOr(currentCondition,whenCondition); 30 | count++; 31 | RexNode value = operands.get(count); 32 | possibleValue[count/2] = getOutEqualValue(value,chooseConstrains); 33 | count++; 34 | 35 | } 36 | RexNode defaultValue = operands.get(count); 37 | possibleValue[count/2] = getOutEqualValue(defaultValue,z3Context.mkNot(currentCondition)); 38 | this.assignConstraints.add((BoolExpr) z3Context.mkOr(possibleValue).simplify()); 39 | } 40 | 41 | private BoolExpr getOutEqualValue(RexNode value, BoolExpr condition){ 42 | RexNodeBase valueConstrains = RexNodeConverter.rexConstraints(inputs,value,z3Context); 43 | BoolExpr outputEqualValue = z3Context.mkEq(this.output.getSymbolicValue(),valueConstrains.getOutputValue()); 44 | BoolExpr nullValue = z3Context.mkEq(this.output.getSymbolicNull(),valueConstrains.getOutputNull()); 45 | 46 | this.assignConstraints.addAll(valueConstrains.getAssignConstrains()); 47 | 48 | return z3Context.mkAnd(condition,outputEqualValue,nullValue); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/Constant.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import Z3Helper.z3Utility; 4 | import com.microsoft.z3.Context; 5 | import com.microsoft.z3.Expr; 6 | import org.apache.calcite.rex.RexLiteral; 7 | import org.apache.calcite.rex.RexNode; 8 | import org.apache.calcite.sql.type.SqlTypeName; 9 | import org.apache.calcite.util.NlsString; 10 | 11 | import java.math.BigDecimal; 12 | import java.util.List; 13 | 14 | public class Constant extends RexNodeBase { 15 | public Constant(List inputs, RexNode node, Context z3Context){ 16 | super(inputs,node,z3Context); 17 | buildExpr(node); 18 | 19 | } 20 | private void buildExpr(RexNode node){ 21 | RexLiteral constant = (RexLiteral) node; 22 | SqlTypeName type = constant.getTypeName(); 23 | if(type.equals(SqlTypeName.NULL)){ 24 | this.output = new SymbolicColumn(z3Utility.mkDumpValue(constant.getType(),z3Context),z3Context.mkTrue(),z3Context); 25 | }else{ 26 | this.output = new SymbolicColumn(convertRexLiteralNotNull(constant),z3Context.mkFalse(),z3Context); 27 | } 28 | } 29 | private Expr convertRexLiteralNotNull(RexLiteral constant){ 30 | SqlTypeName type = constant.getTypeName(); 31 | if(SqlTypeName.APPROX_TYPES.contains(type)){ 32 | return convertApproxType(constant); 33 | } 34 | if(SqlTypeName.INT_TYPES.contains(type)){ 35 | BigDecimal value=(BigDecimal)constant.getValue(); 36 | int intConstant = value.intValue(); 37 | return z3Context.mkInt(intConstant); 38 | } 39 | if(type.equals(SqlTypeName.DECIMAL)){ 40 | BigDecimal value=(BigDecimal)constant.getValue(); 41 | return z3Context.mkReal(value.toString()); 42 | } 43 | if(SqlTypeName.BOOLEAN_TYPES.contains(type)){ 44 | Boolean value = (Boolean)constant.getValue(); 45 | if(value == null){ 46 | return z3Utility.getDumpVariable(z3Context.mkBoolSort(),z3Context); 47 | }else{ 48 | return z3Context.mkBool(value); 49 | } 50 | } 51 | if(type.equals(SqlTypeName.CHAR)) { 52 | NlsString value = (NlsString) constant.getValue(); 53 | return z3Context.mkInt(value.getValue().hashCode()); 54 | } 55 | if(type.equals(SqlTypeName.VARCHAR)){ 56 | NlsString value = (NlsString) constant.getValue(); 57 | return z3Context.mkInt(value.getValue().hashCode()); 58 | } 59 | return z3Context.mkInt(0); 60 | 61 | } 62 | private Expr convertApproxType(RexLiteral constant){ 63 | SqlTypeName type = constant.getTypeName(); 64 | if(SqlTypeName.APPROX_TYPES.contains(type)){ 65 | if(constant.getValue() instanceof Double){ 66 | Double value = (Double) constant.getValue(); 67 | return z3Context.mkReal(value.toString()); 68 | } 69 | if(constant.getValue() instanceof Float){ 70 | Float value = (Float) constant.getValue(); 71 | return z3Context.mkReal(value.toString()); 72 | } 73 | } 74 | BigDecimal value = (BigDecimal)constant.getValue(); 75 | return z3Context.mkReal(value.toString()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/DumpRexNode.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import Z3Helper.z3Utility; 4 | import com.microsoft.z3.Context; 5 | import com.microsoft.z3.Sort; 6 | import org.apache.calcite.rex.RexNode; 7 | 8 | import java.util.List; 9 | 10 | public class DumpRexNode extends RexNodeBase { 11 | public DumpRexNode(List inputs, RexNode node, Context z3Context){ 12 | super(inputs,node,z3Context); 13 | Sort dumpSort = RexNodeUtility.convertRexNodeSort(z3Context,node); 14 | this.output = z3Utility.mkDumpSymbolicColumn(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/NullPredicate.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import com.microsoft.z3.Context; 4 | import org.apache.calcite.rex.RexCall; 5 | import org.apache.calcite.rex.RexNode; 6 | import org.apache.calcite.sql.SqlKind; 7 | 8 | import java.util.List; 9 | 10 | public class NullPredicate extends RexNodeBase { 11 | public NullPredicate(List inputs, RexNode node, Context z3Context){ 12 | super(inputs,node,z3Context); 13 | buildExpr(node); 14 | 15 | } 16 | private void buildExpr(RexNode node){ 17 | RexCall callNode = (RexCall) node; 18 | SqlKind sqlKind = callNode.getKind(); 19 | RexNode operand = callNode.getOperands().get(0); 20 | RexNodeBase rexNodeConverter = RexNodeConverter.rexConstraints(inputs,operand,z3Context); 21 | this.assignConstraints.addAll(rexNodeConverter.getAssignConstrains()); 22 | if(sqlKind.equals(SqlKind.IS_NULL)){ 23 | this.output = new SymbolicColumn(rexNodeConverter.getOutputNull(),z3Context.mkFalse(),z3Context); 24 | } 25 | if(sqlKind.equals(SqlKind.IS_NOT_NULL)){ 26 | this.output = new SymbolicColumn(z3Context.mkNot(rexNodeConverter.getOutputNull()),z3Context.mkFalse(),z3Context); 27 | } 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/RexInputRefConstraints.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import com.microsoft.z3.Context; 4 | import org.apache.calcite.rex.RexInputRef; 5 | import org.apache.calcite.rex.RexNode; 6 | 7 | import java.util.List; 8 | 9 | public class RexInputRefConstraints extends RexNodeBase { 10 | public RexInputRefConstraints(List inputs, RexNode node, Context z3Context){ 11 | super(inputs,node,z3Context); 12 | buildExpr(node); 13 | 14 | } 15 | private void buildExpr(RexNode node){ 16 | RexInputRef inputRef = (RexInputRef) node; 17 | int index = inputRef.getIndex(); 18 | this.output = inputs.get(index); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/RexNodeBase.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | 4 | import com.microsoft.z3.BoolExpr; 5 | import com.microsoft.z3.Context; 6 | import com.microsoft.z3.Expr; 7 | import org.apache.calcite.rex.RexNode; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public abstract class RexNodeBase { 13 | protected Context z3Context; 14 | protected SymbolicColumn output; 15 | protected List inputs; 16 | protected List assignConstraints; 17 | public RexNodeBase(List inputs, RexNode node, Context z3Context){ 18 | this.inputs = inputs; 19 | this.z3Context = z3Context; 20 | this.assignConstraints = new ArrayList<>(); 21 | } 22 | public SymbolicColumn getOutput(){ 23 | return this.output; 24 | } 25 | public Expr getOutputValue(){ 26 | return this.output.getSymbolicValue(); 27 | } 28 | public BoolExpr getOutputNull(){ 29 | return this.output.getSymbolicNull(); 30 | } 31 | public List getAssignConstrains(){ 32 | return this.assignConstraints; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/RexNodeConverter.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import RexNodeHelper.NotIn; 4 | import com.microsoft.z3.Context; 5 | import org.apache.calcite.rex.RexCall; 6 | import org.apache.calcite.rex.RexInputRef; 7 | import org.apache.calcite.rex.RexLiteral; 8 | import org.apache.calcite.rex.RexNode; 9 | import org.apache.calcite.sql.SqlKind; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | public class RexNodeConverter { 15 | static public List logicSQL = Arrays.asList( 16 | SqlKind.AND, SqlKind.OR, SqlKind.NOT, SqlKind.IN); 17 | static public List isNullSQL = Arrays.asList( 18 | SqlKind.IS_NULL, SqlKind.IS_NOT_NULL); 19 | static public List arithmeticCompareSQL = Arrays.asList( 20 | SqlKind.LESS_THAN, SqlKind.LESS_THAN_OR_EQUAL, 21 | SqlKind.GREATER_THAN, SqlKind.GREATER_THAN_OR_EQUAL, 22 | SqlKind.EQUALS, SqlKind.NOT_EQUALS); 23 | static public List arithmeticSQL = Arrays.asList( 24 | SqlKind.PLUS, SqlKind.MINUS, SqlKind.TIMES, SqlKind.DIVIDE); 25 | 26 | public static RexNodeBase rexConstraints(List inputs, RexNode node, Context z3Context){ 27 | if(node instanceof RexLiteral){ 28 | return new Constant(inputs,node,z3Context); 29 | } else if(node instanceof RexCall){ 30 | return rexCallConstraints(inputs,node,z3Context); 31 | } else if(node instanceof RexInputRef){ 32 | return new RexInputRefConstraints(inputs,node,z3Context); 33 | } else { 34 | return null; 35 | } 36 | } 37 | 38 | private static RexNodeBase rexCallConstraints(List inputs, RexNode node, Context z3Context) { 39 | RexCall callNode = (RexCall) node; 40 | if (callNode instanceof NotIn){ 41 | return (new RexNotIn(inputs,node,z3Context)); 42 | } 43 | if (callNode.isA(SqlKind.CASE)) { 44 | return (new CaseNode(inputs, node, z3Context)); 45 | } else if (callNode.isA(SqlKind.OTHER_FUNCTION)) { 46 | return (new UserDeFun(inputs, node, z3Context)); 47 | } else if (callNode.isA(logicSQL)) { 48 | return (new BoolPredicate(inputs, node, z3Context)); 49 | } else if (callNode.isA(isNullSQL)) { 50 | return (new NullPredicate(inputs, node, z3Context)); 51 | } else if (callNode.isA(arithmeticCompareSQL)) { 52 | return (new ArithmeticPredicate(inputs, node, z3Context)); 53 | } else if (callNode.isA(arithmeticSQL)) { 54 | return (new ArithmeticExpr(inputs, node, z3Context)); 55 | } else if (callNode.isA(SqlKind.CAST)) { 56 | RexNode operand = callNode.getOperands().get(0); 57 | return rexConstraints(inputs, operand, z3Context); 58 | } else if (callNode.isA(SqlKind.IS_TRUE)) { 59 | RexNode operand = callNode.getOperands().get(0); 60 | return rexConstraints(inputs, operand, z3Context); 61 | } else { 62 | System.out.println("`rexCallConstraints` does not handle this type of call node: " + callNode.getClass()); 63 | return new DumpRexNode(inputs, node, z3Context); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/RexNodeUtility.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import com.microsoft.z3.Context; 4 | import com.microsoft.z3.Sort; 5 | import org.apache.calcite.rex.RexNode; 6 | import org.apache.calcite.sql.type.SqlTypeName; 7 | 8 | public class RexNodeUtility { 9 | static public Sort convertRexNodeSort(Context z3Context, RexNode node){ 10 | SqlTypeName type = node.getType().getSqlTypeName(); 11 | return getSortBasedOnSqlType(z3Context, type); 12 | } 13 | 14 | static public Sort getSortBasedOnSqlType(Context z3Context, SqlTypeName type) { 15 | if (SqlTypeName.APPROX_TYPES.contains(type)) { 16 | return z3Context.mkRealSort(); 17 | } else if(SqlTypeName.INT_TYPES.contains(type)) { 18 | return z3Context.mkIntSort(); 19 | } else if(type.equals(SqlTypeName.DECIMAL)) { 20 | return z3Context.mkRealSort(); 21 | } else if(SqlTypeName.BOOLEAN_TYPES.contains(type)) { 22 | return z3Context.mkBoolSort(); 23 | } else if(type.equals(SqlTypeName.CHAR)) { 24 | return z3Context.mkIntSort(); 25 | } else if(type.equals(SqlTypeName.VARCHAR)) { 26 | return z3Context.mkIntSort(); 27 | } else { 28 | return z3Context.mkIntSort(); 29 | } 30 | } 31 | 32 | static public void reset(){ 33 | RexNotIn.reset(); 34 | UserDeFun.reset1(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/RexNotIn.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeRule.AlgeRule; 5 | import RexNodeHelper.NotIn; 6 | import Z3Helper.z3Utility; 7 | import com.microsoft.z3.Context; 8 | import org.apache.calcite.rex.RexNode; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class RexNotIn extends RexNodeBase { 14 | static List previousNotIns = new ArrayList<>(); 15 | static public void reset () { 16 | previousNotIns = new ArrayList<>(); 17 | } 18 | 19 | class SymbolicRexNotIn { 20 | private SymbolicColumn result; 21 | private List symbolicColumns; 22 | private AlgeNode subquery; 23 | 24 | public SymbolicRexNotIn (SymbolicColumn result,List symbolicColumns,AlgeNode subquery){ 25 | this.result = result; 26 | this.symbolicColumns = symbolicColumns; 27 | this.subquery = subquery; 28 | } 29 | 30 | public boolean eq (List symbolicColumns, AlgeNode subquery){ 31 | boolean result = z3Utility.symbolicOutputEqual(z3Context.mkTrue(),symbolicColumns,this.symbolicColumns,z3Context); 32 | if (result){ 33 | if (subquery.isEq(this.subquery)){ 34 | return true; 35 | } 36 | } 37 | return false; 38 | } 39 | 40 | public SymbolicColumn getResult() { 41 | return result; 42 | } 43 | } 44 | 45 | public RexNotIn(List inputs, RexNode node, Context z3Context) { 46 | super(inputs,node,z3Context); 47 | buildExpr((NotIn) node); 48 | } 49 | private void buildExpr(NotIn node) { 50 | List columns = node.getColumns(); 51 | List compareColumns = new ArrayList<>(); 52 | for (RexNode column : columns) { 53 | RexNodeBase columnConverter = RexNodeConverter.rexConstraints(inputs,column,z3Context); 54 | compareColumns.add(columnConverter.getOutput()); 55 | } 56 | boolean findMatch = false; 57 | for (SymbolicRexNotIn previousNotIn : previousNotIns) { 58 | if (previousNotIn.eq(compareColumns,node.getSubQuery())){ 59 | this.output = previousNotIn.getResult(); 60 | findMatch = true; 61 | break; 62 | } 63 | } 64 | if (!findMatch){ 65 | this.output = SymbolicColumn.mkNewSymbolicColumn(z3Context,node.getType()); 66 | SymbolicRexNotIn newSymbolicNotIn = new SymbolicRexNotIn(this.output,compareColumns,node.getSubQuery()); 67 | previousNotIns.add(newSymbolicNotIn); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/SymbolicColumn.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import Z3Helper.z3Utility; 4 | import com.microsoft.z3.BoolExpr; 5 | import com.microsoft.z3.Context; 6 | import com.microsoft.z3.Expr; 7 | import org.apache.calcite.rel.type.RelDataType; 8 | import org.apache.calcite.rex.RexNode; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class SymbolicColumn { 14 | static private int count=0; 15 | private Expr symbolicValue; 16 | private BoolExpr symbolicNull; 17 | private Context z3Context; 18 | 19 | static public SymbolicColumn mkNewSymbolicColumn(Context z3Context, RexNode node){ 20 | return mkNewSymbolicColumn(z3Context,node.getType()); 21 | } 22 | static public SymbolicColumn mkNewSymbolicColumn(Context z3Context, RelDataType type){ 23 | Expr value = z3Context.mkConst("value"+count,RexNodeUtility.getSortBasedOnSqlType(z3Context,type.getSqlTypeName())); 24 | BoolExpr valueNull = z3Context.mkBoolConst("isn"+count); 25 | count++; 26 | return (new SymbolicColumn(value,valueNull,z3Context)); 27 | } 28 | 29 | static public List constructFreshColumns(List columns,Context z3Context){ 30 | List freshColumns = new ArrayList<>(); 31 | for(SymbolicColumn column:columns){ 32 | freshColumns.add(constructFreshColumn(column,z3Context)); 33 | } 34 | return freshColumns; 35 | } 36 | 37 | static public SymbolicColumn constructFreshColumn(SymbolicColumn column,Context z3Context) { 38 | Expr freshValue = z3Utility.constructFreshExpr(column.getSymbolicValue(), z3Context); 39 | BoolExpr freshNull = (BoolExpr) z3Utility.constructFreshExpr(column.getSymbolicNull(), z3Context); 40 | return (new SymbolicColumn(freshValue, freshNull, z3Context)); 41 | } 42 | public SymbolicColumn(Expr symbolicValue,BoolExpr symbolicNull,Context z3Context){ 43 | this.symbolicValue = symbolicValue; 44 | this.symbolicNull = symbolicNull; 45 | this.z3Context = z3Context; 46 | } 47 | 48 | static public List constructSymbolicTuple(List inputTypes,Context z3Context){ 49 | List result = new ArrayList<>(); 50 | for (RelDataType inputType : inputTypes) { 51 | result.add(SymbolicColumn.mkNewSymbolicColumn(z3Context, inputType)); 52 | } 53 | return result; 54 | } 55 | 56 | public Expr getSymbolicValue() { 57 | return symbolicValue; 58 | } 59 | 60 | public BoolExpr getSymbolicNull(){ 61 | return symbolicNull; 62 | } 63 | 64 | public BoolExpr isValueTrue(){ 65 | return z3Context.mkAnd((BoolExpr)symbolicValue,z3Context.mkNot(symbolicNull)); 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return "("+ this.symbolicValue+","+ this.symbolicNull+")"; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/SymbolicRexNode/UserDeFun.java: -------------------------------------------------------------------------------- 1 | package SymbolicRexNode; 2 | 3 | import Z3Helper.z3Utility; 4 | import com.microsoft.z3.*; 5 | import org.apache.calcite.rex.RexCall; 6 | import org.apache.calcite.rex.RexNode; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class UserDeFun extends RexNodeBase { 14 | public class SymbolicUserDefun { 15 | private String funName; 16 | private List args; 17 | private SymbolicColumn returnValue; 18 | public SymbolicUserDefun (String funName,List args,SymbolicColumn returnValue){ 19 | this.funName = funName; 20 | this.args = args; 21 | this.returnValue = returnValue; 22 | } 23 | 24 | public SymbolicColumn getReturnValue () { 25 | return this.returnValue; 26 | } 27 | 28 | public boolean isEq (String funName, List args){ 29 | if (this.funName.equals(funName)){ 30 | boolean result = z3Utility.symbolicOutputEqual(z3Context.mkTrue(),args,this.args,z3Context); 31 | return result; 32 | } 33 | return false; 34 | } 35 | } 36 | 37 | static private Map> registerFunction = new HashMap<>(); 38 | 39 | static public void reset1 (){ 40 | registerFunction = new HashMap<>(); 41 | } 42 | 43 | public UserDeFun(List inputs, RexNode node, Context z3Context){ 44 | super(inputs,node,z3Context); 45 | buildExpr(node); 46 | } 47 | 48 | private void buildExpr(RexNode node){ 49 | RexCall functionNode = (RexCall) node; 50 | String funName = functionNode.getOperator().getName(); 51 | List parameters = functionNode.getOperands(); 52 | List args = new ArrayList<>(); 53 | for(int i=0;i oldFuns = registerFunction.remove(funName); 60 | boolean findMatch = false; 61 | for (SymbolicUserDefun oldFun : oldFuns ){ 62 | if (oldFun.isEq(funName,args)){ 63 | this.output = oldFun.getReturnValue(); 64 | findMatch = true; 65 | break; 66 | } 67 | } 68 | if (!findMatch) { 69 | this.output = SymbolicColumn.mkNewSymbolicColumn(z3Context,node.getType()); 70 | SymbolicUserDefun symbolicUserDefun = new SymbolicUserDefun(funName,args,this.output); 71 | oldFuns.add(symbolicUserDefun); 72 | } 73 | registerFunction.put(funName,oldFuns); 74 | 75 | }else { 76 | List oldFuns = new ArrayList<>(); 77 | this.output = SymbolicColumn.mkNewSymbolicColumn(z3Context,node.getType()); 78 | SymbolicUserDefun symbolicUserDefun = new SymbolicUserDefun(funName,args,this.output); 79 | oldFuns.add(symbolicUserDefun); 80 | registerFunction.put(funName,oldFuns); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/Z3Helper/z3Utility.java: -------------------------------------------------------------------------------- 1 | package Z3Helper; 2 | import SymbolicRexNode.RexNodeUtility; 3 | import SymbolicRexNode.SymbolicColumn; 4 | import com.microsoft.z3.*; 5 | import org.apache.calcite.rel.type.RelDataType; 6 | 7 | import java.util.*; 8 | import java.util.stream.Collectors; 9 | 10 | 11 | public class z3Utility { 12 | static private int count = 0; 13 | 14 | static public void reset(){ 15 | count = 0; 16 | } 17 | 18 | static public boolean isConditionEq(List assignConstraints, BoolExpr condition1, BoolExpr condition2, Context z3Context) { 19 | BoolExpr[] equation = new BoolExpr[assignConstraints.size()+1]; 20 | assignConstraints.toArray(equation); 21 | equation[assignConstraints.size()] = z3Context.mkNot(z3Context.mkEq(condition1,condition2)); 22 | Solver s = z3Context.mkSolver(); 23 | s.add(equation); 24 | return s.check() == Status.UNSATISFIABLE; 25 | } 26 | 27 | static public BoolExpr makeColumnsEq(List columns1,List columns2,Context z3Context){ 28 | if (columns1.size() == columns2.size()) { 29 | BoolExpr[] columnsEq = new BoolExpr[columns1.size()]; 30 | for (int i=0; i list1, List list2, Context z3Context){ 45 | if(list1.size() == list2.size()) { 46 | List columnEqs = new ArrayList<>(); 47 | for(int i=0; i constraints,Context z3Context){ 82 | BoolExpr[] andC = new BoolExpr[constraints.size()]; 83 | constraints.toArray(andC); 84 | return (BoolExpr) z3Context.mkAnd(andC).simplify(); 85 | } 86 | 87 | public static BoolExpr mkOr(List constraints,Context z3Context){ 88 | BoolExpr[] orC = new BoolExpr[constraints.size()]; 89 | constraints.toArray(orC); 90 | return (BoolExpr) z3Context.mkOr(orC).simplify(); 91 | } 92 | 93 | public static boolean isUnsat(BoolExpr expr, Context z3Context){ 94 | Solver s = z3Context.mkSolver(); 95 | s.add(expr); 96 | return s.check() == Status.UNSATISFIABLE; 97 | } 98 | 99 | static public BoolExpr symbolicColumnEq(SymbolicColumn column1,SymbolicColumn column2,Context z3Context,boolean checkTrivialEq){ 100 | if (checkTrivialEq&&trivialEqual(column1,column2)){ 101 | return null; 102 | } 103 | BoolExpr bothNull = z3Context.mkAnd(column1.getSymbolicNull(),column2.getSymbolicNull()); 104 | BoolExpr valueEq = z3Context.mkAnd(z3Context.mkEq(column1.getSymbolicNull(),column2.getSymbolicNull()),z3Context.mkEq(column1.getSymbolicValue(),column2.getSymbolicValue())); 105 | return (BoolExpr) z3Context.mkOr(bothNull,valueEq).simplify(); 106 | } 107 | 108 | static private boolean trivialEqual (SymbolicColumn column1, SymbolicColumn column2){ 109 | if (trivialEqual(column1.getSymbolicValue(),column2.getSymbolicValue())){ 110 | if (trivialEqual(column1.getSymbolicNull(),column2.getSymbolicNull())){ 111 | return true; 112 | } 113 | } 114 | return false; 115 | 116 | } 117 | //TODO 118 | public static SymbolicColumn mkDumpSymbolicColumn(){ 119 | return null; 120 | } 121 | 122 | public static List collectAllConstantVariable(Expr e){ 123 | if (isVariable(e)) { 124 | return Collections.singletonList(e); 125 | } else { 126 | List result = new ArrayList<>(); 127 | for (Expr arg : e.getArgs()) { 128 | result.addAll(collectAllConstantVariable(arg)); 129 | } 130 | return result; 131 | } 132 | } 133 | 134 | public static Expr constructFreshExpr(Expr e,Context z3Context){ 135 | List variables = collectAllConstantVariable(e); 136 | Expr[] oldVariables = new Expr[variables.size()]; 137 | Expr[] newVariables = new Expr[variables.size()]; 138 | for(int i=0; i < variables.size(); i++){ 139 | Expr variable = variables.get(i); 140 | String name = variable.getSExpr(); 141 | Expr freshVariable = z3Context.mkConst(name+"Fresh",variable.getSort()); 142 | oldVariables[i] = variable; 143 | newVariables[i] = freshVariable; 144 | } 145 | return e.substitute(oldVariables,newVariables); 146 | } 147 | 148 | public static boolean isVariable(Expr e){ 149 | return e.isConst() && (!isConstant(e)); 150 | } 151 | 152 | public static boolean isConstant(Expr e){ 153 | return e.isTrue() || e.isFalse() || e.isIntNum() || e.isRatNum(); 154 | } 155 | 156 | static private boolean trivialEqual (Expr e1, Expr e2) { 157 | if (e1.isTrue() && e2.isTrue()){ 158 | return true; 159 | }else if (e1.isFalse() && e2.isFalse()){ 160 | return true; 161 | } else if (e1.isRatNum() && e2.isRatNum()) { 162 | RatNum e1V = (RatNum) e1; 163 | RatNum e2V = (RatNum) e2; 164 | return e1V.getBigIntNumerator().equals(e2V.getBigIntDenominator()); 165 | } else if (e1.isIntNum() && e2.isIntNum()){ 166 | IntNum e1V = (IntNum) e1; 167 | IntNum e2V = (IntNum) e2; 168 | return e1V.getBigInteger().equals(e2V.getBigInteger()); 169 | } else if (e1.isConst() && e2.isConst()){ 170 | return e1.getSExpr().equals(e2.getSExpr()); 171 | } else { 172 | return false; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/JoinEqualOuterJoin.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNodeParser.AlgeNodeParserPair; 5 | import AlgeRule.AlgeRule; 6 | import com.microsoft.z3.Context; 7 | import org.apache.calcite.rel.RelNode; 8 | 9 | public class JoinEqualOuterJoin { 10 | public static void main(String[] args) throws Exception{ 11 | Context z3Context = new Context(); 12 | simpleParser parser = new simpleParser(); 13 | RelNode newNode = parser.getRelNode("SELECT * FROM EMP LEFT JOIN DEPT ON EMP.DEPTNO = DEPT.DEPTNO WHERE DEPT.DEPTNO < 5"); 14 | AlgeNode algeExpr = AlgeRule.normalize(AlgeNodeParserPair.constructAlgeNode(newNode,z3Context)); 15 | System.out.println(algeExpr); 16 | simpleParser parser2 = new simpleParser(); 17 | RelNode newNode2 = parser2.getRelNode("SELECT * FROM EMP JOIN (SELECT * FROM DEPT WHERE DEPTNO < 5 ) as B on EMP.DEPTNO = B.DEPTNO "); 18 | AlgeNode algeExpr2 = AlgeRule.normalize(AlgeNodeParserPair.constructAlgeNode(newNode2,z3Context)); 19 | System.out.println("second one"); 20 | System.out.println(algeExpr2); 21 | System.out.println(algeExpr.isEq(algeExpr2)); 22 | //SimpleQueryTests.simpleParser parser2 = new SimpleQueryTests.simpleParser(); 23 | //parser2.explain("SELECT * FROM EMP"); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/SimpleAnalysis.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNodeParser.AlgeNodeParserPair; 5 | import AlgeRule.AlgeRule; 6 | import SymbolicRexNode.SymbolicColumn; 7 | import Z3Helper.z3Utility; 8 | import com.google.gson.JsonArray; 9 | import com.google.gson.JsonObject; 10 | import com.google.gson.JsonParser; 11 | import com.microsoft.z3.Context; 12 | import org.apache.calcite.plan.RelOptUtil; 13 | import org.apache.calcite.rel.RelNode; 14 | import org.apache.calcite.rel.core.JoinRelType; 15 | import org.apache.calcite.rel.logical.LogicalAggregate; 16 | import org.apache.calcite.rel.logical.LogicalJoin; 17 | import org.apache.calcite.sql.JoinType; 18 | 19 | import java.io.*; 20 | 21 | public class SimpleAnalysis { 22 | static long time = 0; 23 | static int SPJcount = 0; 24 | static long SPJTime = 0; 25 | static int aggCount = 0; 26 | static long aggTime = 0; 27 | static int outerJoinCount = 0; 28 | static long outerJoinTime = 0; 29 | 30 | 31 | public static boolean BeVerified (String sql1, String sql2, String name, PrintWriter cannotCompile, PrintWriter cannotProve, PrintWriter prove, PrintWriter bug) { 32 | if ((contains(sql1)) || (contains(sql2))) { 33 | return false; 34 | } 35 | RelNode logicPlan = null; 36 | RelNode logicPlan2 = null; 37 | boolean compile = false; 38 | try { 39 | z3Utility.reset(); 40 | simpleParser parser = new simpleParser(); 41 | simpleParser parser2 = new simpleParser(); 42 | 43 | logicPlan = parser.getRelNode(sql1); 44 | logicPlan2 = parser2.getRelNode(sql2); 45 | //System.out.println(RelOptUtil.toString(logicPlan)); 46 | //System.out.println(RelOptUtil.toString(logicPlan2)); 47 | compile = true; 48 | } catch (Exception e) { 49 | System.out.println("fail compile"); 50 | cannotCompile.println(name); 51 | cannotCompile.println("---------------------------------------------------"); 52 | cannotCompile.println(e); 53 | StackTraceElement[] reasons = e.getStackTrace(); 54 | for (int i = 0; i < reasons.length; i++) { 55 | cannotCompile.println(reasons[i].toString()); 56 | } 57 | cannotCompile.println("---------------------------------------------------"); 58 | return false; 59 | } 60 | if (compile) { 61 | Context z3Context = new Context(); 62 | try { 63 | long startTime = System.currentTimeMillis(); 64 | AlgeNode algeExpr = AlgeNodeParserPair.constructAlgeNode(logicPlan, z3Context); 65 | AlgeNode algeExpr2 = AlgeNodeParserPair.constructAlgeNode(logicPlan2, z3Context); 66 | algeExpr = AlgeRule.normalize(algeExpr); 67 | algeExpr2 = AlgeRule.normalize(algeExpr2); 68 | z3Utility.reset(); 69 | if (algeExpr.isEq(algeExpr2)) { 70 | long stopTime = System.currentTimeMillis(); 71 | long caseTime = (stopTime - startTime); 72 | time = caseTime + time; 73 | boolean uspj = true; 74 | if (hasAgg(logicPlan) || hasAgg(logicPlan2)){ 75 | aggCount++; 76 | aggTime = caseTime+aggTime; 77 | uspj = false; 78 | } 79 | if (outerJoin(logicPlan) || outerJoin(logicPlan2)){ 80 | outerJoinCount++; 81 | outerJoinTime = caseTime+outerJoinTime; 82 | uspj = false; 83 | } 84 | if (uspj){ 85 | SPJcount ++; 86 | SPJTime = caseTime+SPJTime; 87 | } 88 | prove.println(name); 89 | prove.flush(); 90 | z3Context.close(); 91 | return true; 92 | } else { 93 | cannotProve.println(name); 94 | cannotProve.println(RelOptUtil.toString(logicPlan)); 95 | cannotProve.println(RelOptUtil.toString(logicPlan2)); 96 | cannotProve.flush(); 97 | z3Context.close(); 98 | return false; 99 | } 100 | }catch(Exception e){ 101 | System.out.println("buggy in code"); 102 | z3Context.close(); 103 | bug.println(name); 104 | bug.println("---------------------------------------------------"); 105 | bug.println(e); 106 | StackTraceElement[] reasons = e.getStackTrace(); 107 | for (int i = 0; i < reasons.length; i++) { 108 | bug.println(reasons[i].toString()); 109 | } 110 | bug.println("---------------------------------------------------"); 111 | bug.flush(); 112 | return false; 113 | } 114 | } 115 | return false; 116 | } 117 | 118 | public static void main(String[] args) throws Exception { 119 | File f = new File("testData/calcite_tests.json"); 120 | JsonParser parser = new JsonParser(); 121 | JsonArray array = parser.parse(new FileReader(f)).getAsJsonArray(); 122 | FileWriter prove = new FileWriter("calciteProve.txt"); 123 | BufferedWriter bw = new BufferedWriter(prove); 124 | PrintWriter out = new PrintWriter(bw); 125 | FileWriter notProve = new FileWriter("cannotProve.txt"); 126 | BufferedWriter bw2 = new BufferedWriter(notProve); 127 | PrintWriter out2 = new PrintWriter(bw2); 128 | FileWriter notCompile = new FileWriter("cannotCompile.txt"); 129 | BufferedWriter bw3 = new BufferedWriter(notCompile); 130 | PrintWriter out3 = new PrintWriter(bw3); 131 | FileWriter bug = new FileWriter("bug.txt"); 132 | BufferedWriter bw4 = new BufferedWriter(bug); 133 | PrintWriter out4 = new PrintWriter(bw4); 134 | int count = 0; 135 | for(int i=0;i 10 AND DEPTNO < 5"); 14 | AlgeNode algeExpr = AlgeRule.normalize(AlgeNodeParserPair.constructAlgeNode(newNode,z3Context)); 15 | System.out.println(algeExpr); 16 | simpleParser parser2 = new simpleParser(); 17 | RelNode newNode2 = parser2.getRelNode("SELECT * FROM EMP WHERE DEPTNO > 7 AND DEPTNO < 4"); 18 | AlgeNode algeExpr2 = AlgeRule.normalize(AlgeNodeParserPair.constructAlgeNode(newNode2,z3Context)); 19 | System.out.println(algeExpr2); 20 | System.out.println(algeExpr.isEq(algeExpr2)); 21 | //SimpleQueryTests.simpleParser parser2 = new SimpleQueryTests.simpleParser(); 22 | //parser2.explain("SELECT * FROM EMP"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/SimpleLeftOuterJoin.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNodeParser.AlgeNodeParserPair; 5 | import SymbolicRexNode.SymbolicColumn; 6 | import com.microsoft.z3.Context; 7 | import org.apache.calcite.plan.RelOptUtil; 8 | import org.apache.calcite.rel.RelNode; 9 | 10 | public class SimpleLeftOuterJoin { 11 | public static void main(String[] args) throws Exception{ 12 | Context z3Context = new Context(); 13 | simpleParser parser = new simpleParser(); 14 | RelNode newNode = parser.getRelNode("SELECT * FROM EMP LEFt JOIN DEPT ON EMP.DEPTNO = DEPT.DEPTNO WHERE DEPT.DEPTNO > 10."); 15 | System.out.println(RelOptUtil.toString(newNode)); 16 | AlgeNode algeExpr = AlgeNodeParserPair.constructAlgeNode(newNode,z3Context); 17 | // System.out.println("let's check result here"); 18 | //System.out.println(algeExpr.getInputs().get(1)); 19 | //System.out.println(algeExpr); 20 | simpleParser parser2 = new simpleParser(); 21 | RelNode newNode2 = parser2.getRelNode("SELECT * FROM EMP LEFt JOIN DEPT ON EMP.DEPTNO = DEPT.DEPTNO WHERE DEPT.DEPTNO > 10."); 22 | AlgeNode algeExpr2 = AlgeNodeParserPair.constructAlgeNode(newNode2,z3Context); 23 | //System.out.println(algeExpr2); 24 | System.out.println(algeExpr.isEq(algeExpr2)); 25 | //SimpleQueryTests.simpleParser parser2 = new SimpleQueryTests.simpleParser(); 26 | //parser2.explain("SELECT * FROM EMP"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/SingleAnalysis.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNodeParser.AlgeNodeParserPair; 5 | import AlgeRule.AlgeRule; 6 | import com.google.gson.JsonArray; 7 | import com.google.gson.JsonObject; 8 | import com.google.gson.JsonParser; 9 | import com.microsoft.z3.Context; 10 | import org.apache.calcite.plan.RelOptUtil; 11 | import org.apache.calcite.rel.RelNode; 12 | import org.apache.calcite.sql.SqlExplainLevel; 13 | 14 | import java.io.*; 15 | 16 | public class SingleAnalysis { 17 | static long time = 0; 18 | public static boolean BeVerified(String sql1, String sql2, String name, PrintWriter cannotCompile, PrintWriter cannotProve, PrintWriter prove) 19 | throws Exception { 20 | if((contains(sql1))|| (contains(sql2))) { 21 | System.out.println("reason 1"); 22 | return false; 23 | } 24 | if((notUnionAll(sql1))|| (notUnionAll(sql2))) { 25 | System.out.println("reason 2"); 26 | return false; 27 | } 28 | System.out.println("it is here"); 29 | simpleParser parser = new simpleParser(); 30 | simpleParser parser2 = new simpleParser(); 31 | RelNode logicPlan = parser.getRelNode(sql1); 32 | RelNode logicPlan2 = parser2.getRelNode(sql2); 33 | System.out.println(RelOptUtil.toString(logicPlan)); 34 | System.out.println(RelOptUtil.toString(logicPlan2)); 35 | Context z3Context = new Context(); 36 | AlgeNode algeExpr = AlgeRule.normalize(AlgeNodeParserPair.constructAlgeNode(logicPlan,z3Context)); 37 | AlgeNode algeExpr2 = AlgeRule.normalize(AlgeNodeParserPair.constructAlgeNode(logicPlan2,z3Context)); 38 | System.out.println("this is here"); 39 | System.out.println(algeExpr); 40 | System.out.println(algeExpr2); 41 | long startTime = System.currentTimeMillis(); 42 | if(algeExpr.isEq(algeExpr2)){ 43 | long stopTime = System.currentTimeMillis(); 44 | time =(stopTime - startTime)+time; 45 | //System.out.println("time:"+time); 46 | System.out.println(RelOptUtil.toString(logicPlan,SqlExplainLevel.EXPPLAN_ATTRIBUTES)); 47 | System.out.println(RelOptUtil.toString(logicPlan2,SqlExplainLevel.EXPPLAN_ATTRIBUTES)); 48 | prove.println(name); 49 | prove.flush(); 50 | z3Context.close(); 51 | System.out.println("They are equivalent"); 52 | return true; 53 | }else{ 54 | //System.out.println("it fails"); 55 | //System.out.println(name); 56 | //System.out.println(RelOptUtil.toString(logicPlan,SqlExplainLevel.EXPPLAN_ATTRIBUTES)); 57 | //System.out.println(RelOptUtil.toString(logicPlan2,SqlExplainLevel.EXPPLAN_ATTRIBUTES)); 58 | cannotProve.println(name); 59 | cannotProve.flush(); 60 | z3Context.close(); 61 | System.out.println("We don't know"); 62 | return false; 63 | } 64 | } 65 | public static boolean simpleVerify(String sql1, String sql2){ 66 | RelNode logicPlan = null; 67 | RelNode logicPlan2 = null; 68 | try { 69 | simpleParser parser = new simpleParser(); 70 | logicPlan = parser.getRelNode(sql1); 71 | simpleParser parser2 = new simpleParser(); 72 | logicPlan2 = parser2.getRelNode(sql2); 73 | }catch (Exception e){ 74 | System.out.println(e); 75 | } 76 | Context z3Context = new Context(); 77 | AlgeNode algeExpr = AlgeNodeParserPair.constructAlgeNode(logicPlan,z3Context); 78 | AlgeNode algeExpr2 = AlgeNodeParserPair.constructAlgeNode(logicPlan2,z3Context); 79 | System.out.println(algeExpr); 80 | System.out.println(algeExpr2); 81 | return algeExpr.isEq(algeExpr2); 82 | } 83 | public static boolean simplify(String sql1, String sql2){ 84 | RelNode logicPlan = null; 85 | RelNode logicPlan2 = null; 86 | try { 87 | simpleParser parser = new simpleParser(); 88 | logicPlan = parser.getRelNode(sql1); 89 | simpleParser parser2 = new simpleParser(); 90 | logicPlan2 = parser2.getRelNode(sql2); 91 | }catch (Exception e){ 92 | System.out.println(e); 93 | } 94 | Context z3Context = new Context(); 95 | AlgeNode algeExpr =AlgeRule.normalize(AlgeNodeParserPair.constructAlgeNode(logicPlan,z3Context)); 96 | AlgeNode algeExpr2 =AlgeRule.normalize(AlgeNodeParserPair.constructAlgeNode(logicPlan2,z3Context)); 97 | System.out.println(algeExpr); 98 | System.out.println(algeExpr2); 99 | return false; 100 | } 101 | public static void main(String[] args) throws Exception { 102 | File f = new File("testData/calcite_tests.json"); 103 | JsonParser parser = new JsonParser(); 104 | JsonArray array = parser.parse(new FileReader(f)).getAsJsonArray(); 105 | FileWriter prove = new FileWriter("calciteProve.txt"); 106 | BufferedWriter bw = new BufferedWriter(prove); 107 | PrintWriter out = new PrintWriter(bw); 108 | FileWriter notProve = new FileWriter("tryToHandle.txt"); 109 | BufferedWriter bw2 = new BufferedWriter(notProve); 110 | PrintWriter out2 = new PrintWriter(bw2); 111 | FileWriter notCompile = new FileWriter("cannotCompile.txt"); 112 | BufferedWriter bw3 = new BufferedWriter(notCompile); 113 | PrintWriter out3 = new PrintWriter(bw3); 114 | int count = 0; 115 | for(int i=0;i 10 group by DEPTNO,MGR"); 13 | AlgeNode algeExpr = AlgeNodeParserPair.constructAlgeNode(newNode,z3Context); 14 | simpleParser parser2 = new simpleParser(); 15 | RelNode newNode2 = parser2.getRelNode("SELECT max(SAL) FROM EMP WHERE DEPTNO < 10 group by DEPTNO,MGR"); 16 | AlgeNode algeExpr2 = AlgeNodeParserPair.constructAlgeNode(newNode2,z3Context); 17 | System.out.println(algeExpr.isEq(algeExpr2)); 18 | //SimpleQueryTests.simpleParser parser2 = new SimpleQueryTests.simpleParser(); 19 | //parser2.explain("SELECT * FROM EMP"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/simpleExists.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNodeParser.AlgeNodeParserPair; 5 | import com.microsoft.z3.Context; 6 | import org.apache.calcite.plan.RelOptUtil; 7 | import org.apache.calcite.rel.RelNode; 8 | 9 | public class simpleExists { 10 | public static void main(String[] args) throws Exception{ 11 | simpleParser parser = new simpleParser(); 12 | RelNode newNode = parser.getRelNode("SELECT * FROM EMP WHERE EMP.DEPTNO IN (SELECT DEPT.DEPTNO FROM DEPT)"); 13 | System.out.println(RelOptUtil.toString(newNode)); 14 | AlgeNode algeExpr = AlgeNodeParserPair.constructAlgeNode(newNode,new Context()); 15 | System.out.println(algeExpr.toString()); 16 | //SimpleQueryTests.simpleParser parser2 = new SimpleQueryTests.simpleParser(); 17 | //parser2.explain("SELECT * FROM EMP"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/simpleFilter.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNodeParser.AlgeNodeParserPair; 5 | import SimpleQueryTests.simpleParser; 6 | import com.microsoft.z3.Context; 7 | import org.apache.calcite.rel.RelNode; 8 | 9 | public class simpleFilter { 10 | public static void main(String[] args) throws Exception{ 11 | Context z3Context = new Context(); 12 | simpleParser parser = new simpleParser(); 13 | RelNode newNode = parser.getRelNode("SELECT * FROM EMP WHERE DEPTNO > 10"); 14 | AlgeNode algeExpr = AlgeNodeParserPair.constructAlgeNode(newNode,z3Context); 15 | simpleParser parser2 = new simpleParser(); 16 | RelNode newNode2 = parser2.getRelNode("SELECT * FROM EMP WHERE DEPTNO > 10"); 17 | AlgeNode algeExpr2 = AlgeNodeParserPair.constructAlgeNode(newNode2,z3Context); 18 | System.out.println(algeExpr.constructQPSR(algeExpr2)); 19 | //SimpleQueryTests.simpleParser parser2 = new SimpleQueryTests.simpleParser(); 20 | //parser2.explain("SELECT * FROM EMP"); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/simpleJoin.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests; 2 | 3 | import AlgeNode.AlgeNode; 4 | import AlgeNodeParser.AlgeNodeParserPair; 5 | import com.microsoft.z3.Context; 6 | import org.apache.calcite.plan.RelOptUtil; 7 | import org.apache.calcite.rel.RelNode; 8 | 9 | public class simpleJoin { 10 | public static void main(String[] args) throws Exception{ 11 | Context z3Context = new Context(); 12 | simpleParser parser = new simpleParser(); 13 | RelNode newNode = parser.getRelNode("SELECT * FROM EMP INNER JOIN DEPT ON EMP.DEPTNO = DEPT.DEPTNO WHERE DEPT.DEPTNO > 10."); 14 | System.out.println(RelOptUtil.toString(newNode)); 15 | AlgeNode algeExpr = AlgeNodeParserPair.constructAlgeNode(newNode,z3Context); 16 | System.out.println(algeExpr); 17 | simpleParser parser2 = new simpleParser(); 18 | RelNode newNode2 = parser2.getRelNode("SELECT * FROM EMP INNER JOIN DEPT ON EMP.DEPTNO = DEPT.DEPTNO WHERE DEPT.DEPTNO > 10."); 19 | AlgeNode algeExpr2 = AlgeNodeParserPair.constructAlgeNode(newNode2,z3Context); 20 | System.out.println(algeExpr2); 21 | System.out.println(algeExpr.isEq(algeExpr2)); 22 | //SimpleQueryTests.simpleParser parser2 = new SimpleQueryTests.simpleParser(); 23 | //parser2.explain("SELECT * FROM EMP"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/simpleParser.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests; 2 | 3 | import SimpleQueryTests.tableSchema.ACCOUNT; 4 | import SimpleQueryTests.tableSchema.BONUS; 5 | import SimpleQueryTests.tableSchema.DEPT; 6 | import org.apache.calcite.adapter.java.JavaTypeFactory; 7 | import org.apache.calcite.jdbc.JavaTypeFactoryImpl; 8 | import org.apache.calcite.plan.RelOptUtil; 9 | import org.apache.calcite.rel.RelNode; 10 | import org.apache.calcite.rel.type.RelDataTypeSystem; 11 | import org.apache.calcite.schema.SchemaPlus; 12 | import org.apache.calcite.sql.SqlExplainLevel; 13 | import org.apache.calcite.sql.SqlNode; 14 | import org.apache.calcite.sql.parser.SqlParseException; 15 | import org.apache.calcite.sql2rel.SqlToRelConverter; 16 | import org.apache.calcite.tools.*; 17 | 18 | import SimpleQueryTests.tableSchema.EMP; 19 | 20 | import java.lang.reflect.Type; 21 | 22 | public class simpleParser { 23 | public static final JavaTypeFactory typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT); 24 | public static final SchemaPlus defaultSchema = Frameworks.createRootSchema(true); 25 | 26 | private FrameworkConfig config = Frameworks.newConfigBuilder().defaultSchema(defaultSchema).build(); 27 | private Planner planner = Frameworks.getPlanner(config); 28 | 29 | public simpleParser(){ 30 | addTableSchema(); 31 | } 32 | 33 | public void addTableSchema(){ 34 | SqlToRelConverter.configBuilder().build(); 35 | defaultSchema.add("EMP", new EMP()); 36 | defaultSchema.add("DEPT",new DEPT()); 37 | defaultSchema.add("BONUS",new BONUS()); 38 | defaultSchema.add("ACCOUNT",new ACCOUNT()); 39 | } 40 | 41 | public RelNode getRelNode(String sql) throws SqlParseException, ValidationException, RelConversionException{ 42 | SqlNode parse = planner.parse(sql); 43 | //System.out.println(parse.toString()); 44 | SqlToRelConverter.configBuilder().build(); 45 | SqlNode validate = planner.validate(parse); 46 | RelNode tree = planner.rel(validate).rel; 47 | //String plan = RelOptUtil.toString(tree,SqlExplainLevel.EXPPLAN_ATTRIBUTES); //explain(tree, SqlExplainLevel.ALL_ATTRIBUTES); 48 | //System.out.println(plan); 49 | return tree; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/simpleTest.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests; 2 | 3 | import org.apache.calcite.plan.RelOptUtil; 4 | import org.apache.calcite.rel.RelNode; 5 | 6 | import com.google.gson.JsonObject; 7 | import com.microsoft.z3.Context; 8 | 9 | import AlgeNode.AlgeNode; 10 | import AlgeNodeParser.AlgeNodeParserPair; 11 | import AlgeRule.AlgeRule; 12 | 13 | public class simpleTest { 14 | public static void main(String[] args){ 15 | String q1 = args[0]; 16 | String q2 = args[1]; 17 | JsonObject result = verify(q1,q2); 18 | System.out.println(result); 19 | } 20 | 21 | 22 | public static JsonObject verify(String sql1, String sql2){ 23 | JsonObject result = new JsonObject(); 24 | if((contains(sql1))|| (contains(sql2))) { 25 | result.addProperty("decision","unknown"); 26 | result.addProperty("reason","sql feature not support"); 27 | return result; 28 | } 29 | simpleParser parser = new simpleParser(); 30 | simpleParser parser2 = new simpleParser(); 31 | RelNode logicPlan = null; 32 | RelNode logicPlan2 = null; 33 | try { 34 | logicPlan = parser.getRelNode(sql1); 35 | logicPlan2 = parser2.getRelNode(sql2); 36 | }catch (Exception e){ 37 | result.addProperty("decision","unknown"); 38 | result.addProperty("reason","syntax error in sql"); 39 | return result; 40 | } 41 | AlgeNode algeExpr = null; 42 | AlgeNode algeExpr2 = null; 43 | try { 44 | System.out.println(RelOptUtil.toString(logicPlan)); 45 | System.out.println(RelOptUtil.toString(logicPlan2)); 46 | Context z3Context = new Context(); 47 | algeExpr = AlgeRule.normalize(AlgeNodeParserPair.constructAlgeNode(logicPlan, z3Context)); 48 | algeExpr2 = AlgeRule.normalize(AlgeNodeParserPair.constructAlgeNode(logicPlan2, z3Context)); 49 | }catch (Exception e){ 50 | result.addProperty("decision","unknown"); 51 | result.addProperty("reason","sql feature not support"); 52 | return result; 53 | } 54 | try { 55 | if (algeExpr.isEq(algeExpr2)) { 56 | result.addProperty("decision","true"); 57 | } else { 58 | result.addProperty("decision","false"); 59 | } 60 | result.addProperty("plan1",RelOptUtil.toString(logicPlan)); 61 | result.addProperty("plan2",RelOptUtil.toString(logicPlan2)); 62 | }catch (Exception e){ 63 | result.addProperty("decision","unknown"); 64 | result.addProperty("reason","unknown"); 65 | }finally { 66 | return result; 67 | } 68 | } 69 | 70 | static public boolean contains(String sql){ 71 | String[] keyWords ={"VALUE","EXISTS","ROW","ORDER","CAST","INTERSECT","EXCEPT"," IN "}; 72 | for (String keyWord : keyWords) { 73 | if (sql.contains(keyWord)) { 74 | return true; 75 | } 76 | } 77 | return false; 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/tableSchema/ACCOUNT.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests.tableSchema; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.apache.calcite.config.CalciteConnectionConfig; 5 | import org.apache.calcite.rel.RelCollations; 6 | import org.apache.calcite.rel.RelFieldCollation; 7 | import org.apache.calcite.rel.type.RelDataType; 8 | import org.apache.calcite.rel.type.RelDataTypeFactory; 9 | import org.apache.calcite.schema.Schema; 10 | import org.apache.calcite.schema.Statistic; 11 | import org.apache.calcite.schema.Statistics; 12 | import org.apache.calcite.schema.Table; 13 | import org.apache.calcite.sql.SqlCall; 14 | import org.apache.calcite.sql.SqlNode; 15 | import org.apache.calcite.util.ImmutableBitSet; 16 | 17 | public class ACCOUNT implements Table { 18 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 19 | RelDataTypeFactory.FieldInfoBuilder b = typeFactory.builder(); 20 | b.add("ACCTNO", typeFactory.createJavaType(Integer.class)); 21 | b.add("TYPE", typeFactory.createJavaType(String.class)); 22 | b.add("BALANCE", typeFactory.createJavaType(String.class)); 23 | return b.build(); 24 | } 25 | @Override 26 | public boolean isRolledUp(String s) { 27 | return false; 28 | } 29 | @Override 30 | public boolean rolledUpColumnValidInsideAgg(String s, SqlCall sqlCall, SqlNode sqlNode, CalciteConnectionConfig calciteConnectionConfig) { 31 | return false; 32 | } 33 | public Statistic getStatistic() { 34 | // return Statistics.of(100, ImmutableList.of()); 35 | RelFieldCollation.Direction dir = RelFieldCollation.Direction.ASCENDING; 36 | RelFieldCollation collation = new RelFieldCollation(0, dir, RelFieldCollation.NullDirection.UNSPECIFIED); 37 | return Statistics.of(5, ImmutableList.of(ImmutableBitSet.of(0)), 38 | ImmutableList.of(RelCollations.of(collation))); 39 | } 40 | public Schema.TableType getJdbcTableType() { 41 | return Schema.TableType.STREAM; 42 | } 43 | 44 | public Table stream() { 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/tableSchema/BONUS.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests.tableSchema; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.apache.calcite.config.CalciteConnectionConfig; 5 | import org.apache.calcite.rel.RelCollations; 6 | import org.apache.calcite.rel.RelFieldCollation; 7 | import org.apache.calcite.rel.type.RelDataType; 8 | import org.apache.calcite.rel.type.RelDataTypeFactory; 9 | import org.apache.calcite.schema.Schema; 10 | import org.apache.calcite.schema.Statistic; 11 | import org.apache.calcite.schema.Statistics; 12 | import org.apache.calcite.schema.Table; 13 | import org.apache.calcite.sql.SqlCall; 14 | import org.apache.calcite.sql.SqlNode; 15 | import org.apache.calcite.util.ImmutableBitSet; 16 | 17 | public class BONUS implements Table { 18 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 19 | RelDataTypeFactory.FieldInfoBuilder b = typeFactory.builder(); 20 | b.add("ENAME", typeFactory.createJavaType(Integer.class)); 21 | b.add("JOB", typeFactory.createJavaType(String.class)); 22 | b.add("SAL", typeFactory.createJavaType(String.class)); 23 | b.add("COMM", typeFactory.createJavaType(String.class)); 24 | return b.build(); 25 | } 26 | @Override 27 | public boolean isRolledUp(String s) { 28 | return false; 29 | } 30 | @Override 31 | public boolean rolledUpColumnValidInsideAgg(String s, SqlCall sqlCall, SqlNode sqlNode, CalciteConnectionConfig calciteConnectionConfig) { 32 | return false; 33 | } 34 | public Statistic getStatistic() { 35 | // return Statistics.of(100, ImmutableList.of()); 36 | RelFieldCollation.Direction dir = RelFieldCollation.Direction.ASCENDING; 37 | RelFieldCollation collation = new RelFieldCollation(0, dir, RelFieldCollation.NullDirection.UNSPECIFIED); 38 | return Statistics.of(5, ImmutableList.of(ImmutableBitSet.of(0)), 39 | ImmutableList.of(RelCollations.of(collation))); 40 | } 41 | public Schema.TableType getJdbcTableType() { 42 | return Schema.TableType.STREAM; 43 | } 44 | 45 | public Table stream() { 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/tableSchema/DEPT.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests.tableSchema; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.apache.calcite.config.CalciteConnectionConfig; 5 | import org.apache.calcite.rel.RelCollations; 6 | import org.apache.calcite.rel.RelFieldCollation; 7 | import org.apache.calcite.rel.type.RelDataType; 8 | import org.apache.calcite.rel.type.RelDataTypeFactory; 9 | import org.apache.calcite.schema.Schema; 10 | import org.apache.calcite.schema.Statistic; 11 | import org.apache.calcite.schema.Statistics; 12 | import org.apache.calcite.schema.Table; 13 | import org.apache.calcite.sql.SqlCall; 14 | import org.apache.calcite.sql.SqlNode; 15 | import org.apache.calcite.util.ImmutableBitSet; 16 | 17 | public class DEPT implements Table{ 18 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 19 | RelDataTypeFactory.FieldInfoBuilder b = typeFactory.builder(); 20 | b.add("DEPTNO", typeFactory.createJavaType(Integer.class)); 21 | b.add("NAME", typeFactory.createJavaType(String.class)); 22 | return b.build(); 23 | } 24 | @Override 25 | public boolean isRolledUp(String s) { 26 | return false; 27 | } 28 | @Override 29 | public boolean rolledUpColumnValidInsideAgg(String s, SqlCall sqlCall, SqlNode sqlNode, CalciteConnectionConfig calciteConnectionConfig) { 30 | return false; 31 | } 32 | public Statistic getStatistic() { 33 | // return Statistics.of(100, ImmutableList.of()); 34 | RelFieldCollation.Direction dir = RelFieldCollation.Direction.ASCENDING; 35 | RelFieldCollation collation = new RelFieldCollation(0, dir, RelFieldCollation.NullDirection.UNSPECIFIED); 36 | return Statistics.of(5, ImmutableList.of(ImmutableBitSet.of(0)), 37 | ImmutableList.of(RelCollations.of(collation))); 38 | } 39 | public Schema.TableType getJdbcTableType() { 40 | return Schema.TableType.STREAM; 41 | } 42 | 43 | public Table stream() { 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/tableSchema/EMP.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests.tableSchema; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.apache.calcite.config.CalciteConnectionConfig; 5 | import org.apache.calcite.rel.RelCollations; 6 | import org.apache.calcite.rel.RelFieldCollation; 7 | import org.apache.calcite.rel.type.RelDataType; 8 | import org.apache.calcite.rel.type.RelDataTypeFactory; 9 | import org.apache.calcite.schema.Schema; 10 | import org.apache.calcite.schema.Statistic; 11 | import org.apache.calcite.schema.Statistics; 12 | import org.apache.calcite.schema.Table; 13 | import org.apache.calcite.sql.SqlCall; 14 | import org.apache.calcite.sql.SqlNode; 15 | import org.apache.calcite.util.ImmutableBitSet; 16 | 17 | public class EMP implements Table{ 18 | 19 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 20 | RelDataTypeFactory.FieldInfoBuilder b = typeFactory.builder(); 21 | b.add("EMPNO", typeFactory.createJavaType(Integer.class)); 22 | b.add("ENAME", typeFactory.createJavaType(String.class)); 23 | b.add("JOB", typeFactory.createJavaType(String.class)); 24 | b.add("MGR", typeFactory.createJavaType(Integer.class)); 25 | b.add("HIREDATE", typeFactory.createJavaType(Integer.class)); 26 | b.add("COMM", typeFactory.createJavaType(Integer.class)); 27 | b.add("SAL", typeFactory.createJavaType(Integer.class)); 28 | b.add("DEPTNO", typeFactory.createJavaType(Integer.class)); 29 | b.add("SLACKER", typeFactory.createJavaType(Integer.class)); 30 | return b.build(); 31 | } 32 | @Override 33 | public boolean isRolledUp(String s) { 34 | return false; 35 | } 36 | @Override 37 | public boolean rolledUpColumnValidInsideAgg(String s, SqlCall sqlCall, SqlNode sqlNode, CalciteConnectionConfig calciteConnectionConfig) { 38 | return false; 39 | } 40 | public Statistic getStatistic() { 41 | // return Statistics.of(100, ImmutableList.of()); 42 | RelFieldCollation.Direction dir = RelFieldCollation.Direction.ASCENDING; 43 | RelFieldCollation collation = new RelFieldCollation(0, dir, RelFieldCollation.NullDirection.UNSPECIFIED); 44 | return Statistics.of(5, ImmutableList.of(ImmutableBitSet.of(0)), 45 | ImmutableList.of(RelCollations.of(collation))); 46 | } 47 | public Schema.TableType getJdbcTableType() { 48 | return Schema.TableType.STREAM; 49 | } 50 | 51 | public Table stream() { 52 | return null; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/SimpleQueryTests/testOftest.java: -------------------------------------------------------------------------------- 1 | package SimpleQueryTests; 2 | 3 | import org.apache.calcite.plan.hep.HepPlanner; 4 | import org.apache.calcite.plan.hep.HepProgram; 5 | import org.apache.calcite.plan.hep.HepProgramBuilder; 6 | import org.apache.calcite.rel.RelNode; 7 | import org.apache.calcite.rel.rules.ReduceExpressionsRule; 8 | 9 | public class testOftest { 10 | public static void main(String[] args) throws Exception{ 11 | HepProgram preProgram = new HepProgramBuilder() 12 | .build(); 13 | 14 | HepProgramBuilder builder = new HepProgramBuilder(); 15 | builder.addRuleClass(ReduceExpressionsRule.class); 16 | HepPlanner hepPlanner = new HepPlanner(builder.build()); 17 | hepPlanner.addRule(ReduceExpressionsRule.FILTER_INSTANCE); 18 | 19 | final String sql = "select sum(sal)\n" 20 | + "from emp"; 21 | simpleParser parser = new simpleParser(); 22 | RelNode newNode = parser.getRelNode(sql); 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/z3Test1.java: -------------------------------------------------------------------------------- 1 | import Z3Helper.z3Utility; 2 | import com.microsoft.z3.*; 3 | 4 | public class z3Test1 { 5 | public static void main(String[] args) { 6 | Context z3 = new Context(); 7 | IntExpr zero = z3.mkInt(0); 8 | IntExpr one = z3.mkInt(1); 9 | IntExpr variable = z3.mkIntConst("a"); 10 | IntExpr variable2 = z3.mkIntConst("c"); 11 | IntExpr variable3 = z3.mkIntConst("b"); 12 | System.out.println(variable.toString()); 13 | BoolExpr greater = z3.mkGe(variable,zero); 14 | BoolExpr lessthan = z3.mkLt(variable2,one); 15 | BoolExpr formula = z3.mkAnd(greater,lessthan); 16 | System.out.println(greater); 17 | Expr[] copy =new Expr[2]; 18 | copy[0] = variable; 19 | copy[1] = variable2; 20 | Expr[] beCopied =new Expr[2]; 21 | beCopied[0] = variable3; 22 | beCopied[1] = variable3; 23 | System.out.println(formula); 24 | System.out.println(z3Utility.constructFreshExpr(formula,z3)); 25 | Solver s = z3.mkSolver(); 26 | s.add(greater); 27 | Status result = s.check(); 28 | System.out.println(result); 29 | 30 | } 31 | } 32 | --------------------------------------------------------------------------------