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

oracle.kv.impl.query.runtime.AnyOpIter Maven / Gradle / Ivy

/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.impl.query.runtime;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;

import oracle.kv.impl.api.table.FieldDefImpl;
import oracle.kv.impl.api.table.FieldValueImpl;
import oracle.kv.impl.api.table.NullValueImpl;
import oracle.kv.impl.api.table.TupleValue;
import oracle.kv.impl.query.compiler.Expr;
import oracle.kv.impl.query.compiler.FuncAnyOp;
import oracle.kv.impl.query.compiler.FunctionLib.FuncCode;
import oracle.kv.impl.query.compiler.QueryFormatter;
import oracle.kv.impl.query.runtime.CompOpIter.CompResult;

/**
 * Iterator to implement the "any" comparison operators {@literal (=any, !=any,
 * any, and >=any)}. These operators have existential semantics:
 *
 * boolean AnyOp(any*, any*)
 *
 * Note: This implementation assumes no knowledge of the types of the items in
 * the 2 sequences. Each sequence may contain items of any type, including items
 * of different types. As a result, the implementation is not very efficient
 * when both sequences have more than one items, because it simply performs a
 * nested loop over the 2 sequences. Better implementations are possible when
 * we have type info. For example, if all the items in both sequences are of
 * the same type, then {@literal any, and >=any} can be
 * implemented by computing the min and max of each sequence and comparing
 * these min/max values. TODO: implement "specialized" versions of AnyOpIter
 * that make use of the static types of the input sequences.
 */
public class AnyOpIter extends PlanIter {

    static private class AnyOpIterState extends PlanIterState {

        final CompResult theResult = new CompResult();

        boolean theHaveNull;

        @Override
        public void reset(PlanIter iter) {
            super.reset(iter);
            theResult.clear();
            theHaveNull = false;
        }
    }

    private static final int theChunkSize = 10;

    private final FuncCode theAnyCode;

    private final FuncCode theCompCode;

    private final PlanIter theLeftOp;

    private final PlanIter theRightOp;

    public AnyOpIter(
        Expr e,
        int resultReg,
        FuncCode code,
        PlanIter[] argIters) {

        super(e, resultReg);
        theAnyCode = code;
        theCompCode = FuncAnyOp.anyToComp(code);
        assert(argIters.length == 2);
        theLeftOp = argIters[0];
        theRightOp = argIters[1];
    }

    /**
     * TupleSerialization constructor.
     */
    AnyOpIter(DataInput in, short serialVersion) throws IOException {
        super(in, serialVersion);

        short ordinal = readOrdinal(in, FuncCode.values().length);
        theAnyCode = FuncCode.values()[ordinal];
        theCompCode = FuncAnyOp.anyToComp(theAnyCode);
        theLeftOp = deserializeIter(in, serialVersion);
        theRightOp = deserializeIter(in, serialVersion);
    }

    /**
     * FastExternalizable writer.  Must call superclass method first to
     * write common elements.
     */
    @Override
    public void writeFastExternal(DataOutput out, short serialVersion)
            throws IOException {

        super.writeFastExternal(out, serialVersion);
        out.writeShort(theAnyCode.ordinal());
        serializeIter(theLeftOp, out, serialVersion);
        serializeIter(theRightOp, out, serialVersion);
    }

    @Override
    public PlanIterKind getKind() {
        return PlanIterKind.ANY_OP;
    }

    @Override
    FuncCode getFuncCode() {
        return theAnyCode;
    }


    @Override
    public void open(RuntimeControlBlock rcb) {
        rcb.setState(theStatePos, new AnyOpIterState());
        theLeftOp.open(rcb);
        theRightOp.open(rcb);
    }

