/* * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software;Designed and Developed mainly by many Chinese * opensource volunteers. you can redistribute it and/or modify it under the * terms of the GNU General Public License version 2 only, as published by the * Free Software Foundation. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Any questions about this component can be directed to it's project Web address * https://code.google.com/p/opencloudb/. * */ package com.akiban.sql.parser; import com.akiban.sql.StandardException; /** * A SubqueryNode represents a subquery. Subqueries return values to their * outer queries. An quantified subquery is one that appears under a quantified * operator (like IN or EXISTS) - quantified subqueries can return more than * one value per invocation. An expression subquery is one that is not directly * under a quantified operator - expression subqueries are allowed to return * at most one value per invocation (returning no value is considered to be * equivalent to returning NULL). * * There are a large number of subquery types. Because of the large number of * types, and the large amount of shared code, we have decided to have 1 SubqueryNode * without any subclasses. The subquery type (and operator) is encoded in the * subqueryType field. * * The query optimizer is responsible for optimizing subqueries, and also for * transforming them so that code can be generated for them. The optimizer may * eliminate some subqueries by transforming them into joins, or it may * change the internal form of a subquery (for example, transforming * 'where x in (select y from z where ...)' into * 'where (select true from z where x = y and ...)'). * * Note that aggregates present some additional issues. A transformation * such as: * <UL> where x in (SELECT <I>expression</I> FROM z) </UL> * has to be treated specially if <I>expression</I> has an aggregate. * We change it to: * <UL> where x = (SELECT true FROM (SELECT MAX(x) FROM z) WHERE SQLCOL1 = y) </UL> * */ public class SubqueryNode extends ValueNode { /* ** This must be a single-column result set. If the subquery is ** not quantified, it must also be a single-row result set - that is, ** expression subqueries are allowed to return only a single value ** per invocation. ** NOTE: SubqueryNodes are used as an intermediate step within the parser ** for building a derived table. Derived tables can be multi-column and ** multi-table. */ private ResultSetNode resultSet; /* Type of this subquery */ private SubqueryType subqueryType; /* Since we do not have separate subquery operator nodes, the * type of the subquery is stored in the subqueryType field. Most subquery * types take a left operand (except for expression and exists). We could * either add a leftOperand field here or subclass SubqueryNode for those * types that take a left operand. We have decided to add the left operand * here for now. */ private ValueNode leftOperand; private OrderByList orderByList; private ValueNode offset; private ValueNode fetchFirst; /* Subquery types. * NOTE: FROM only exists for a brief second in the parser. It * should never appear in a query tree. * NOTE: NOT EXISTS and NOT IN subquery types do not exist prior to NOT * elimination during preprocessing. Prior to that, there is a separate * NotNode above the SubqueryNode in the tree. * */ public static enum SubqueryType { FROM, IN, NOT_IN, EQ_ANY, EQ_ALL, NE_ANY, NE_ALL, GT_ANY, GT_ALL, GE_ANY, GE_ALL, LT_ANY, LT_ALL, LE_ANY, LE_ALL, EXISTS, NOT_EXISTS, EXPRESSION } /** * Initializer. * * @param resultSet The ResultSetNode for the subquery * @param subqueryType The type of the subquery * @param leftOperand The left operand, if any, of the subquery * @param orderCols ORDER BY list * @param offset OFFSET n ROWS * @param fetchFirst FETCH FIRST n ROWS ONLY */ public void init(Object resultSet, Object subqueryType, Object leftOperand, Object orderCols, Object offset, Object fetchFirst) { this.resultSet = (ResultSetNode)resultSet; this.subqueryType = (SubqueryType)subqueryType; this.orderByList = (OrderByList)orderCols; this.offset = (ValueNode)offset; this.fetchFirst = (ValueNode)fetchFirst; this.leftOperand = (ValueNode)leftOperand; } /** * Fill this node with a deep copy of the given node. */ public void copyFrom(QueryTreeNode node) throws StandardException { super.copyFrom(node); SubqueryNode other = (SubqueryNode)node; this.resultSet = (ResultSetNode)getNodeFactory().copyNode(other.resultSet, getParserContext()); this.subqueryType = other.subqueryType; this.leftOperand = (ValueNode)getNodeFactory().copyNode(other.leftOperand, getParserContext()); this.orderByList = (OrderByList)getNodeFactory().copyNode(other.orderByList, getParserContext()); this.offset = (ValueNode)getNodeFactory().copyNode(other.offset, getParserContext()); this.fetchFirst = (ValueNode)getNodeFactory().copyNode(other.fetchFirst, getParserContext()); } /** * Convert this object to a String. See comments in QueryTreeNode.java * for how this should be done for tree printing. * * @return This object as a String */ public String toString() { return "subqueryType: " + subqueryType + "\n" + super.toString(); } /** * Prints the sub-nodes of this object. See QueryTreeNode.java for * how tree printing is supposed to work. * * @param depth The depth of this node in the tree */ public void printSubNodes(int depth) { super.printSubNodes(depth); if (resultSet != null) { printLabel(depth, "resultSet: "); resultSet.treePrint(depth + 1); } if (leftOperand != null) { printLabel(depth, "leftOperand: "); leftOperand.treePrint(depth + 1); } if (orderByList != null) { printLabel(depth, "orderByList: "); orderByList.treePrint(depth + 1); } if (offset != null) { printLabel(depth, "offset: "); offset.treePrint(depth + 1); } if (fetchFirst != null) { printLabel(depth, "fetchFirst: "); fetchFirst.treePrint(depth + 1); } } /** * Accept the visitor for all visitable children of this node. * * @param v the visitor * * @exception StandardException on error */ void acceptChildren(Visitor v) throws StandardException { super.acceptChildren(v); if (resultSet != null) { resultSet = (ResultSetNode)resultSet.accept(v); } if (leftOperand != null) { leftOperand = (ValueNode)leftOperand.accept(v); } if (orderByList != null) { orderByList = (OrderByList)orderByList.accept(v); } if (offset != null) { offset = (ValueNode)offset.accept(v); } if (fetchFirst != null) { fetchFirst = (ValueNode)fetchFirst.accept(v); } } /** * Return the resultSet for this SubqueryNode. * * @return ResultSetNode underlying this SubqueryNode. */ public ResultSetNode getResultSet() { return resultSet; } public void setResultSet(ResultSetNode resultSet) { this.resultSet = resultSet; } /** * Return the type of this subquery. * * @return int Type of this subquery. */ public SubqueryType getSubqueryType() { return subqueryType; } /** * Set the type of this subquery. * * @param subqueryType of this subquery. */ public void setSubqueryType(SubqueryType subqueryType) { this.subqueryType = subqueryType; } /** * {@inheritDoc} */ protected boolean isEquivalent(ValueNode o) { return false; } /** * Get ORDER BY list (used to construct FROM only), cf. * FromSubquery, for which this node is transient. * * @return order by list if specified, else null. */ public OrderByList getOrderByList() { return orderByList; } /** * Get OFFSET (used to construct FROM only), cf. * FromSubquery, for which this node is transient. * * @return offset if specified, else null. */ public ValueNode getOffset() { return offset; } /** * Get FETCH FIRST (used to construct FROM only), cf. * FromSubquery, for which this node is transient. * * @return fetch first if specified, else null. */ public ValueNode getFetchFirst() { return fetchFirst; } public ValueNode getLeftOperand() { return leftOperand; } }