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

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

Go to download

NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.

The newest version!
/*-
 * 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 static oracle.kv.impl.util.SerialVersion.NAMESPACE_VERSION;
import static oracle.kv.impl.util.SerialVersion.QUERY_VERSION_2;
import static oracle.kv.impl.util.SerialVersion.QUERY_VERSION_5;
import static oracle.kv.impl.util.SerialVersion.QUERY_VERSION_6;
import static oracle.kv.impl.util.SerialVersion.QUERY_VERSION_7;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import oracle.kv.Direction;
import oracle.kv.impl.api.table.FieldValueImpl;
import oracle.kv.impl.api.table.IndexImpl;
import oracle.kv.impl.api.table.IndexKeyImpl;
import oracle.kv.impl.api.table.NameUtils;
import oracle.kv.impl.api.table.PrimaryKeyImpl;
import oracle.kv.impl.api.table.RecordDefImpl;
import oracle.kv.impl.api.table.RecordValueImpl;
import oracle.kv.impl.api.table.TableImpl;
import oracle.kv.impl.query.QueryException;
import oracle.kv.impl.query.compiler.Expr;
import oracle.kv.impl.query.compiler.FuncCompOp;
import oracle.kv.impl.query.compiler.FunctionLib.FuncCode;
import oracle.kv.impl.query.compiler.QueryFormatter;
import oracle.kv.impl.util.SerialVersion;
import oracle.kv.impl.util.SerializationUtil;
import oracle.kv.table.FieldRange;

/**
 * BaseTableIter performs table scans via the primary or a secondary index of
 * the table.
 *
 * The query plan generated by the compiler includes instances of BaseTableIter,
 * but the actual work is done by a ServerTableIter instance, which is created
 * during BaseTableIter.open(). After the creation of the worker iter, the
 * open/next/reset/close methods of BaseTableIter are propagated to that
 * worker. The reason for this separation is that ServerTableIter uses 
 * server-side classes, which are not available in the client jar.
 *
 * ServerIterFactory is an interface whose createTableIterator() method
 * does the job of creating the worker iter. Specifically, when the RCB is
 * created at the server, an instance of ServerIterFactoryImpl is also created
 * and stored in the RCB. Then, during BaseTableIter.open() the
 * ServerIterFactoryImpl stored in the RCB is used to create the
 * ServerTableIter.
 */
public class BaseTableIter extends PlanIter {

    protected final String theNamespace;

    protected final String[] theTableNames; // changed in QUERY_VERSION_6

    protected final int theNumAncestors; // added in QUERY_VERSION_6

    protected final int theNumDescendants; // added in QUERY_VERSION_6

    protected final String theIndexName;

    /*
     * The definition of the data returned by this iterator.
     */
    protected final RecordDefImpl theTypeDef;

    /*
     * The direction of the table scan
     */
    protected final Direction theDirection;

    /*
     * The primary key to use in accessing the base table. This will be null if
     * a secondary index must be used to access the table. It will be empty
     * if the primary index must be used to access the table, but there were
     * no equality predicates on primary-key columns to be pushed into the
     * primary index scan.
     *
     * Note: thePrimKey is stored here as a RecordValue, instead of PrimaryKey,
     * for 2 reasons:
     * - When we serialize the BaseTableIter, we don't want to serialize a
     *   PrimaryKey, because that contains a TableImpl as well.
     * - thePrimKey may include "placeholder" values that correspod to external
     *   variables. As a result, a new primary key instance must be created and
     *   stored in the plan state during the open() method. This new instance
     *   will actually be an instance of PrimaryKey.
     *
     * NOTE: In R4.5, this and the next 2 data members were changed to be arrays
     * of RecordValuee and FieldRanges. This is to allow for scanning multiple
     * ranges withing an index by a BaseTableIter. Currently, the need for
     * multi-range scans arises when a query contains EXISTS predicates that
     * are pushed to an index. EXISTS is translated to 2 range scans:
     * field < EMPTY and EMPTY < field.
     */
    protected final RecordValueImpl[] thePrimKeys;

    /*
     * The secondary key to use in accessing the base table. This will be null
     * if the primary index must be used to access the table. It will be empty
     * if a secondary index must be used to access the table, but there were
     * no equality predicates on secondary index columns to be pushed into the
     * index scan.
     *
     * Note: theSecKey is stored here as a RecordValue, instead of IndexKey,
     * for the same reasons described above for thePrimKey.
     */
    protected final RecordValueImpl[] theSecKeys;

