
oracle.kv.impl.query.runtime.BaseTableIter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of oracle-nosql-server Show documentation
Show all versions of oracle-nosql-server Show documentation
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