All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lealone.sql.query.SelectUnion Maven / Gradle / Ivy

/*
 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.lealone.sql.query;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.lealone.common.exceptions.DbException;
import org.lealone.common.util.StringUtils;
import org.lealone.db.SysProperties;
import org.lealone.db.api.ErrorCode;
import org.lealone.db.async.AsyncHandler;
import org.lealone.db.async.AsyncResult;
import org.lealone.db.result.LocalResult;
import org.lealone.db.result.Result;
import org.lealone.db.result.ResultTarget;
import org.lealone.db.session.ServerSession;
import org.lealone.db.table.Column;
import org.lealone.db.table.Table;
import org.lealone.db.value.Value;
import org.lealone.sql.ISelectUnion;
import org.lealone.sql.PreparedSQLStatement;
import org.lealone.sql.SQLStatement;
import org.lealone.sql.executor.YieldableBase;
import org.lealone.sql.expression.Expression;
import org.lealone.sql.expression.ExpressionColumn;
import org.lealone.sql.expression.Parameter;
import org.lealone.sql.expression.visitor.ExpressionVisitor;
import org.lealone.sql.optimizer.ColumnResolver;
import org.lealone.sql.optimizer.TableFilter;

/**
 * Represents a union SELECT statement.
 * 
 * @author H2 Group
 * @author zhh
 */
public class SelectUnion extends Query implements ISelectUnion {

    final int unionType;
    final Query left;
    final Query right;

    public SelectUnion(ServerSession session, int unionType, Query left, Query right) {
        super(session);
        this.unionType = unionType;
        this.left = left;
        this.right = right;
    }

    @Override
    public int getType() {
        return SQLStatement.SELECT;
    }

    @Override
    public int getUnionType() {
        return unionType;
    }

    @Override
    public Query getLeft() {
        return left;
    }

    @Override
    public Query getRight() {
        return right;
    }

    @Override
    public Result getMetaData() {
        int columnCount = left.getColumnCount();
        LocalResult result = new LocalResult(session, expressionArray, columnCount);
        result.done();
        return result;
    }

    @Override
    public LocalResult getEmptyResult() {
        int columnCount = left.getColumnCount();
        return new LocalResult(session, expressionArray, columnCount);
    }

    @Override
    public void init() {
        if (SysProperties.CHECK && checkInit) {
            DbException.throwInternalError();
        }
        checkInit = true;
        left.init();
        right.init();
        int len = left.getColumnCount();
        if (len != right.getColumnCount()) {
            throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
        }
        ArrayList le = left.getExpressions();
        // set the expressions to get the right column count and names,
        // but can't validate at this time
        expressions = new ArrayList<>(len);
        for (int i = 0; i < len; i++) {
            Expression l = le.get(i);
            expressions.add(l);
        }
    }

    @Override
    public PreparedSQLStatement prepare() {
        if (isPrepared) {
            // sometimes a subquery is prepared twice (CREATE TABLE AS SELECT)
            return this;
        }
        if (SysProperties.CHECK && !checkInit) {
            DbException.throwInternalError("not initialized");
        }
        isPrepared = true;
        left.prepare();
        right.prepare();
        int len = left.getColumnCount();
        // set the correct expressions now
        expressions = new ArrayList<>(len);
        ArrayList le = left.getExpressions();
        ArrayList re = right.getExpressions();
        for (int i = 0; i < len; i++) {
            Expression l = le.get(i);
            Expression r = re.get(i);
            int type = Value.getHigherOrder(l.getType(), r.getType());
            long prec = Math.max(l.getPrecision(), r.getPrecision());
            int scale = Math.max(l.getScale(), r.getScale());
            int displaySize = Math.max(l.getDisplaySize(), r.getDisplaySize());
            Column col = new Column(l.getAlias(), type, prec, scale, displaySize);
            Expression e = new ExpressionColumn(session.getDatabase(), col);
            expressions.add(e);
        }
        if (orderList != null) {
            initOrder(session, expressions, null, orderList, getColumnCount(), true, null);
            sort = prepareOrder(session, orderList, expressions.size());
            orderList = null;
        }
        expressionArray = new Expression[expressions.size()];
        expressions.toArray(expressionArray);
        return this;
    }

    @Override
    public double getCost() {
        return left.getCost() + right.getCost();
    }

    @Override
    public HashSet getTables() {
        HashSet
set = left.getTables(); set.addAll(right.getTables()); return set; } @Override public void setForUpdate(boolean forUpdate) { left.setForUpdate(forUpdate); right.setForUpdate(forUpdate); isForUpdate = forUpdate; } @Override public int getColumnCount() { return left.getColumnCount(); } @Override public void mapColumns(ColumnResolver resolver, int level) { left.mapColumns(resolver, level); right.mapColumns(resolver, level); } @Override public String getPlanSQL() { StringBuilder buff = new StringBuilder(); buff.append('(').append(left.getPlanSQL()).append(')'); switch (unionType) { case UNION_ALL: buff.append("\nUNION ALL\n"); break; case UNION: buff.append("\nUNION\n"); break; case INTERSECT: buff.append("\nINTERSECT\n"); break; case EXCEPT: buff.append("\nEXCEPT\n"); break; default: DbException.throwInternalError("type=" + unionType); } buff.append('(').append(right.getPlanSQL()).append(')'); Expression[] exprList = expressions.toArray(new Expression[expressions.size()]); if (sort != null) { buff.append("\nORDER BY ").append(sort.getSQL(exprList, exprList.length)); } if (limitExpr != null) { buff.append("\nLIMIT ").append(StringUtils.unEnclose(limitExpr.getSQL())); if (offsetExpr != null) { buff.append("\nOFFSET ").append(StringUtils.unEnclose(offsetExpr.getSQL())); } } if (sampleSizeExpr != null) { buff.append("\nSAMPLE_SIZE ").append(StringUtils.unEnclose(sampleSizeExpr.getSQL())); } if (isForUpdate) { buff.append("\nFOR UPDATE"); } return buff.toString(); } @Override public R accept(ExpressionVisitor visitor) { return visitor.visitSelectUnion(this); } @Override public void fireBeforeSelectTriggers() { left.fireBeforeSelectTriggers(); right.fireBeforeSelectTriggers(); } @Override public List getFilters() { List filters = left.getFilters(); filters.addAll(right.getFilters()); return filters; } @Override public List getTopFilters() { List filters = left.getTopFilters(); filters.addAll(right.getTopFilters()); return filters; } @Override public boolean allowGlobalConditions() { return left.allowGlobalConditions() && right.allowGlobalConditions(); } @Override public void addGlobalCondition(Parameter param, int columnId, int comparisonType) { addParameter(param); switch (unionType) { case UNION_ALL: case UNION: case INTERSECT: { left.addGlobalCondition(param, columnId, comparisonType); right.addGlobalCondition(param, columnId, comparisonType); break; } case EXCEPT: { left.addGlobalCondition(param, columnId, comparisonType); break; } default: DbException.throwInternalError("type=" + unionType); } } @Override public int getPriority() { return Math.min(left.getPriority(), right.getPriority()); } @Override public Result query(int maxRows, ResultTarget target) { YieldableSelectUnion yieldable = new YieldableSelectUnion(this, maxRows, false, null, target); return syncExecute(yieldable); } @Override public YieldableBase createYieldableQuery(int maxRows, boolean scrollable, AsyncHandler> asyncHandler, ResultTarget target) { return new YieldableSelectUnion(this, maxRows, scrollable, asyncHandler, target); } }