    /*
     * An optional key range
     */
    protected final FieldRange[] theRanges;

    /*
     * thePredIters has an entry for each table. The entry is non-null if
     * there is a predicate that filters index or table rows during the scan
     * of the associated table. For the target table, the pred is always an
     * index-only pred that has been pushed down from the WHERE clause. For
     * the non-target tables, the pred is the ON pred, if any, associated
     * with the table. 
     */
    protected PlanIter[] thePredIters; // changed in QUERY_VERSION_6

    /*
     * For each tale, it tells whether the index used to access the table
     * is a covering index or not.
     */
    protected final boolean[] theUsesCoveringIndex;

    /*
     * True if the table will be accessed via a multikey index in a way that
     * may generate duplicate table rows. Such duplicates should be eliminated.
     *
     * added in QUERY_VERSION_2
     */
    protected final boolean theEliminateIndexDups;

    /*
     * added in QUERY_VERSION_5
     */
    protected final boolean theIsUpdate;

    /*
     * added in 18.3
     */
    protected final boolean theIsDelete;

    /*
     * See IndexAnalyzer.thePushedExternals
     */
    protected final PlanIter[][] thePushedExternals;

    /*
     * theTupleRegs are used differently depending on whether the iter accesses
     * a single table or implements a NESTED TABLES clause. In the 1st case, it
     * stores the columns of the current table row, so the number of regs is
     * equal to the number of table columns. In the 2nd case, the current table
     * row from each table, so the number of regs is equal to the number of
     * tables.
     */
    protected final int[] theTupleRegs;

    /*
     * theIndexTupleRegs are used for the target table only, when the index used
     * to access the table is a secondary one and the index is covering or the
     * query has index-only  filtering preds. In these cases, theIndexTupleRegs
     * store the columns of the current index entry.
     */
    protected int[] theIndexTupleRegs; // added in QUERY_VERSION_6

    protected int theIndexResultReg; // added in QUERY_VERSION_6

    /*
     * The "worker" iter associated with this BaseTableIter.
     */
    protected PlanIter theTableIter;

    protected short theVersion; // added in QUERY_VERSION_6

    /**
     * Constructor used by compiler during code generation.
     *
     * primKey and secKey will be both null if no predicates were pushed to
     * either the primary or any secondary index. In this case, an empty
     * primary key is created by this constructor, so that the table will be
     * accessed via a full scan of the primary index. If a range exists,
     * either primKey or secKey will be non-null to indicate whether the
     * range is over the primary or a secondary index. But the given key will
     * be empty, if no equality predicates were pushed to the index.
     */
    public BaseTableIter(
        Expr e,
        int resultReg,
        int[] tupleRegs,
        TableImpl table,
        List tables,
        int numAncestors,
        int numDescendants,
        Direction dir,
        ArrayList primKeys,
        ArrayList secKeys,
        ArrayList ranges,
        boolean[] coveringIndexes,
        boolean eliminateIndexDups,
        boolean isUpdate,
        boolean isDelete,
        PlanIter[] pushedExternals) {

        super(e, resultReg);

        assert(primKeys == null || secKeys == null);
        assert(ranges == null || primKeys != null || secKeys != null);

        theNamespace = table.getInternalNamespace();
        theTableNames = new String[tables.size()];

        for (int i = 0; i < tables.size(); ++i) {
            theTableNames[i] = tables.get(i).getFullName();
        }
        theNumAncestors = numAncestors;
        theNumDescendants = numDescendants;
        
        theTypeDef = (RecordDefImpl) e.getType().getDef();

        theDirection = dir;

        int numRanges = 1;

        if (primKeys == null && secKeys == null) {
            theSecKeys = null;
            theIndexName = null;
            thePrimKeys = new RecordValueImpl[numRanges];
            theRanges = new FieldRange[numRanges];
            thePrimKeys[0] = table.createPrimaryKey();
            theRanges[0] = null;

        } else if (primKeys != null) {
            theSecKeys = null;
            theIndexName = null;
            numRanges = ranges.size();
            thePrimKeys = new RecordValueImpl[numRanges];
            theRanges = new FieldRange[numRanges];

            for (int i = 0; i < numRanges; ++i) {
                thePrimKeys[i] = table.createPrimaryKey();
                thePrimKeys[i].copyFrom(primKeys.get(i));
                theRanges[i] = ranges.get(i);
            }

        } else {
            thePrimKeys = null;
            theIndexName = secKeys.get(0).getIndex().getName();
            numRanges = ranges.size();
            theSecKeys = new RecordValueImpl[numRanges];
            theRanges = new FieldRange[numRanges];

            for (int i = 0; i < numRanges; ++i) {
                theSecKeys[i] = secKeys.get(i).clone();
                theRanges[i] = ranges.get(i);
            }
        }

        thePredIters = new PlanIter[theTableNames.length];

        theUsesCoveringIndex = coveringIndexes;
        theEliminateIndexDups = eliminateIndexDups;
        theIsUpdate = isUpdate;
        theIsDelete = isDelete;
        thePushedExternals = new PlanIter[numRanges][];
        for (int i = 0; i < numRanges; ++i) {
            thePushedExternals[i] = pushedExternals;
        }

        theTupleRegs = tupleRegs;
    }

    /**
     * Constructor called during creation of ServerTableIter.
     */
    protected BaseTableIter(BaseTableIter parent) {

        super(parent.theStatePos, parent.theResultReg, parent.theLocation);

        theNamespace = parent.theNamespace;
        theTableNames = parent.theTableNames;
        thePredIters = parent.thePredIters;
        theNumAncestors = parent.theNumAncestors;
        theNumDescendants = parent.theNumDescendants;
        theIndexName = parent.theIndexName;

        theTypeDef = parent.theTypeDef;

        theDirection = parent.theDirection;

        thePrimKeys = parent.thePrimKeys;
        theSecKeys = parent.theSecKeys;
        theRanges = parent.theRanges;

        theUsesCoveringIndex = parent.theUsesCoveringIndex;
        theEliminateIndexDups = parent.theEliminateIndexDups;
        theIsUpdate = parent.theIsUpdate;
        theIsDelete = parent.theIsDelete;

        thePushedExternals = parent.thePushedExternals;

        theTupleRegs = parent.theTupleRegs;
        theIndexResultReg = parent.theIndexResultReg;
        theIndexTupleRegs = parent.theIndexTupleRegs;

        theVersion = parent.theVersion;
    }

    BaseTableIter(DataInput in, short serialVersion) throws IOException {

        super(in, serialVersion);

        theVersion = serialVersion;

        if (serialVersion >= NAMESPACE_VERSION) {
            theNamespace = SerializationUtil.readString(in, serialVersion);
        } else {
            theNamespace = null;
        }

        if (serialVersion >= QUERY_VERSION_6) {
            theTableNames = PlanIter.deserializeStringArray(in, serialVersion);
            theNumAncestors = in.readInt();
            theNumDescendants = in.readInt();
        } else {
            theTableNames = new String[1];
            theTableNames[0] = SerializationUtil.readString(in, serialVersion);
            theNumAncestors = 0;
            theNumDescendants = 0;
        }

        theIndexName = SerializationUtil.readString(in, serialVersion);

        theTypeDef = (RecordDefImpl)deserializeFieldDef(in, serialVersion);
        short ordinal = readOrdinal(in, Direction.values().length);
        theDirection = Direction.valueOf(ordinal);

        int numRanges = 1;

        if (serialVersion >= QUERY_VERSION_5) {
            numRanges = readPositiveInt(in);
        }

        if (theIndexName == null) {
            thePrimKeys = new RecordValueImpl[numRanges];
            for (int i = 0; i < numRanges; ++i) {
                thePrimKeys[i] = deserializeKey(in, serialVersion);
            }
            theSecKeys = null;
        } else {
            thePrimKeys = null;
            theSecKeys = new RecordValueImpl[numRanges];
            for (int i = 0; i < numRanges; ++i) {
                theSecKeys[i] = deserializeKey(in, serialVersion);
            }
        }

        theRanges = new FieldRange[numRanges];
        for (int i = 0; i < numRanges; ++i) {
            theRanges[i] = deserializeFieldRange(in, serialVersion);
        }

        if (serialVersion >= QUERY_VERSION_6) {
            theUsesCoveringIndex = PlanIter.deserializeBooleanArray(in);
        } else {
            theUsesCoveringIndex = new boolean[1];
            theUsesCoveringIndex[0] = in.readBoolean();
        }

        if (serialVersion < QUERY_VERSION_2) {
            theEliminateIndexDups = false;
        } else {
            theEliminateIndexDups = in.readBoolean();
        }

        if (serialVersion < QUERY_VERSION_5) {
            theIsUpdate = false;
        } else {
            theIsUpdate = in.readBoolean();
        }

        if (serialVersion < QUERY_VERSION_7) {
            theIsDelete = false;
        } else {
            theIsDelete = in.readBoolean();
        }

        thePushedExternals = new PlanIter[numRanges][];
        for (int i = 0; i < numRanges; ++i) {
            thePushedExternals[i] = deserializeIters(in, serialVersion);
        }

       if (serialVersion >= QUERY_VERSION_6) {
           thePredIters = PlanIter.deserializeIters(in, serialVersion);
       } else {
           PlanIter filterIter = deserializeIter(in, serialVersion);
           thePredIters = new PlanIter[1];
           thePredIters[0] = filterIter;
       }

        theTupleRegs = deserializeIntArray(in, serialVersion);

        if (serialVersion < QUERY_VERSION_6) {
            theIndexResultReg = -1;
            theIndexTupleRegs = null;
        } else {
            theIndexResultReg = readPositiveInt(in, true);
            theIndexTupleRegs = deserializeIntArray(in, serialVersion);
        }
    }

    @Override
    public void writeFastExternal(DataOutput out, short serialVersion)
            throws IOException {

        super.writeFastExternal(out, serialVersion);

        if (serialVersion >= NAMESPACE_VERSION) {
            SerializationUtil.writeString(out, serialVersion, theNamespace);
        }

        if (serialVersion >= QUERY_VERSION_6) {
            PlanIter.serializeStringArray(theTableNames, out, serialVersion);
            out.writeInt(theNumAncestors);
            out.writeInt(theNumDescendants);
        } else if (theTableNames.length == 1) {
            SerializationUtil.writeString(out, serialVersion, theTableNames[0]);
        } else {
            String QV6String =
                SerialVersion.getKVVersion(QUERY_VERSION_6).
                getNumericVersionString();

            throw new QueryException(
                "Cannot execute a query with a NESTED TABLES clause " +
                "at a server whose version is less than " +
                QV6String + "\nserialVersion = " + serialVersion +
                " expected version = " + QUERY_VERSION_6);
        }

        SerializationUtil.writeString(out, serialVersion, theIndexName);
        serializeFieldDef(theTypeDef, out, serialVersion);
        out.writeShort(theDirection.ordinal());

        if (serialVersion >= QUERY_VERSION_5) {

            if (theIndexName == null) {
                out.writeInt(thePrimKeys.length);
                for (RecordValueImpl pk : thePrimKeys) {
                    serializeKey(pk, out, serialVersion);
                }
            } else {
                out.writeInt(theSecKeys.length);
                for (RecordValueImpl sk : theSecKeys) {
                    serializeKey(sk, out, serialVersion);
                }
            }

            for (FieldRange fr : theRanges) {
                serializeFieldRange(fr, out, serialVersion);
            }

        } else if (theIndexName == null && thePrimKeys.length == 1) {
            serializeKey(thePrimKeys[0], out, serialVersion);
            serializeFieldRange(theRanges[0], out, serialVersion);

        } else if (theIndexName != null && theSecKeys.length == 1) {
            serializeKey(theSecKeys[0], out, serialVersion);
            serializeFieldRange(theRanges[0], out, serialVersion);

        } else {
            String QV5String =
                SerialVersion.getKVVersion(QUERY_VERSION_5).
                getNumericVersionString();

            /*
             * See java doc for this.thePrimKeys for the kind of queries that
             * may require more than 1 range scans inside the same index.
             */
            throw new QueryException(
                "Cannot execute a query that scans more than one ranges " +
                "inside an index at a server whose version is less than " +
                QV5String + "\nserialVersion = " + serialVersion +
                " expected version = " + QUERY_VERSION_5);
        }

        if (serialVersion >= QUERY_VERSION_6) {
            PlanIter.serializeBooleanArray(theUsesCoveringIndex, out);
        } else {
            out.writeBoolean(theUsesCoveringIndex[0]);
        }

        if (serialVersion >= QUERY_VERSION_2) {
            out.writeBoolean(theEliminateIndexDups);
        }

        if (serialVersion >= QUERY_VERSION_5) {
            out.writeBoolean(theIsUpdate);
        }

        if (serialVersion >= QUERY_VERSION_7) {
            out.writeBoolean(theIsUpdate);
        }

        if (serialVersion >= QUERY_VERSION_5) {
            for (int i = 0; i < thePushedExternals.length; ++i) {
                serializeIters(thePushedExternals[i], out, serialVersion);
            }
        } else {
            serializeIters(thePushedExternals[0], out, serialVersion);
        }

        if (serialVersion >= QUERY_VERSION_6) {
            serializeIters(thePredIters, out, serialVersion);
        } else if (thePredIters != null) {
            assert(thePredIters.length == 1);
            serializeIter(thePredIters[0], out, serialVersion);
        } else {
            serializeIter(null, out, serialVersion);
        }

        serializeIntArray(theTupleRegs, out, serialVersion);

        if (serialVersion >= QUERY_VERSION_6) {
            out.writeInt(theIndexResultReg);
            serializeIntArray(theIndexTupleRegs, out, serialVersion);
        } else {
            String QV6String =
                SerialVersion.getKVVersion(QUERY_VERSION_6).
                getNumericVersionString();

            throw new QueryException(
                "Cannot execute a query that uses a covering index or " +
                "applies index-filtering predicates at a server whose " +
                "version is less than " +
                QV6String + "\nserialVersion = " + serialVersion +
                " expected version = " + QUERY_VERSION_6);
        }
    }

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

    @Override
    public int[] getTupleRegs() {
        return theTupleRegs;
    }

    public void setPredIter(int tablePos, PlanIter iter) {
        thePredIters[tablePos] = iter; 
    }

    protected PlanIter getTargetTablePred() {
        if (thePredIters.length > 0) {
            return thePredIters[theNumAncestors];
        }
        return null;
    }

    public void setIndexRegs(int resultReg, int[] tupleRegs) {
        theIndexResultReg = resultReg;
        theIndexTupleRegs = tupleRegs;
    }

    @Override
    public void open(RuntimeControlBlock rcb) {

        /*
         * If the actual iterator has not yet been created, do so now.
         */
        if (theTableIter == null) {
            ServerIterFactory serverIterFactory = rcb.getServerIterFactory();
            theTableIter = serverIterFactory.createTableIter(rcb, this);
        }
        theTableIter.open(rcb);
    }

    @Override
    public boolean next(RuntimeControlBlock rcb) {

        boolean retVal = false;

        if (theTableIter != null) {
            retVal = theTableIter.next(rcb);
        }

        return retVal;
    }

    @Override
    public void reset(RuntimeControlBlock rcb) {
        if (theTableIter != null) {
            theTableIter.reset(rcb);
        }
    }

    @Override
    public void close(RuntimeControlBlock rcb) {
        if (theTableIter != null) {
            theTableIter.close(rcb);
        }
    }

    @Override
    protected void display(StringBuilder sb, QueryFormatter formatter) {

        formatter.indent(sb);
        sb.append(getKind());

        displayRegs(sb);

        if (theIndexTupleRegs != null) {
            sb.append("\n");
            formatter.indent(sb);
            sb.append("Index entry regs: ").append("(");
            sb.append("[").append(theIndexResultReg).append("], ");
            for (int i = 0; i < theIndexTupleRegs.length; ++i) {
                sb.append(theIndexTupleRegs[i]);
                if (i < theIndexTupleRegs.length - 1) {
                    sb.append(", ");
                }
            }
            sb.append(")");
        }

        displayContent(sb, formatter);
    }

    @Override
    protected void displayContent(StringBuilder sb, QueryFormatter formatter) {

        int numRanges = theRanges.length;

        sb.append("\n");
        formatter.indent(sb);
        sb.append("[\n");

        formatter.incIndent();
        formatter.indent(sb);
        sb.append(NameUtils.makeQualifiedName(theNamespace,
                                    theTableNames[theNumAncestors]));

        if (thePrimKeys != null) {

            if (theUsesCoveringIndex[theNumAncestors]) {
                sb.append(" via covering primary index");
            } else {
                sb.append(" via primary index");
            }

            for (int i = 0; i < numRanges; ++i) {
                sb.append("\n");
                formatter.indent(sb);
                sb.append("KEY: ");
                sb.append(thePrimKeys[i]);

                sb.append("\n");
                formatter.indent(sb);
                sb.append("RANGE: ");
                sb.append(theRanges[i]);

                displayPushedExternals(sb, formatter, i);
            }
        }

        if (theSecKeys != null) {

            if (theUsesCoveringIndex[theNumAncestors]) {
                sb.append(" via covering index ");
            } else {
                sb.append(" via index ");
            }
            sb.append(theIndexName);
            if (theEliminateIndexDups) {
                sb.append(" with duplicate elimination");
            }

            for (int i = 0; i < numRanges; ++i) {
                sb.append("\n");
                formatter.indent(sb);
                sb.append("SEC KEY: ");
                sb.append(theSecKeys[i]);

                sb.append("\n");
                formatter.indent(sb);
                sb.append("RANGE: ");
                sb.append(theRanges[i]);

                displayPushedExternals(sb, formatter, i);
            }
        }

        if (theNumAncestors > 0) {
            sb.append("\n\n");
            formatter.indent(sb);
            sb.append("Ancestors :");
            for (int i = 0; i < theNumAncestors; ++i) {
                sb.append("\n");
                formatter.indent(sb);
                sb.append(theTableNames[i]);
                if (theUsesCoveringIndex[i]) {
                    sb.append(" via covering primary index");
                } else {
                    sb.append(" via primary index");
                }
            }
        }

        if (theNumDescendants > 0) {
            sb.append("\n\n");
            formatter.indent(sb);
            sb.append("Descendantss :");
            for (int i = theNumAncestors + 1; i < theTableNames.length; ++i) {
                sb.append("\n");
                formatter.indent(sb);
                sb.append(theTableNames[i]);
                if (theUsesCoveringIndex[i]) {
                    sb.append(" via covering primary index");
                } else {
                    sb.append(" via primary index");
                }
            }
        }

        if (thePredIters != null) {

            if (thePredIters[theNumAncestors] != null) {
                sb.append("\n\n");
                formatter.indent(sb);
                sb.append("Filtering Predicate:\n");
                thePredIters[theNumAncestors].display(sb, formatter);
            }

            for (int i = 0; i < theTableNames.length; ++i) {

                if (i == theNumAncestors || thePredIters[i] == null) {
                    continue;
                }

                sb.append("\n\n");
                formatter.indent(sb);
                sb.append("ON Predicate for table ").
                   append(theTableNames[i]).
                   append(":\n");
                thePredIters[i].display(sb, formatter);
            }
        }

        sb.append("\n");
        formatter.decIndent();
        formatter.indent(sb);
        sb.append("]");
    }

    private void displayPushedExternals(
        StringBuilder sb,
        QueryFormatter formatter,
        int pos) {

        if (thePushedExternals == null) {
            return;
        }

        PlanIter[] pushedExternals = thePushedExternals[pos];

        if (pushedExternals == null) {
            return;
        }

        sb.append("\n\n");
        formatter.indent(sb);
        sb.append("EXTERNAL KEY EXPRS: ");
        sb.append(pushedExternals.length);

        for (PlanIter iter : pushedExternals) {

            sb.append("\n");
            if (iter != null) {
                iter.display(sb, formatter);
            } else {
                formatter.indent(sb);
                sb.append("null");
            }
        }
    }

    static protected FieldValueImpl castValueToIndexKey(
        TableImpl table,
        IndexImpl index,
        int keyPos,
        FieldValueImpl val,
        FuncCode opcode) {

        if (index != null) {
            return FuncCompOp.castConstInCompOp(
                index.getFieldDef(keyPos),
                index.getIndexPath(keyPos).isJson(), /*allowJsonNull*/
                false, /*nullable*/
                true, /*scalar*/
                val,
                opcode,
                false/*strict*/);
        }

        return FuncCompOp.castConstInCompOp(
            table.getPrimKeyColumnDef(keyPos),
            false, /*allowJsonNull*/
            false, /*nullable*/
            true, /*scalar*/
            val,
            opcode,
            false/*strict*/);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy