/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package org.voltdb.planner; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.json_voltpatches.JSONException; import org.json_voltpatches.JSONObject; import org.voltdb.plannodes.AbstractPlanNode; import org.voltdb.plannodes.AbstractScanPlanNode; import org.voltdb.plannodes.SendPlanNode; import org.voltdb.types.PlanNodeType; public class testPlannerTester extends PlannerTestCase { private String m_currentDir; private String m_homeDir; @Override protected void setUp() throws Exception { setupSchema(testPlannerTester.class.getResource("testplans-plannerTester-ddl.sql"), "testplans-plannerTester-ddl", true); m_currentDir = new File(".").getCanonicalPath() + "/"; m_homeDir = System.getProperty("user.home") + "/"; } /// Unit test some of the techniques used by plannerTester public void testGetScanNodeList() { AbstractPlanNode pn = null; pn = compile("select * from l where lname=? and b=0 order by id asc limit ?;"); ArrayList<AbstractScanPlanNode> collected = pn.getScanNodeList(); System.out.println(collected); System.out.println(collected.size()); for (AbstractPlanNode n : collected) { System.out.println(n.toExplainPlanString()); } assertTrue(collected.size() == 1); JSONObject j; try { j = new JSONObject(collected.get(0).toJSONString()); System.out.println(j.getString("PLAN_NODE_TYPE")); assertTrue(j.getString("PLAN_NODE_TYPE").equalsIgnoreCase("INDEXSCAN")); } catch (JSONException e) { e.printStackTrace(); } } public void testReAttachFragments() { try { plannerTester.setUpForTest(m_currentDir + "tests/frontend/org/voltdb/planner/testplans-plannerTester-ddl.sql", "testplans-plannerTester-ddl"); List<AbstractPlanNode> pnList = plannerTester.testCompile("select * from l, t where t.a=l.a limit ?;"); System.out.println(pnList.size()); System.out.println(pnList.get(0).toExplainPlanString()); assert(pnList.size() == 2); assert(pnList.get(1) instanceof SendPlanNode); if (pnList.get(0).reattachFragment(pnList.get(1))) { AbstractPlanNode pn = pnList.get(0); System.out.println(pn.toExplainPlanString()); assertTrue(pn.toExplainPlanString().contains("SEND PARTITION RESULTS TO COORDINATOR")); } } catch (Exception e) { e.printStackTrace(); } } public void testWriteAndLoad() throws Exception { AbstractPlanNode pn = null; plannerTester.setUpForTest(m_currentDir + "tests/frontend/org/voltdb/planner/testplans-plannerTester-ddl.sql", "testplans-plannerTester-ddl"); List<AbstractPlanNode> pnList = plannerTester.testCompile("select * from l, t where t.a=l.a limit ?;"); System.out.println(pnList.size()); pn = pnList.get(0); assert(pnList.get(1) instanceof SendPlanNode); pn.reattachFragment(pnList.get(1)); System.out.println(pn.toJSONString()); System.out.println(pn.toExplainPlanString()); plannerTester.writePlanToFile(pn, m_homeDir, "prettyJson.txt", ""); ArrayList<String> getsql = new ArrayList<String>(); AbstractPlanNode pn2 = plannerTester.loadPlanFromFile(m_homeDir + "prettyJson.txt", getsql); System.out.println(pn2.toExplainPlanString()); ArrayList<AbstractPlanNode> list1 = pn.getPlanNodeList(); ArrayList<AbstractPlanNode> list2 = pn2.getPlanNodeList(); assertTrue(list1.size() == list2.size()); for (int i = 0; i < list1.size(); i++) { Map<PlanNodeType, AbstractPlanNode> inlineNodes1 = list1.get(i).getInlinePlanNodes(); Map<PlanNodeType, AbstractPlanNode> inlineNodes2 = list2.get(i).getInlinePlanNodes(); if (inlineNodes1 != null) { assertTrue(inlineNodes1.size() == inlineNodes2.size()); } } } public void testLoadJoinType() throws FileNotFoundException { AbstractPlanNode pn = null; pn = compile("select * from l, t where l.b=t.b limit ?;"); plannerTester.setUpForTest(m_currentDir + "tests/frontend/org/voltdb/planner/testplans-plannerTester-ddl.sql", "testplans-plannerTester-ddl"); System.out.println(pn.toExplainPlanString()); System.out.println(pn.toJSONString()); plannerTester.writePlanToFile(pn, m_homeDir, "prettyJson.txt", ""); ArrayList<String> getsql = new ArrayList<>(); AbstractPlanNode pn2 = plannerTester.loadPlanFromFile(m_homeDir + "prettyJson.txt", getsql); System.out.println(pn2.toExplainPlanString()); ArrayList<AbstractPlanNode> list1 = pn.getPlanNodeList(); ArrayList<AbstractPlanNode> list2 = pn2.getPlanNodeList(); assertTrue(list1.size() == list2.size()); for (int i = 0; i < list1.size(); i++) { Map<PlanNodeType, AbstractPlanNode> inlineNodes1 = list1.get(i).getInlinePlanNodes(); Map<PlanNodeType, AbstractPlanNode> inlineNodes2 = list2.get(i).getInlinePlanNodes(); if (inlineNodes1 != null) { assertTrue(inlineNodes1.size() == inlineNodes2.size()); } } } /// Unit test some of the techniques used by plannerTester public void testGetList() { AbstractPlanNode pn1 = null; pn1 = compile("select * from l, t where t.b=l.b limit ?;"); ArrayList<AbstractPlanNode> pnlist = pn1.getPlanNodeList(); System.out.println(pn1.toExplainPlanString()); System.out.println(pnlist.size()); for (int i = 0; i < pnlist.size(); i++) { System.out.println(pnlist.get(i).toJSONString()); } assertTrue(pnlist.size() == 6); } public void testScanDiff() { assertTrue(compileDiffMatchPattern("select * from t where a = ?;", "select * from l,t where l.a = t.a order by b limit ?;", "Scan time diff :", "(1 => 2)", "Table diff at leaf 0:", "(INDEXSCAN on T => INDEXSCAN on L)", "Table diff at leaf 1:", "([] => INDEXSCAN on T)")); } /// Unit test plannerTester as a plan diff engine. public void testJoinDiff() { assertTrue(compileDiffMatchPattern("select count(*) from t;", "select * from l,t where l.a = t.a order by b limit ?;", "Join Node List diff:", "([] => NESTLOOPINDEX[5])")); assertTrue(compileDiffMatchPattern("select * from l, t where t.a=l.a;", "select * from l, t where t.b=l.b order by a limit ?;", "Join Node Type diff:", "(NESTLOOPINDEX[3] => NESTLOOP[5])")); } /// Unit test plannerTester as a plan diff engine. public void testPlanNodeAndInlinePositionDiff() { assertTrue(compileDiffMatchPattern("select * from l order by a;", "select * from l order by a limit ?;", "ORDERBY diff:", "([3] => [4])", "INDEXSCAN diff:", "([4] => [5])", "PROJECTION diff:", "([2] => [3])", "LIMIT diff:" , "([] => [2])", "Inline PROJECTION diff:", "([INDEXSCAN[4]] => [INDEXSCAN[5]])")); } /// Unit test plannerTester as a plan diff engine. public void testComprehensiveDiff() { assertTrue(compileDiffMatchPattern("select * from l, t where t.a=l.a;", "select * from l, t where t.b=l.b order by a limit ?;", "Table diff at leaf 0:", "(INDEXSCAN on L => SEQSCAN on T)", "Table diff at leaf 1:", "(INDEXSCAN on T => INDEXSCAN on L)", "Plan tree size diff: (4 => 7)", "ORDERBY diff:", "([] => [4])", "NESTLOOP diff:", "([] => [5])", "SEQSCAN diff:", "([] => [6])", "PROJECTION diff:", "([2] => [3])", "INDEXSCAN diff:", "([4] => [7])", "LIMIT diff:", "([] => [2])", "NESTLOOPINDEX diff:", "([3] => [])", "Inline INDEXSCAN diff:", "([NESTLOOPINDEX[3]] => [])", "Inline PROJECTION diff:", "([INDEXSCAN[4]] => [SEQSCAN[6], INDEXSCAN[7]])", "Join Node Type diff:", "(NESTLOOPINDEX[3] => NESTLOOP[5])")); } public boolean compileDiffMatchPattern(String sql1, String sql2, String... patterns) { AbstractPlanNode pn1 = compile(sql1); AbstractPlanNode pn2 = compile(sql2); plannerTester.diff(pn1, pn2, true); int numMatched = 0; for (String str : plannerTester.m_diffMessages) { String[] splits = str.split("\n"); for (String split : splits) { int wasMatched = numMatched; for (String pattern : patterns) { if (split.trim().equals(pattern)) { numMatched++; break; } } if (wasMatched == numMatched) { System.out.println("Skipped harmless noise? :" + split); } } } return (numMatched == patterns.length); } }