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

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

/*
 * Copyright Lealone Database Group.
 * Licensed under the Server Side Public License, v 1.
 * Initial Developer: zhh
 */
package org.lealone.sql.query;

import org.lealone.common.exceptions.DbException;
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.SessionStatus;
import org.lealone.db.value.Value;
import org.lealone.db.value.ValueInt;
import org.lealone.db.value.ValueNull;
import org.lealone.sql.executor.YieldableBase;
import org.lealone.sql.expression.Expression;
import org.lealone.sql.expression.ValueExpression;

class YieldableSelectUnion extends YieldableQueryBase {

    private final SelectUnion selectUnion;
    private final ResultTarget target;

    private Expression limitExpr;
    private boolean insertFromSelect;
    private YieldableBase leftYieldableQuery;
    private YieldableBase rightYieldableQuery;
    private int columnCount;
    private LocalResult result;
    private Result leftRows;
    private Result rightRows;
    private LocalResult temp;
    private int rowNumber;
    private boolean done;

    public YieldableSelectUnion(SelectUnion selectUnion, int maxRows, boolean scrollable,
            AsyncHandler> asyncHandler, ResultTarget target) {
        super(selectUnion, maxRows, scrollable, asyncHandler);
        this.selectUnion = selectUnion;
        this.target = target;
    }

    @Override
    protected boolean startInternal() {
        selectUnion.fireBeforeSelectTriggers();
        // union doesn't always know the parameter list of the left and right queries
        if (maxRows != 0) {
            // maxRows is set (maxRows 0 means no limit)
            int l;
            if (limitExpr == null) {
                l = -1;
            } else {
                Value v = limitExpr.getValue(session);
                l = v == ValueNull.INSTANCE ? -1 : v.getInt();
            }
            if (l < 0) {
                // for limitExpr, 0 means no rows, and -1 means no limit
                l = maxRows;
            } else {
                l = Math.min(l, maxRows);
            }
            limitExpr = ValueExpression.get(ValueInt.get(l));
        }

        if (session.getDatabase().getSettings().optimizeInsertFromSelect) {
            if (selectUnion.unionType == SelectUnion.UNION_ALL && target != null) {
                if (selectUnion.sort == null && !selectUnion.distinct && maxRows == 0
                        && selectUnion.offsetExpr == null && limitExpr == null) {
                    insertFromSelect = true;
                    leftYieldableQuery = selectUnion.left.createYieldableQuery(0, false, null, target);
                    rightYieldableQuery = selectUnion.right.createYieldableQuery(0, false, null, target);
                    return false;
                }
            }
        }
        columnCount = selectUnion.left.getColumnCount();
        result = new LocalResult(session, selectUnion.expressionArray, columnCount);
        if (selectUnion.sort != null) {
            result.setSortOrder(selectUnion.sort);
        }
        if (selectUnion.distinct) {
            selectUnion.left.setDistinct(true);
            selectUnion.right.setDistinct(true);
            result.setDistinct();
        }
        switch (selectUnion.unionType) {
        case SelectUnion.UNION:
        case SelectUnion.EXCEPT:
            selectUnion.left.setDistinct(true);
            selectUnion.right.setDistinct(true);
            result.setDistinct();
            break;
        case SelectUnion.UNION_ALL:
            break;
        case SelectUnion.INTERSECT:
            selectUnion.left.setDistinct(true);
            selectUnion.right.setDistinct(true);
            temp = new LocalResult(session, selectUnion.expressionArray, columnCount);
            temp.setDistinct();
            break;
        default:
            DbException.throwInternalError("type=" + selectUnion.unionType);
        }
        leftYieldableQuery = selectUnion.left.createYieldableQuery(0, false, null, null);
        rightYieldableQuery = selectUnion.right.createYieldableQuery(0, false, null, null);
        return false;
    }

    @Override
    protected void stopInternal() {
    }

