private boolean columnMatched(TableColumn c1, TableColumn c2) {
   Utils.checkTrue(!c1.getFullName().equals(c2.getFullName()));
   if (matchingName) {
     return c1.getColumnName().equals(c2.getColumnName());
   }
   return true;
 }
  public SQLSkeleton inferSQLSkeleton() {

    SQLSkeleton skeleton = new SQLSkeleton(this.inputTables, this.outputTable);

    // first get all tables
    List<TableInstance> tables = new LinkedList<TableInstance>();
    List<TableColumn> outputColumns = outputTable.getColumns();
    // FIXME no consider repetitive columns
    //		for(TableColumn column : outputColumns) {
    //			TableInstance t = TableUtils.findFirstTableWithMatchedColumn(column.getColumnName(),
    // this.inputTables);
    //			if(t != null && !tables.contains(t)) {
    //				tables.add(t);
    //			}
    //		}
    tables.addAll(this.inputTables);
    skeleton.addTables(tables);
    Utils.checkTrue(skeleton.getTables().size() >= this.inputTables.size());

    // all possible joining conditions
    // join with keys
    List<Pair<TableColumn, TableColumn>> p1list = this.getKeyPairs(tables);
    // join by columns, with name
    List<Pair<TableColumn, TableColumn>> p2list = this.getNameMatchedPairs(tables);

    // FIXME whether we should add columns with different names
    // but the same type? controlled by the flag of matchingNames.

    if (p1list != null && !p1list.isEmpty()) {
      for (Pair<TableColumn, TableColumn> p1 : p1list) {
        skeleton.addJoinColumns(p1);
      }
    }
    if (p2list != null && !p2list.isEmpty()) {
      for (Pair<TableColumn, TableColumn> p2 : p2list) {
        skeleton.addJoinColumns(p2);
      }
    }

    // all projection columns
    int index = 0;
    for (TableColumn column : outputColumns) {
      // the first column with the same name
      TableColumn c = TableUtils.findFirstMatchedColumn(column.getColumnName(), this.inputTables);
      if (c != null) {
        skeleton.setProjectColumn(index, c);
      }
      index++;
    }

    return skeleton;
  }
  /**
   * Return all pairs that is not in the same table but with the same type and the same column name
   */
  private List<Pair<TableColumn, TableColumn>> getNameMatchedPairs(List<TableInstance> tables) {
    if (tables.size() == 1) {
      return null;
    }

    List<Pair<TableColumn, TableColumn>> pairs = new LinkedList<Pair<TableColumn, TableColumn>>();
    List<TableColumn> allColumns = new LinkedList<TableColumn>();
    for (TableInstance t : tables) {
      allColumns.addAll(t.getColumns());
    }
    for (int i = 0; i < allColumns.size(); i++) {
      for (int j = i; j < allColumns.size(); j++) {
        TableColumn c1 = allColumns.get(i);
        TableColumn c2 = allColumns.get(j);
        if (c1.isKey() && c2.isKey()) {
          continue;
        }
        if (TableUtils.sameType(c1, c2)
            && !c1.getTableName().equals(c2.getTableName())
            && this.columnMatched(c1, c2)) {
          Pair<TableColumn, TableColumn> p = new Pair<TableColumn, TableColumn>(c1, c2);
          pairs.add(p);
        }
      }
    }

    return pairs;
  }