Package org.hsqldb

Source Code of org.hsqldb.StatementQuery$Pair

/* Copyright (c) 2001-2009, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


package org.hsqldb;

import java.util.ArrayList;
import org.hsqldb.HSQLInterface.HSQLParseException;
import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.HsqlNameManager.SimpleName;
import org.hsqldb.ParserDQL.CompileContext;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.result.Result;
import org.hsqldb.result.ResultMetaData;

/**
* Implementation of Statement for query expressions.<p>
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public class StatementQuery extends StatementDMQL {

    StatementQuery(Session session, QueryExpression queryExpression,
                   CompileContext compileContext) {

        super(StatementTypes.SELECT_CURSOR, StatementTypes.X_SQL_DATA,
              session.currentSchema);

        this.queryExpression = queryExpression;

        setDatabseObjects(compileContext);
        checkAccessRights(session);
    }

    StatementQuery(Session session, QueryExpression queryExpression,
                   CompileContext compileContext, HsqlName[] targets) {

        super(StatementTypes.SELECT_SINGLE, StatementTypes.X_SQL_DATA,
              session.currentSchema);

        this.queryExpression = queryExpression;

        setDatabseObjects(compileContext);
        checkAccessRights(session);
    }

    @Override
    Result getResult(Session session) {

        Result result = queryExpression.getResult(session,
            session.getMaxRows());

        result.setStatement(this);

        return result;
    }

    @Override
    public ResultMetaData getResultMetaData() {

        switch (type) {

            case StatementTypes.SELECT_CURSOR :
                return queryExpression.getMetaData();

            case StatementTypes.SELECT_SINGLE :
                return queryExpression.getMetaData();

            default :
                throw Error.runtimeError(
                    ErrorCode.U_S0500,
                    "CompiledStatement.getResultMetaData()");
        }
    }

    @Override
    void getTableNamesForRead(OrderedHashSet set) {

        queryExpression.getBaseTableNames(set);

        for (SubQuery subquerie : subqueries) {
            if (subquerie.queryExpression != null) {
                subquerie.queryExpression.getBaseTableNames(set);
            }
        }
    }

    @Override
    void getTableNamesForWrite(OrderedHashSet set) {}

    private static class Pair<T, U> {
        protected final T m_first;
        protected final U m_second;

        public Pair(T first, U second) {
            m_first = first;
            m_second = second;
        }

        /**
         * @return the first
         */
        public T getFirst() {
            return m_first;
        }

        /**
         * @return the second
         */
        public U getSecond() {
            return m_second;
        }

        /**
         * Convenience class method for constructing pairs using Java's generic type
         * inference.
         */
        public static <T extends Comparable<T>, U> Pair<T, U> of(T x, U y) {
            return new Pair<T, U>(x, y);
        }
    }
    /**
     * Returns true if the specified exprColumn index is in the list of column indices specified by groupIndex
     * @return true/false
     */
    boolean isGroupByColumn(QuerySpecification select, int index) {
        if (!select.isGrouped) {
            return false;
        }
        for (int ii = 0; ii < select.groupIndex.getColumnCount(); ii++) {
            if (index == select.groupIndex.getColumns()[ii]) {
                return true;
            }
        }
        return false;
    }

    /*************** VOLTDB *********************/

    /**
     * VoltDB added method to get a non-catalog-dependent
     * representation of this HSQLDB object.
     * @param session The current Session object may be needed to resolve
     * some names.
     * @param indent A string of whitespace to be prepended to every line
     * in the resulting XML.
     * @param params The parameters (if any) to this compiled SELECT
     * statement. These are not available to this object, so they are
     * hackily passed to this method here.
     * @return XML, correctly indented, representing this object.
     * @throws HSQLParseException
     */
    @Override
    String voltGetXML(Session session, String orig_indent)
    throws HSQLParseException
    {
        QuerySpecification select = (QuerySpecification) queryExpression;

        try {
            getResult(session);
        }
        catch (HsqlException e)
        {
            throw new HSQLParseException(e.getMessage());
        }
        catch (Exception e)
        {
            // XXX coward.
        }

        StringBuffer sb = new StringBuffer();
        String indent = orig_indent + HSQLInterface.XML_INDENT;

        // select
        sb.append(orig_indent).append("<select");
        if (select.isDistinctSelect)
            sb.append(" distinct=\"true\"");
        if (select.isGrouped)
            sb.append(" grouped=\"true\"");
        if (select.isAggregated)
            sb.append(" aggregated=\"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) {
                        sb.append(" offset=\"" + offset + " \"");
                    }
                }
                else {
                    sb.append(" offset_paramid=\"" + limitCondition.nodes[0].getUniqueId() + "\"");
                }

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

        sb.append(">\n");

        // columns
        sb.append(indent + "<columns>\n");

        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 if (expr.opType == OpTypes.SIMPLE_COLUMN) {
                // Other simple columns are ignored. If others exist, maybe
                // volt infers a display column from another column collection?
            } else {
                displayCols.add(expr);
            }
        }

        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
                        sb.append(otherCol.voltGetXML(session, indent + HSQLInterface.XML_INDENT)).append("\n");
                        // null-out otherCol to not serialize it twice
                        displayCols.set(ii, null);
                        // quit seeking simple_column's replacement
                        break;
                    }
                }
            }
            else {
                sb.append(expr.voltGetXML(session, indent + HSQLInterface.XML_INDENT)).append("\n");
            }
        }

        sb.append(indent + "</columns>\n");

        // parameters
        sb.append(indent + "<parameters>\n");
        for (int i = 0; i < parameters.length; i++) {
            sb.append(indent + HSQLInterface.XML_INDENT + "<parameter index='").append(i).append("'");
            ExpressionColumn param = parameters[i];
            sb.append(" id='").append(param.getUniqueId()).append("'");
            sb.append(" type='").append(Types.getTypeName(param.getDataType().typeCode)).append("'");
            sb.append(" />\n");
        }
        sb.append(indent + "</parameters>\n");

        // scans
        sb.append(indent + "<tablescans>\n");
        for (RangeVariable rangeVariable : rangeVariables)
            sb.append(rangeVariable.voltGetXML(session, indent + HSQLInterface.XML_INDENT)).append("\n");
        sb.append(indent + "</tablescans>\n");

        // conditions
        if (select.queryCondition != null) {
            sb.append(indent).append("<querycondition>\n");
            sb.append(select.queryCondition.voltGetXML(session, indent + HSQLInterface.XML_INDENT)).append("\n");
            sb.append(indent).append("</querycondition>\n");
        }
        else {
            // look for inner joins expressed on range variables
            Expression cond = null;
            for (int rvi=0; rvi < select.rangeVariables.length; ++rvi) {
                RangeVariable rv = rangeVariables[rvi];
                // joins on non-indexed columns for inner join tokens created a range variable
                // and assigned this expression.
                if (rv.nonIndexJoinCondition != null) {
                    if (cond != null) {
                        cond = new ExpressionLogical(OpTypes.AND, cond, rv.nonIndexJoinCondition);
                    } else {
                        cond = rv.nonIndexJoinCondition;
                    }
                }
                // joins on indexed columns for inner join tokens created a range variable
                // and assigned an expression and set the flag isJoinIndex.
                else if (rv.isJoinIndex) {
                    if (rv.indexCondition != null) {
                        if (cond != null) {
                            cond = new ExpressionLogical(OpTypes.AND, cond, rv.indexCondition);
                        } else {
                            cond = rv.indexCondition;
                        }
                    }
                    if (rv.indexEndCondition != null) {
                        if (cond != null) {
                            cond = new ExpressionLogical(OpTypes.AND, cond, rv.indexCondition);
                        } else {
                            cond = rv.indexCondition;
                        }
                    }
                }
            }
            if (cond != null) {
                sb.append(indent).append("<querycondition>\n");
                sb.append(cond.voltGetXML(session, indent + HSQLInterface.XML_INDENT)).append("\n");
                sb.append(indent).append("</querycondition>\n");
            }
        }

        // having
        if (select.havingCondition != null) {
            sb.append(indent).append("<havingcondition>\n");
            sb.append(select.havingCondition.voltGetXML(session, indent + HSQLInterface.XML_INDENT)).append("\n");
            sb.append(indent).append("</havingcondition>\n");
        }

        // groupby
        if (select.isGrouped) {
            sb.append(indent + "<groupcolumns>\n");
            for (Expression groupByCol : groupByCols) {
                sb.append(groupByCol.voltGetXML(session, indent + HSQLInterface.XML_INDENT)).append("\n");
            }
            sb.append(indent + "</groupcolumns>\n");
        }
        // orderby
        if (orderByCols.size() > 0) {
            sb.append(indent + "<ordercolumns>\n");
            for (Expression orderByCol : orderByCols)
                sb.append(orderByCol.voltGetXML(session, indent + HSQLInterface.XML_INDENT)).append("\n");
            sb.append(indent + "</ordercolumns>\n");
        }

        sb.append(orig_indent).append("</select>");

        return sb.toString();
    }
}
TOP

Related Classes of org.hsqldb.StatementQuery$Pair

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.