/* * 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 ResultColumnList is the target list of a SELECT, INSERT, or UPDATE. * * @see ResultColumn */ public class ResultColumnList extends QueryTreeNodeList<ResultColumn> { int orderBySelect = 0; // the number of result columns pulled up // from ORDERBY list /* * A comment on 'orderBySelect'. When we encounter a SELECT .. ORDER BY * statement, the columns (or expressions) in the ORDER BY clause may * or may not have been explicitly mentioned in the SELECT column list. * If the columns were NOT explicitly mentioned in the SELECT column * list, then the parsing of the ORDER BY clause implicitly generates * them into the result column list, because we'll need to have those * columns present at execution time in order to sort by them. Those * generated columns are added to the *end* of the ResultColumnList, and * we keep track of the *number* of those columns in 'orderBySelect', * so we can tell whether we are looking at a generated column by seeing * whether its position in the ResultColumnList is in the last * 'orderBySelect' number of columns. If the SELECT .. ORDER BY * statement uses the "*" token to select all the columns from a table, * then during ORDER BY parsing we redundantly generate the columns * mentioned in the ORDER BY clause into the ResultColumnlist, but then * later in getOrderByColumnToBind we determine that these are * duplicates and we take them back out again. */ /* ** Is this ResultColumnList for a FromBaseTable for an index ** that is to be updated? */ protected boolean forUpdate; // Number of RCs in this RCL at "init" time, before additional // ones were added internally. private int initialListSize = 0; public ResultColumnList() { } /** * Add a ResultColumn (at this point, ResultColumn or * AllResultColumn) to the list * * @param resultColumn The ResultColumn to add to the list */ public void addResultColumn(ResultColumn resultColumn) { /* Lists are 0-based, ResultColumns are 1-based */ resultColumn.setVirtualColumnId(size() + 1); add(resultColumn); } /** * Append a given ResultColumnList to this one, resetting the virtual * column ids in the appended portion. * * @param resultColumns The ResultColumnList to be appended * @param destructiveCopy Whether or not this is a destructive copy * from resultColumns */ public void appendResultColumns(ResultColumnList resultColumns, boolean destructiveCopy) { int oldSize = size(); int newID = oldSize + 1; /* ** Set the virtual column ids in the list being appended. ** Lists are zero-based, and virtual column ids are one-based, ** so the new virtual column ids start at the original size ** of this list, plus one. */ int otherSize = resultColumns.size(); for (int index = 0; index < otherSize; index++) { /* ResultColumns are 1-based */ resultColumns.get(index).setVirtualColumnId(newID); newID++; } if (destructiveCopy) { destructiveAddAll(resultColumns); } else { addAll(resultColumns); } } /** * Get a ResultColumn from a column position (1-based) in the list * * @param position The ResultColumn to get from the list (1-based) * * @return the column at that position. */ public ResultColumn getResultColumn(int position) { /* ** First see if it falls in position x. If not, ** search the whole shebang */ if (position <= size()) { // this wraps the cast needed, // and the 0-based nature of the Lists. ResultColumn rc = (ResultColumn)get(position-1); if (rc.getColumnPosition() == position) { return rc; } } /* ** Check each column */ int size = size(); for (int index = 0; index < size; index++) { ResultColumn rc = get(index); if (rc.getColumnPosition() == position) { return rc; } } return null; } /** * Get a ResultColumn from a column position (1-based) in the list, * null if out of range (for order by). * * @param position The ResultColumn to get from the list (1-based) * * @return the column at that position, null if out of range */ public ResultColumn getOrderByColumn(int position) { // this wraps the cast needed, and the 0-based nature of the Lists. if (position == 0) return null; return getResultColumn(position); } /** * Get a ResultColumn that matches the specified columnName. If requested * to, mark the column as referenced. * * @param columnName The ResultColumn to get from the list * @param markIfReferenced True if we should mark this column as referenced. * * @return the column that matches that name. */ public ResultColumn getResultColumn(String columnName) { int size = size(); for (int index = 0; index < size; index++) { ResultColumn resultColumn = get(index); if (columnName.equalsIgnoreCase(resultColumn.getName())) { return resultColumn; } } return null; } /** * Get an array of strings for all the columns * in this RCL. * * @return the array of strings */ public String[] getColumnNames() { String strings[] = new String[size()]; int size = size(); for (int index = 0; index < size; index++) { ResultColumn resultColumn = get(index); strings[index] = resultColumn.getName(); } return strings; } /** * Remove the columns which are join columns (in the * joinColumns RCL) from this list. This is useful * for a JOIN with a USING clause. * * @param joinColumns The list of join columns */ public void removeJoinColumns(ResultColumnList joinColumns) { for (ResultColumn joinRC : joinColumns) { String columnName = joinRC.getName(); ResultColumn rightRC = getResultColumn(columnName); // Remove the RC from this list. if (rightRC != null) { remove(rightRC); } } } /** * Get the join columns from this list. * This is useful for a join with a USING clause. * (ANSI specifies that the join columns appear 1st.) * * @param joinColumns A list of the join columns. * * @return A list of the join columns from this list */ public ResultColumnList getJoinColumns(ResultColumnList joinColumns) throws StandardException { ResultColumnList newRCL = new ResultColumnList(); /* Find all of the join columns and put them on the new RCL. */ for (ResultColumn joinRC : joinColumns) { String columnName = joinRC.getName(); ResultColumn xferRC = getResultColumn(columnName); if (xferRC == null) { throw new StandardException("Column not found: " + columnName); } // Add the RC to the new list. newRCL.add(xferRC); } return newRCL; } /* **** * Take note of the size of this RCL _before_ we start * processing/binding it. This is so that, at bind time, * we can tell if any columns in the RCL were added * internally by us (i.e. they were not specified by the * user and thus will not be returned to the user). */ protected void markInitialSize() { initialListSize = size(); } /** * 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 super.toString(); } }