code: @Override
  	public Plan createPlan(QueryData data, Transaction tx) {
  		// Step 1: Create a TablePlanner object for each mentioned table/view
  		int id = 0;
  		for (String tbl : data.tables()) {
  			String viewdef = VanillaDb.catalogMgr().getViewDef(tbl, tx);
  			if (viewdef != null)
  				views.add(VanillaDb.newPlanner().createQueryPlan(viewdef, tx));
  			else {
  				TablePlanner tp = new TablePlanner(tbl, data.pred(), tx, id);
  				tablePlanners.add(tp);
  				id += 1;
  			}
  		}
  		// step 2: Use Selinger optimization to find join access path
  		Plan trunk = getAccessPath(); 
  		// Step 3: Add a group by plan if specified
  		if (data.groupFields() != null)
  			trunk = new GroupByPlan(trunk, data.groupFields(),
  					data.aggregationFn(), tx);
  		// Step 4. Project on the field names
  		trunk = new ProjectPlan(trunk, data.projectFields());
  		// Step 5: Add a sort plan if specified
  		if (data.sortFields() != null)
  			trunk = new SortPlan(trunk, data.sortFields(),
  					data.sortDirections(), tx);
  		// Step 6: Add a explain plan if the query is explain statement
  		if (data.isExplain())
  			trunk = new ExplainPlan(trunk);
  		return trunk;
  	}
docs: Creates a left-deep query plan using the Selinger optimization.
  Main idea is to find all permutation of table join order and to
  choose the cheapest plan. However, all permutation can be too
  much for us to go through all. So we use DP and left-deep only
  to optimize this process.