void parseDisplayColumns(VoltXMLElement columnsNode) { for (VoltXMLElement child : columnsNode.children) { ParsedColInfo col = new ParsedColInfo(); col.expression = parseExpressionTree(child); if (col.expression instanceof ConstantValueExpression) { assert (col.expression.getValueType() != VoltType.NUMERIC); } ExpressionUtil.finalizeValueTypes(col.expression); assert (col.expression != null); col.alias = child.attributes.get("alias"); if (child.name.equals("columnref")) { col.columnName = child.attributes.get("column"); col.tableName = child.attributes.get("table"); } else { // XXX hacky, assume all non-column refs come from a temp table col.tableName = "VOLT_TEMP_TABLE"; col.columnName = ""; } // This index calculation is only used for sanity checking // materialized views (which use the parsed select statement but // don't go through the planner pass that does more involved // column index resolution). col.index = displayColumns.size(); displayColumns.add(col); } }
void parseGroupByColumn(VoltXMLElement groupByNode) { ParsedColInfo col = new ParsedColInfo(); col.expression = parseExpressionTree(groupByNode); assert (col.expression != null); if (groupByNode.name.equals("columnref")) { col.alias = groupByNode.attributes.get("alias"); col.columnName = groupByNode.attributes.get("column"); col.tableName = groupByNode.attributes.get("table"); col.groupBy = true; } else { throw new RuntimeException("GROUP BY with complex expressions not yet supported"); } assert (col.alias.equalsIgnoreCase(col.columnName)); assert (getTableFromDB(col.tableName) != null); assert (getTableFromDB(col.tableName).getColumns().getIgnoreCase(col.columnName) != null); org.voltdb.catalog.Column catalogColumn = getTableFromDB(col.tableName).getColumns().getIgnoreCase(col.columnName); col.index = catalogColumn.getIndex(); groupByColumns.add(col); }
void parseOrderColumn(VoltXMLElement orderByNode) { // make sure everything is kosher assert (orderByNode.name.equalsIgnoreCase("operation")); String operationType = orderByNode.attributes.get("type"); assert (operationType != null); assert (operationType.equalsIgnoreCase("orderby")); // get desc/asc String desc = orderByNode.attributes.get("desc"); boolean descending = (desc != null) && (desc.equalsIgnoreCase("true")); // get the columnref expression inside the orderby node VoltXMLElement child = orderByNode.children.get(0); assert (child != null); // create the orderby column ParsedColInfo order_col = new ParsedColInfo(); order_col.orderBy = true; order_col.ascending = !descending; AbstractExpression order_exp = parseExpressionTree(child); // Cases: // inner child could be columnref, in which case it's just a normal // column. Just make a ParsedColInfo object for it and the planner // will do the right thing later if (child.name.equals("columnref")) { order_col.columnName = child.attributes.get("column"); order_col.tableName = child.attributes.get("table"); String alias = child.attributes.get("alias"); order_col.alias = alias; // The ORDER BY column MAY be identical to a simple display column, in which case, // tagging the actual display column as being also an order by column // helps later when trying to determine ORDER BY coverage (for determinism). if (order_exp instanceof TupleValueExpression) { ParsedColInfo orig_col = null; for (ParsedColInfo col : displayColumns) { if (col.expression.equals(order_exp)) { orig_col = col; break; } } if (orig_col != null) { orig_col.orderBy = true; orig_col.ascending = order_col.ascending; } } } else if (child.name.equals("operation")) { order_col.columnName = ""; // I'm not sure anyone actually cares about this table name order_col.tableName = "VOLT_TEMP_TABLE"; // If it's a simplecolumn operation. This means that the alias that // we have should refer to a column that we compute // somewhere else (like an aggregate, mostly). // Look up that column in the displayColumns list, // and then create a new order by column with a magic TVE. // This case seems to be the result of cross-referencing a display column // by its position, as in "ORDER BY 2, 3". Otherwise the ORDER BY column // is a columnref as handled in the prior code block. if (order_exp instanceof TupleValueExpression) { String alias = child.attributes.get("alias"); order_col.alias = alias; ParsedColInfo orig_col = null; for (ParsedColInfo col : displayColumns) { if (col.alias.equals(alias)) { orig_col = col; break; } } // We need the original column expression so we can extract // the value size and type for our TVE that refers back to it. // XXX: This check runs into problems for some cases where a display column expression gets // re-used in the ORDER BY. // I THINK one problem case was "select x, max(y) from t group by x order by max(y);" --paul if (orig_col == null) { throw new PlanningErrorException( "Unable to find source " + "column for simplecolumn: " + alias); } // Tagging the actual display column as being also an order by column // helps later when trying to determine ORDER BY coverage (for determinism). orig_col.orderBy = true; orig_col.ascending = order_col.ascending; assert (orig_col.tableName.equals("VOLT_TEMP_TABLE")); // Construct our fake TVE that will point back at the input // column. TupleValueExpression tve = (TupleValueExpression) order_exp; tve.setColumnAlias(alias); tve.setColumnName(""); tve.setColumnIndex(-1); tve.setTableName("VOLT_TEMP_TABLE"); tve.setValueSize(orig_col.expression.getValueSize()); tve.setValueType(orig_col.expression.getValueType()); if (orig_col.expression.hasAnySubexpressionOfClass(AggregateExpression.class)) { tve.setHasAggregate(true); } } } else if (child.name.equals("function") == false) { throw new RuntimeException("ORDER BY parsed with strange child node type: " + child.name); } if (order_exp instanceof ConstantValueExpression) { assert (order_exp.getValueType() != VoltType.NUMERIC); } ExpressionUtil.finalizeValueTypes(order_exp); order_col.expression = order_exp; orderColumns.add(order_col); }