    @Override
    protected void executeInternal() {
        if (insertFromSelect) {
            if (leftYieldableQuery != null) {
                leftYieldableQuery.run();
                if (leftYieldableQuery.isStopped()) {
                    leftYieldableQuery = null;
                }
            }
            if (leftYieldableQuery == null && rightYieldableQuery != null) {
                rightYieldableQuery.run();
                if (rightYieldableQuery.isStopped()) {
                    rightYieldableQuery = null;
                }
            }
        }

        switch (selectUnion.unionType) {
        case SelectUnion.UNION_ALL:
        case SelectUnion.UNION: {
            if (leftYieldableQuery != null && runLeftQuery()) {
                return;
            }
            if (leftRows != null && addLeftRows()) {
                return;
            }

            if (rightYieldableQuery != null && runRightQuery()) {
                return;
            }
            if (rightRows != null && addRightRows()) {
                return;
            }
            break;
        }
        case SelectUnion.EXCEPT: {
            if (leftYieldableQuery != null && runLeftQuery()) {
                return;
            }
            if (leftRows != null && addLeftRows()) {
                return;
            }

            if (rightYieldableQuery != null && runRightQuery()) {
                return;
            }
            if (rightRows != null) {
                while (rightRows.next()) {
                    result.removeDistinct(convert(rightRows.currentRow(), columnCount));
                    if (yieldIfNeeded(++rowNumber)) {
                        return;
                    }
                }
                rightRows = null;
            }
            break;
        }
        case SelectUnion.INTERSECT: {
            if (leftYieldableQuery != null && runLeftQuery()) {
                return;
            }
            if (leftRows != null) {
                while (leftRows.next()) {
                    temp.addRow(convert(leftRows.currentRow(), columnCount));
                    if (yieldIfNeeded(++rowNumber)) {
                        return;
                    }
                }
                leftRows = null;
            }

            if (rightYieldableQuery != null && runRightQuery()) {
                return;
            }
            if (rightRows != null) {
                while (rightRows.next()) {
                    Value[] values = convert(rightRows.currentRow(), columnCount);
                    if (temp.containsDistinct(values)) {
                        result.addRow(values);
                    }
                    if (yieldIfNeeded(++rowNumber)) {
                        return;
                    }
                }
                rightRows = null;
            }
            break;
        }
        default:
            DbException.throwInternalError("type=" + selectUnion.unionType);
        }
        if (!done) {
            if (selectUnion.offsetExpr != null) {
                result.setOffset(selectUnion.offsetExpr.getValue(session).getInt());
            }
            if (limitExpr != null) {
                Value v = limitExpr.getValue(session);
                if (v != ValueNull.INSTANCE) {
                    result.setLimit(v.getInt());
                }
            }
            rowNumber = 0;
            result.done();
            done = true;
        }
        if (target == null) {
            setResult(result, result.getRowCount());
        } else {
            while (result.next()) {
                target.addRow(result.currentRow());
                if (yieldIfNeeded(++rowNumber)) {
                    return;
                }
            }
            result.close();
        }
        session.setStatus(SessionStatus.STATEMENT_COMPLETED);
    }

    private boolean runLeftQuery() {
        if (leftRows == null) {
            leftYieldableQuery.run();
            if (leftYieldableQuery.isStopped()) {
                rowNumber = 0;
                leftRows = leftYieldableQuery.getResult();
                leftYieldableQuery = null;
                return false;
            }
        }
        return true;
    }

    private boolean runRightQuery() {
        if (rightRows == null) {
            rightYieldableQuery.run();
            if (rightYieldableQuery.isStopped()) {
                rowNumber = 0;
                rightRows = rightYieldableQuery.getResult();
                rightYieldableQuery = null;
                return false;
            }
        }
        return true;
    }

    private boolean addLeftRows() {
        while (leftRows.next()) {
            result.addRow(convert(leftRows.currentRow(), columnCount));
            if (yieldIfNeeded(++rowNumber)) {
                return true;
            }
        }
        leftRows = null;
        return false;
    }

    private boolean addRightRows() {
        while (rightRows.next()) {
            result.addRow(convert(rightRows.currentRow(), columnCount));
            if (yieldIfNeeded(++rowNumber)) {
                return true;
            }
        }
        rightRows = null;
        return false;
    }

    private Value[] convert(Value[] values, int columnCount) {
        Value[] newValues;
        if (columnCount == values.length) {
            // re-use the array if possible
            newValues = values;
        } else {
            // create a new array if needed,
            // for the value hash set
            newValues = new Value[columnCount];
        }
        for (int i = 0; i < columnCount; i++) {
            Expression e = selectUnion.expressions.get(i);
            newValues[i] = values[i].convertTo(e.getType());
        }
        return newValues;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy