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;
  }