Example #1
0
  /**
   * Columns from USING expression are unqualified. In case of INNER join, it doesn't matter we can
   * pick the first table which contains the input column. In case of OUTER joins, we must the OUTER
   * table - if it's a null-able column the outer join must return them.
   *
   * @param columnName
   * @return table name this column belongs to
   */
  protected void resolveUsingColumns(VoltXMLElement columns, RangeVariable[] rvs)
      throws HSQLParseException {
    // Only one OUTER join for a whole select is supported so far
    for (VoltXMLElement columnElmt : columns.children) {
      boolean innerJoin = true;
      String table = null;
      if (columnElmt.attributes.get("table") == null) {
        for (RangeVariable rv : rvs) {
          if (rv.isLeftJoin || rv.isRightJoin) {
            if (innerJoin == false) {
              throw new HSQLParseException(
                  "VoltDB does not support outer joins with more than two tables involved");
            }
            innerJoin = false;
          }

          if (!rv.getTable().columnList.containsKey(columnElmt.attributes.get("column"))) {
            // The column is not from this table. Skip it
            continue;
          }

          // If there is an OUTER join we need to pick the outer table
          if (rv.isRightJoin == true) {
            // this is the outer table. no need to search further.
            table = rv.getTable().getName().name;
            break;
          } else if (rv.isLeftJoin == false) {
            // it's the inner join. we found the table but still need to iterate
            // just in case there is an outer table we haven't seen yet.
            table = rv.getTable().getName().name;
          }
        }
        if (table != null) {
          columnElmt.attributes.put("table", table);
        }
      }
    }
  }
Example #2
0
  VoltXMLElement voltGetXMLSpecification(QuerySpecification select, Session session)
      throws HSQLParseException {

    // select
    VoltXMLElement query = new VoltXMLElement("select");
    if (select.isDistinctSelect) query.attributes.put("distinct", "true");

    // limit
    if ((select.sortAndSlice != null) && (select.sortAndSlice.limitCondition != null)) {
      Expression limitCondition = select.sortAndSlice.limitCondition;
      if (limitCondition.nodes.length != 2) {
        throw new HSQLParseException(
            "Parser did not create limit and offset expression for LIMIT.");
      }
      try {
        // read offset. it may be a parameter token.
        if (limitCondition.nodes[0].isParam() == false) {
          Integer offset = (Integer) limitCondition.nodes[0].getValue(session);
          if (offset > 0) {
            query.attributes.put("offset", offset.toString());
          }
        } else {
          query.attributes.put("offset_paramid", limitCondition.nodes[0].getUniqueId(session));
        }

        // read limit. it may be a parameter token.
        if (limitCondition.nodes[1].isParam() == false) {
          Integer limit = (Integer) limitCondition.nodes[1].getValue(session);
          query.attributes.put("limit", limit.toString());
        } else {
          query.attributes.put("limit_paramid", limitCondition.nodes[1].getUniqueId(session));
        }
      } catch (HsqlException ex) {
        // XXX really?
        ex.printStackTrace();
      }
    }

    // columns that need to be output by the scans
    VoltXMLElement scanCols = new VoltXMLElement("scan_columns");
    query.children.add(scanCols);
    assert (scanCols != null);

    // Just gather a mish-mash of every possible relevant expression
    // and uniq them later
    HsqlList col_list = new HsqlArrayList();
    select.collectAllExpressions(
        col_list, Expression.columnExpressionSet, Expression.emptyExpressionSet);
    if (select.queryCondition != null) {
      Expression.collectAllExpressions(
          col_list,
          select.queryCondition,
          Expression.columnExpressionSet,
          Expression.emptyExpressionSet);
    }
    for (int i = 0; i < select.exprColumns.length; i++) {
      Expression.collectAllExpressions(
          col_list,
          select.exprColumns[i],
          Expression.columnExpressionSet,
          Expression.emptyExpressionSet);
    }
    for (RangeVariable rv : select.rangeVariables) {
      if (rv.indexCondition != null) {
        Expression.collectAllExpressions(
            col_list,
            rv.indexCondition,
            Expression.columnExpressionSet,
            Expression.emptyExpressionSet);
      }
      if (rv.indexEndCondition != null) {
        Expression.collectAllExpressions(
            col_list,
            rv.indexEndCondition,
            Expression.columnExpressionSet,
            Expression.emptyExpressionSet);
      }
      if (rv.nonIndexJoinCondition != null) {
        Expression.collectAllExpressions(
            col_list,
            rv.nonIndexJoinCondition,
            Expression.columnExpressionSet,
            Expression.emptyExpressionSet);
      }
    }
    HsqlList uniq_col_list = new HsqlArrayList();
    for (int i = 0; i < col_list.size(); i++) {
      Expression orig = (Expression) col_list.get(i);
      if (!uniq_col_list.contains(orig)) {
        uniq_col_list.add(orig);
      }
    }
    for (int i = 0; i < uniq_col_list.size(); i++) {
      VoltXMLElement xml = ((Expression) uniq_col_list.get(i)).voltGetXML(session);
      scanCols.children.add(xml);
      assert (xml != null);
    }

    // columns
    VoltXMLElement cols = new VoltXMLElement("columns");
    query.children.add(cols);

    ArrayList<Expression> orderByCols = new ArrayList<Expression>();
    ArrayList<Expression> groupByCols = new ArrayList<Expression>();
    ArrayList<Expression> displayCols = new ArrayList<Expression>();
    ArrayList<Pair<Integer, SimpleName>> aliases = new ArrayList<Pair<Integer, SimpleName>>();

    /*
     * select.exprColumn stores all of the columns needed by HSQL to
     * calculate the query's result set. It contains more than just the
     * columns in the output; for example, it contains columns representing
     * aliases, columns for groups, etc.
     *
     * Volt uses multiple collections to organize these columns.
     *
     * Observing this loop in a debugger, the following seems true:
     *
     * 1. Columns in exprColumns that appear in the output schema, appear in
     * exprColumns in the same order that they occur in the output schema.
     *
     * 2. expr.columnIndex is an index back in to the select.exprColumns
     * array. This allows multiple exprColumn entries to refer to each
     * other; for example, an OpType.SIMPLE_COLUMN type storing an alias
     * will have its columnIndex set to the offset of the expr it aliases.
     */
    for (int i = 0; i < select.exprColumns.length; i++) {
      final Expression expr = select.exprColumns[i];

      if (expr.alias != null) {
        /*
         * Remember how aliases relate to columns. Will iterate again later
         * and mutate the exprColumn entries setting the alias string on the aliased
         * column entry.
         */
        if (expr instanceof ExpressionColumn) {
          ExpressionColumn exprColumn = (ExpressionColumn) expr;
          if (exprColumn.alias != null && exprColumn.columnName == null) {
            aliases.add(Pair.of(expr.columnIndex, expr.alias));
          }
        } else if (expr.columnIndex > -1) {
          /*
           * Only add it to the list of aliases that need to be
           * propagated to columns if the column index is valid.
           * ExpressionArithmetic will have an alias but not
           * necessarily a column index.
           */
          aliases.add(Pair.of(expr.columnIndex, expr.alias));
        }
      }

      // If the column doesn't refer to another exprColumn entry, set its
      // column index to itself. If all columns have a valid column index,
      // it's easier to patch up display column ordering later.
      if (expr.columnIndex == -1) {
        expr.columnIndex = i;
      }

      if (isGroupByColumn(select, i)) {
        groupByCols.add(expr);
      } else if (expr.opType == OpTypes.ORDER_BY) {
        orderByCols.add(expr);
      } else if (expr.opType != OpTypes.SIMPLE_COLUMN || (expr.isAggregate && expr.alias != null)) {
        // Add aggregate aliases to the display columns to maintain
        // the output schema column ordering.
        displayCols.add(expr);
      }
      // else, other simple columns are ignored. If others exist, maybe
      // volt infers a display column from another column collection?
    }

    for (Pair<Integer, SimpleName> alias : aliases) {
      // set the alias data into the expression being aliased.
      select.exprColumns[alias.getFirst()].alias = alias.getSecond();
    }

    /*
     * The columns chosen above as display columns aren't always the same
     * expr objects HSQL would use as display columns - some data were
     * unified (namely, SIMPLE_COLUMN aliases were pushed into COLUMNS).
     *
     * However, the correct output schema ordering was correct in exprColumns.
     * This order was maintained by adding SIMPLE_COLUMNs to displayCols.
     *
     * Now need to serialize the displayCols, serializing the non-simple-columns
     * corresponding to simple_columns for any simple_columns that woodchucks
     * could chuck.
     *
     * Serialize the display columns in the exprColumn order.
     */
    for (int jj = 0; jj < displayCols.size(); ++jj) {
      Expression expr = displayCols.get(jj);
      if (expr == null) {
        continue;
      } else if (expr.opType == OpTypes.SIMPLE_COLUMN) {
        // simple columns are not serialized as display columns
        // but they are place holders for another column
        // in the output schema. Go find that corresponding column
        // and serialize it in this place.
        for (int ii = jj; ii < displayCols.size(); ++ii) {
          Expression otherCol = displayCols.get(ii);
          if (otherCol == null) {
            continue;
          } else if ((otherCol.opType != OpTypes.SIMPLE_COLUMN)
              && (otherCol.columnIndex == expr.columnIndex)) {
            // serialize the column this simple column stands-in for
            VoltXMLElement xml = otherCol.voltGetXML(session);
            cols.children.add(xml);
            assert (xml != null);
            // null-out otherCol to not serialize it twice
            displayCols.set(ii, null);
            // quit seeking simple_column's replacement
            break;
          }
        }
      } else {
        VoltXMLElement xml = expr.voltGetXML(session);
        cols.children.add(xml);
        assert (xml != null);
      }
    }

    // parameters
    voltAppendParameters(session, query);

    // scans
    VoltXMLElement scans = new VoltXMLElement("tablescans");
    query.children.add(scans);
    assert (scans != null);

    for (RangeVariable rangeVariable : select.rangeVariables) {
      scans.children.add(rangeVariable.voltGetRangeVariableXML(session));
    }

    // Columns from USING expression in join are not qualified.
    // if join is INNER then the column from USING expression can be from any table
    // participating in join. In case of OUTER join, it must be the outer column
    resolveUsingColumns(cols, select.rangeVariables);

    // having
    if (select.havingCondition != null) {
      throw new HSQLParseException("VoltDB does not support the HAVING clause");
    }

    // groupby
    if (select.isGrouped) {
      VoltXMLElement groupCols = new VoltXMLElement("groupcolumns");
      query.children.add(groupCols);
      for (Expression groupByCol : groupByCols) {
        groupCols.children.add(groupByCol.voltGetXML(session));
      }
    }
    // orderby
    if (orderByCols.size() > 0) {
      VoltXMLElement orderCols = new VoltXMLElement("ordercolumns");
      query.children.add(orderCols);
      for (Expression orderByCol : orderByCols) {
        orderCols.children.add(orderByCol.voltGetXML(session));
      }
    }

    // query condition should be covered by table scans, but can un-comment
    // this to see if anything is missed
    /*if (select.queryCondition != null) {
        VoltXMLElement queryCond = new VoltXMLElement("querycondition");
        query.children.add(queryCond);
        queryCond.children.add(select.queryCondition.voltGetXML(session));
    }*/

    return query;
  }