/* This file is part of VoltDB. * Copyright (C) 2008-2010 VoltDB L.L.C. * * VoltDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VoltDB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.planner; import java.util.ArrayList; import java.util.HashMap; import java.util.Map.Entry; import org.voltdb.catalog.Database; import org.voltdb.expressions.AbstractExpression; import org.voltdb.expressions.ExpressionUtil; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /** * * */ public class ParsedSelectStmt extends AbstractParsedStmt { public static class ParsedColInfo { public String alias; public String columnName; public String tableName; public AbstractExpression expression; public boolean finalOutput = true; public int index = 0; public int size; // orderby stuff public boolean orderBy = false; public boolean ascending = true; // groupby public boolean groupBy = false; } public HashMap<String, ParsedColInfo> allColumns = new HashMap<String, ParsedColInfo>(); public ArrayList<ParsedColInfo> displayColumns = new ArrayList<ParsedColInfo>(); public ArrayList<ParsedColInfo> orderColumns = new ArrayList<ParsedColInfo>(); public AbstractExpression having = null; public ArrayList<ParsedColInfo> groupByColumns = new ArrayList<ParsedColInfo>(); public long limit = -1; public long offset = 0; public long limitParameterId = -1; public long offsetParameterId = -1; public boolean grouped = false; public boolean distinct = false; @Override void parse(Node stmtNode, Database db) { NamedNodeMap attrs = stmtNode.getAttributes(); Node node; if ((node = attrs.getNamedItem("limit")) != null) limit = Long.parseLong(node.getNodeValue()); if ((node = attrs.getNamedItem("offset")) != null) offset = Long.parseLong(node.getNodeValue()); if ((node = attrs.getNamedItem("limit_paramid")) != null) limitParameterId = Long.parseLong(node.getNodeValue()); if ((node = attrs.getNamedItem("offset_paramid")) != null) offsetParameterId = Long.parseLong(node.getNodeValue()); if ((node = attrs.getNamedItem("grouped")) != null) grouped = Boolean.parseBoolean(node.getNodeValue()); if ((node = attrs.getNamedItem("distinct")) != null) distinct = Boolean.parseBoolean(node.getNodeValue()); // limit and offset can't have both value and parameter if (limit != -1) assert limitParameterId == -1 : "Parsed value and param. limit."; if (offset != 0) assert offsetParameterId == -1 : "Parsed value and param. offset."; for (Node child = stmtNode.getFirstChild(); child != null; child = child.getNextSibling()) { if (child.getNodeType() != Node.ELEMENT_NODE) continue; if (child.getNodeName().equalsIgnoreCase("columns")) parseDisplayColumns(child, db); else if (child.getNodeName().equalsIgnoreCase("querycondition")) parseQueryCondition(child, db); else if (child.getNodeName().equalsIgnoreCase("ordercolumns")) parseOrderColumns(child, db); else if (child.getNodeName().equalsIgnoreCase("groupcolumns")) { parseGroupByColumns(child, db); } } } void parseDisplayColumns(Node columnsNode, Database db) { for (Node child = columnsNode.getFirstChild(); child != null; child = child.getNextSibling()) { if (child.getNodeType() != Node.ELEMENT_NODE) continue; final String nodeName = child.getNodeName(); ParsedColInfo col = new ParsedColInfo(); col.expression = parseExpressionTree(child, db); ExpressionUtil.assignLiteralConstantTypesRecursively(col.expression); ExpressionUtil.assignOutputValueTypesRecursively(col.expression); assert(col.expression != null); col.alias = child.getAttributes().getNamedItem("alias").getNodeValue(); if (nodeName.equals("columnref")) { col.columnName = child.getAttributes().getNamedItem("column").getNodeValue(); col.tableName = child.getAttributes().getNamedItem("table").getNodeValue(); } col.index = allColumns.size(); displayColumns.add(col); allColumns.put(col.alias, col); } } void parseQueryCondition(Node conditionNode, Database db) { Node exprNode = conditionNode.getFirstChild(); while ((exprNode != null) && (exprNode.getNodeType() != Node.ELEMENT_NODE)) exprNode = exprNode.getNextSibling(); if (exprNode == null) return; where = parseExpressionTree(exprNode, db); ExpressionUtil.assignLiteralConstantTypesRecursively(where); ExpressionUtil.assignOutputValueTypesRecursively(where); } void parseGroupByColumns(Node columnsNode, Database db) { for (Node child = columnsNode.getFirstChild(); child != null; child = child.getNextSibling()) { if (child.getNodeType() != Node.ELEMENT_NODE) continue; parseGroupByColumn(child, db); } } void parseGroupByColumn(Node groupByNode, Database db) { // make sure everything is kosher assert(groupByNode.getNodeType() == Node.ELEMENT_NODE); ParsedColInfo col = new ParsedColInfo(); col.expression = parseExpressionTree(groupByNode, db); assert(col.expression != null); if (groupByNode.getNodeName().equals("columnref")) { NamedNodeMap attrs = groupByNode.getAttributes(); col.alias = attrs.getNamedItem("alias").getNodeValue(); col.columnName = attrs.getNamedItem("column").getNodeValue(); col.tableName = attrs.getNamedItem("table").getNodeValue(); col.groupBy = true; } else { throw new RuntimeException("GROUP BY with complex expressions not yet supported"); } assert(col.alias.equalsIgnoreCase(col.columnName)); assert(db.getTables().getIgnoreCase(col.tableName) != null); assert(db.getTables().getIgnoreCase(col.tableName).getColumns().getIgnoreCase(col.columnName) != null); org.voltdb.catalog.Column catalogColumn = db.getTables().getIgnoreCase(col.tableName).getColumns().getIgnoreCase(col.columnName); col.index = catalogColumn.getIndex(); groupByColumns.add(col); } void parseOrderColumns(Node columnsNode, Database db) { for (Node child = columnsNode.getFirstChild(); child != null; child = child.getNextSibling()) { if (child.getNodeType() != Node.ELEMENT_NODE) continue; parseOrderColumn(child, db); } } void parseOrderColumn(Node orderByNode, Database db) { // make sure everything is kosher assert(orderByNode.getNodeType() == Node.ELEMENT_NODE); assert(orderByNode.getNodeName().equalsIgnoreCase("operation")); NamedNodeMap attrs = orderByNode.getAttributes(); Node operationTypeNode = attrs.getNamedItem("type"); assert(operationTypeNode != null); assert(operationTypeNode.getNodeValue().equalsIgnoreCase("orderby")); // get desc/asc Node descNode = attrs.getNamedItem("desc"); boolean descending = descNode.getNodeValue().equalsIgnoreCase("true"); // get the columnref expression inside the orderby node Node child = orderByNode.getFirstChild(); while (child.getNodeType() != Node.ELEMENT_NODE) child = child.getNextSibling(); assert(child != null); if (child.getNodeName().equals("columnref")) { NamedNodeMap childAttrs = child.getAttributes(); String alias = childAttrs.getNamedItem("alias").getNodeValue(); // create the orderby column ParsedColInfo col = allColumns.get(alias); if (col == null) { col = new ParsedColInfo(); col.alias = alias; col.expression = parseExpressionTree(child, db); ExpressionUtil.assignLiteralConstantTypesRecursively(col.expression); ExpressionUtil.assignOutputValueTypesRecursively(col.expression); col.index = allColumns.size(); col.columnName = childAttrs.getNamedItem("column").getNodeValue(); col.tableName = childAttrs.getNamedItem("table").getNodeValue(); allColumns.put(alias, col); } col.orderBy = true; col.ascending = !descending; orderColumns.add(col); } else { throw new RuntimeException("ORDER BY with complex expressions not yet supported"); } } @Override public String toString() { String retval = super.toString() + "\n"; retval += "LIMIT " + String.valueOf(limit) + "\n"; retval += "OFFSET " + String.valueOf(limit) + "\n"; retval += "ALL COLUMNS:\n"; for (Entry<String, ParsedColInfo> col : allColumns.entrySet()) { retval += "\tColumn: " + col.getKey() + ": "; retval += col.getValue().expression.toString() + "\n"; } retval += "DISPLAY COLUMNS:\n"; for (ParsedColInfo col : displayColumns) { retval += "\tColumn: " + col.alias + ": "; retval += col.expression.toString() + "\n"; } retval += "ORDER COLUMNS:\n"; for (ParsedColInfo col : orderColumns) { retval += "\tColumn: " + col.alias + ": " + col.ascending; retval += col.expression.toString() + "\n"; } retval += "GROUP_BY COLUMNS:\n"; for (ParsedColInfo col : groupByColumns) { retval += "\tColumn: " + col.alias + ": "; retval += col.expression.toString() + "\n"; } retval = retval.trim(); return retval; } }