    @Override
    public boolean next(RuntimeControlBlock rcb) {

        AnyOpIterState state = (AnyOpIterState)rcb.getState(theStatePos);

        if (state.isDone()) {
            return false;
        }

        FieldValueImpl lv1;
        FieldValueImpl lv2;
        FieldValueImpl rv1;
        FieldValueImpl rv2;
        boolean more;

        /*
         * Optimize for the case where at least one of the input sequences
         * contains at most one item.
         */

        more = theLeftOp.next(rcb);
        if (!more) {
            done(rcb, state, false);
            return true;
        }

        more = theRightOp.next(rcb);
        if (!more) {
            done(rcb, state, false);
            return true;
        }

        /*
         * Both the left and the right side have at least 1 item.
         * Compare the 1st items from each side now.
         */
        lv1 = rcb.getRegVal(theLeftOp.getResultReg());
        rv1 = rcb.getRegVal(theRightOp.getResultReg());

        if (compare(rcb, state, lv1, rv1)) {
            done(rcb, state, true);
            return true;
        }

        if (lv1.isTuple()) {
            lv1 = ((TupleValue)lv1).toRecord();
        }

        more = theLeftOp.next(rcb);

        if (!more) {

            /* Left size has exactly one item */
            while (theRightOp.next(rcb)) {

                rv2 = rcb.getRegVal(theRightOp.getResultReg());

                if (compare(rcb, state, lv1, rv2)) {
                    done(rcb, state, true);
                    return true;
                }
            }

            done(rcb, state, false);
            return true;
        }

        /* Left size has more than one item */
        lv2 = rcb.getRegVal(theLeftOp.getResultReg());

        if (rv1.isTuple()) {
            rv1 = ((TupleValue)rv1).toRecord();
        }

        more = theRightOp.next(rcb);

        if (!more) {

            /* Right size has exactly one item */
            if (compare(rcb, state, lv2, rv1)) {
               done(rcb, state, true);
               return true;
           }

           while (theLeftOp.next(rcb)) {

                lv2 = rcb.getRegVal(theLeftOp.getResultReg());

                if (compare(rcb, state, lv2, rv1)) {
                    done(rcb, state, true);
                    return true;
                }
           }

           done(rcb, state, false);
           return true;
        }

        rv2 = rcb.getRegVal(theRightOp.getResultReg());

        /*
         * Handle the general case. Compute a chunk of items from the left
         * side, and then compare these items with all of the items from
         * the right side. Repeat until a left item compares true with a
         * right item or until there are no more left items. The right-side
         * subplan is reset at the end of each left chunk (i.e., the right-
         * side subplan is re-computed fully for each left chunk).
         *
         * TODO consider chunking the right side as well
         */

        /*
         * lv2 and rv2 have not yet been compared with each other and with
         * lv1/rv1. Do these comparisons now, before handling the reset of
         * the sequences.
         */
        if (compare(rcb, state, lv1, rv2) ||
            compare(rcb, state, lv2, rv1) ||
            compare(rcb, state, lv2, rv2)) {
            done(rcb, state, true);
            return true;
        }

        ArrayList lchunk =
            new ArrayList(theChunkSize);

        while (true) {

            while (lchunk.size() < theChunkSize && theLeftOp.next(rcb)) {
                lv1 = rcb.getRegVal(theLeftOp.getResultReg());
                lchunk.add(lv1);
            }

            while (theRightOp.next(rcb)) {

                rv1 = rcb.getRegVal(theRightOp.getResultReg());

                for (int i = 0; i < lchunk.size(); ++i) {

                    lv1 = lchunk.get(i);

                    if (compare(rcb, state, lv1, rv1)) {
                        done(rcb, state, true);
                        return true;
                    }
                }
            }

            if (lchunk.size() == theChunkSize) {
                theRightOp.reset(rcb);
                lchunk.clear();
                continue;
            }

            break;
        }

        done(rcb, state, false);
        return true;
    }

    private void done(
        RuntimeControlBlock rcb,
        AnyOpIterState state,
        boolean result) {

        FieldValueImpl res = (state.theHaveNull && !result ?
                              NullValueImpl.getInstance()  :
                              FieldDefImpl.booleanDef.createBoolean(result));
        rcb.setRegVal(theResultReg, res);
        state.done();
    }

    private boolean compare(
        RuntimeControlBlock rcb,
        AnyOpIterState state,
        FieldValueImpl v0,
        FieldValueImpl v1) {

        state.theResult.clear();

        CompOpIter.compare(rcb,
                           v0,
                           v1,
                           theCompCode,
                           state.theResult,
                           getLocation());

        if (state.theResult.haveNull) {
            state.theHaveNull = true;
            return false;
        }

        if (state.theResult.incompatible) {
            return false;
        }

        int comp = state.theResult.comp;

        switch (theCompCode) {
        case OP_EQ:
            return (comp == 0);
        case OP_NEQ:
            return (comp != 0);
        case OP_GT:
            return (comp > 0);
        case OP_GE:
            return (comp >= 0);
        case OP_LT:
            return (comp < 0);
        case OP_LE:
            return (comp <= 0);
        default:
            assert(false);
            return false;
        }
    }

    @Override
    public void reset(RuntimeControlBlock rcb) {
        theLeftOp.reset(rcb);
        theRightOp.reset(rcb);
        PlanIterState state = rcb.getState(theStatePos);
        state.reset(this);
    }

    @Override
    public void close(RuntimeControlBlock rcb) {
        theLeftOp.close(rcb);
        theRightOp.close(rcb);
        PlanIterState state = rcb.getState(theStatePos);
        state.close();
    }

    @Override
    protected void displayContent(StringBuilder sb, QueryFormatter formatter) {
        theLeftOp.display(sb, formatter);
        sb.append(",\n");
        theRightOp.display(sb, formatter);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy