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

oracle.kv.impl.query.compiler.ExprBaseTable Maven / Gradle / Ivy

Go to download

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

There is a newer version: 18.3.10
Show 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.compiler;

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

import oracle.kv.Direction;
import oracle.kv.impl.api.table.IndexImpl;
import oracle.kv.impl.api.table.IndexKeyImpl;
import oracle.kv.impl.api.table.FieldDefFactory;
import oracle.kv.impl.api.table.FieldMap;
import oracle.kv.impl.api.table.PrimaryKeyImpl;
import oracle.kv.impl.api.table.RecordDefImpl;
import oracle.kv.impl.api.table.TableImpl;
import oracle.kv.impl.query.QueryException;
import oracle.kv.impl.query.QueryStateException;
import oracle.kv.impl.query.QueryException.Location;
import oracle.kv.impl.query.types.ExprType;
import oracle.kv.impl.query.types.ExprType.Quantifier;
import oracle.kv.impl.query.types.TypeManager;
import oracle.kv.table.FieldRange;


/**
 * ExprBaseTable is an internal expression representing a single table or a
 * NESTED TABLES clause appearing in a FROM clause of a query. A NESTED TABLES
 * clause consists of a target table and a number of ancestor and/or descendant
 * tables of the target table.
 *
 * Evaluation of this expr returns a set of rows. In the single-table case, the 
 * rows are the rows of the referenced table and they are returned as tuples,
 * i.e., if the rows of the table consist of N columns, the expression returns
 * N values per row (stored in a TupleValue). In the NESTED TABLES case, the
 * returned rows are composite rows constructed by the left-outer-joins among
 * the participating tables. Each composite row has N columns, where N is the
 * number of tables in the NESTED TABLES. The value of each column is a row
 * from the associated table or NULL if there is no row from the associated
 * table that matches with the other table rows in the composite row. The
 * N values per composite row are stored in a TupleValue,
 *
 * theTargetTable:
 * The target table
 *
 * theTables:
 * Stores the TableImpl for each referenced table. In the NESTED TABLES case,
 * the tables are ordered in the order that they would be encountered during
 * a depth-first traversal of the table hierarchy tree (so, the ancestor 
 * tables are first followed by the target table, and then the descendandant
 * tables). The position of each table is this array serves as the id of the
 * table in the query context.
 *
 * theAliases:
 * Mirrors theTables and stores the alias used for each table.
 *
 * theNumAncestors:
 * The number of ancestor tables.
 *
 * theNumDescendants:
 * The number of descendant tables.
 *
 * theTablePreds:
 * Mirrors theTables and stores the condition expr, if any, that must be applied
 * on each table row during the evaluation of this ExprBaseTable. For the target
 * table, the condition is an index-filtering pred (i.e., can be evaluated by
 * index columns only) that has been pushed down from the WHERE clause. For each
 * non-target table, the condition is the ON predicate that appears in the
 * NESTED TABLES clause next to that table; it may be an index-filtering pred
 * or not.
 *
 * theUsesCoveringIndex:
 * For each table, it says whether the index used to access the table is
 * covering or not.
 */
class ExprBaseTable extends Expr {

    static class IndexHint {

        IndexImpl theIndex; // null means the primary index
        boolean theForce;

        IndexHint(IndexImpl index, boolean force) {
            theIndex = index;
            theForce = force;
        }

        @Override
        public String toString() {

            String name = (theIndex == null ? "primary" : theIndex.getName());

            if (theForce) {
                return "FORCE_INDEX(" + name + ")";
            }

            return "PREFER_INDEX(" + name + ")";
        }
    }

    private TableImpl theTargetTable;

    private ArrayList theTables = new ArrayList(1);

    private ArrayList theAliases = new ArrayList(1);

    private int theNumAncestors;

    private int theNumDescendants;
    
    private Expr[] theTablePreds;

    private Direction theDirection = Direction.FORWARD;

    private ArrayList thePrimKeys;

    private ArrayList theSecKeys;

    private ArrayList theRanges;

    private ArrayList thePushedExternals;

    private boolean[] theUsesCoveringIndex;

    private List theIndexHints = null;

    private IndexHint theForceIndexHint = null;

    private boolean theEliminateIndexDups;

    private boolean theIsUpdate;

    ExprBaseTable(
        QueryControlBlock qcb,
        StaticContext sctx,
        QueryException.Location location) {

        super(qcb, sctx, ExprKind.BASE_TABLE, location);
    }

    void addTable(
        TableImpl table,
        String alias,
        boolean isAncestor,
        boolean isDescendant,
        QueryException.Location loc) {

        theTables.add(table);
        theAliases.add(alias);

        if (isAncestor) {
            if (!theTargetTable.isAncestor(table)) {
                throw new QueryException(
                    "Table " + table.getFullName() + " is not an ancestor " +
                    "of target table " + theTargetTable.getFullName(), loc);
            }

            ++theNumAncestors;

        } else if (isDescendant) {

            if (!table.isAncestor(theTargetTable)) {
                throw new QueryException(
                    "Table " + table.getFullName() + " is not a descendant " +
                    "of target table " + theTargetTable.getFullName(), loc);
            }

            ++theNumDescendants;

        } else {
            theTargetTable = table;
        }
    }

    void setSortedTables(
        ArrayList sortedTables,
        ArrayList sortedAliases) {

        theTables = sortedTables;
        theAliases = sortedAliases;

        computeType();
    }

    void finalizeTables() {

        int numTables = theTables.size();

        theUsesCoveringIndex = new boolean[numTables];
        theTablePreds = new Expr[numTables];

        for (int i = 0; i < numTables; ++ i) {
            theUsesCoveringIndex[i] = false;
            theTablePreds[i] = null;
        }

        assert(theType == null);
        computeType();
    }

    int getNumTables() {
        return theTables.size();
    }

    int getNumAncestors() {
        return theNumAncestors;
    }

    int getNumDescendants() {
        return theNumDescendants;
    }

    boolean hasNestedTables() {
        return (theNumAncestors > 0 || theNumDescendants > 0);
    }

    boolean isDescendant(int tablePos) {
        return tablePos > theNumAncestors + 1;
    }

    TableImpl getTargetTable() {
        return theTargetTable;
    }

    int getTargetTablePos() {
        return theNumAncestors;
    }

    ArrayList getTables() {
        return theTables;
    }

    TableImpl getTable(int pos) {
        return theTables.get(pos);
    }

    int getTablePos(TableImpl table) {

        for (int i = 0; i < theTables.size(); ++i) {
            if (theTables.get(i).getId() == table.getId()) {
                return i;
            }
        }

        return -1;
    }

    void setTablePred(int tablePos, Expr pred, boolean destroy) {

        if (theTablePreds[tablePos] != null) {
            theTablePreds[tablePos].removeParent(this, destroy);
        }

        pred = ExprPromote.create(null, pred, TypeManager.BOOLEAN_QSTN());
        theTablePreds[tablePos] = pred;
        pred.addParent(this);
    }

    Expr getTablePred(int tablePos) {
        return theTablePreds[tablePos];
    }

    void removeTablePred(int tablePos, boolean destroy) {
        theTablePreds[tablePos].removeParent(this, destroy);
        theTablePreds[tablePos] = null;
    }

    ArrayList getAliases() {
        return theAliases;
    }

    TableImpl getTableForAlias(String alias) {

        for (int i = 0; i < theTables.size(); ++i) {
            if (theAliases.get(i).equals(alias))
                return theTables.get(i);
        }

        throw new QueryStateException(
            "Could not find table for alias " + alias);
    }

    IndexImpl getIndex() {
        if (theSecKeys == null) {
            return null;
        }

        return (IndexImpl)theSecKeys.get(0).getIndex();
    }

    /*
     * The children of an ExprbaseTable are the ON/filtering conditions
     * associated with each table. We assume that the number of children
     * is equal to the number of tables, even if some or all of the table
     * preds may be null. The ExprIterator skips over the null preds.
     */
    @Override
    int getNumChildren() {
        return (theNumAncestors + theNumDescendants + 1);
    }

    Direction getDirection() {
        return theDirection;
    }

    void setDirection(Direction dir) {
        theDirection = dir;
    }

    ArrayList getPrimaryKeys() {
        return thePrimKeys;
    }

    void addPrimaryKeys(
        int tablePos,
        ArrayList keys,
        ArrayList ranges,
        boolean isCoveringIndex) {

        /* Set thePrimKeys and theRanges only if it is the target table */
        if (tablePos == theNumAncestors) {
            thePrimKeys = keys;
            theRanges = ranges;
        }
        theUsesCoveringIndex[tablePos] = isCoveringIndex;
    }

    ArrayList getSecondaryKeys() {
        return theSecKeys;
    }

    void addSecondaryKeys(
        int tablePos,
        ArrayList keys,
        ArrayList ranges,
        boolean isCoveringIndex) {

        /* Set theSecKeys and theRanges only if it is the target table */
        if (tablePos == theNumAncestors) {
            theSecKeys = keys;
            theRanges = ranges;
        }
        theUsesCoveringIndex[tablePos] = isCoveringIndex;
    }

    ArrayList getRanges() {
        return theRanges;
    }

    boolean[] getUsesCoveringIndex() {
        return theUsesCoveringIndex;
    }

    void setPushedExternals(ArrayList v) {
        assert(thePushedExternals == null);
        thePushedExternals = v;
    }

    ArrayList getPushedExternals() {
        return thePushedExternals;
    }

    /**
     * If index is null, we are checking whether the ptimary index
     * has been specified in a hint/
     */
    boolean isIndexHint(IndexImpl index) {

        if (theIndexHints == null) {
            return false;
        }

        for (IndexHint hint : theIndexHints) {
            if (hint.theIndex == index) {
                return true;
            }
        }

        return false;
    }

    IndexHint getForceIndexHint() {
        return theForceIndexHint;
    }

    /**
     * If index is null, it means the hint is about the primary index
     */
    void addIndexHint(IndexImpl index, boolean force, Location loc) {

        if (theIndexHints == null) {
            theIndexHints = new ArrayList();
        }

        IndexHint hint = new IndexHint(index, force);

        if ( !containsHint(theIndexHints, hint) ) {
            theIndexHints.add(hint);
        }

        if (force) {
            if (theForceIndexHint != null) {
                throw new QueryException(
                    "Cannot have more than one FORCE_INDEX hints", loc);
            }

            theForceIndexHint = hint;
        }
    }

    private static boolean containsHint(
        List indexHints,
        IndexHint hint) {
        for (IndexHint h : indexHints) {
            if (h.theIndex == null && hint.theIndex == null ||
                h.theIndex.getName().equals(hint.theIndex.getName())) {
                return true;
            }
        }
        return false;
    }

    void setEliminateIndexDups() {
        theEliminateIndexDups = true;
    }

    boolean getEliminateIndexDups() {
        return theEliminateIndexDups;
    }

    void setIsUpdate() {
        theIsUpdate = true;
    }

    boolean getIsUpdate() {
        return theIsUpdate;
    }

    @Override
    ExprType computeType() {

        if (theType != null) {
            return theType;
        }

        if (theTables.size() == 1) {
            theType = TypeManager.createTableRecordType(theTargetTable,
                                                        Quantifier.STAR);
            return theType;
        }

        FieldMap unionMap = new FieldMap();

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

            TableImpl table = theTables.get(i);
            RecordDefImpl rowDef = table.getRowDef();
            String fname = theAliases.get(i);

            unionMap.put(fname, rowDef, true, /*nullable*/
                         null/*defaultValue*/);
        }

        RecordDefImpl unionDef =
            FieldDefFactory.createRecordDef(unionMap, "fromDef");

        theType = TypeManager.createType(unionDef, Quantifier.STAR);

        return theType;
    }

    @Override
    public boolean mayReturnNULL() {
        return false;
    }

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

        formatter.indent(sb);
        sb.append("TABLE");
        displayContent(sb, formatter);
    }

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

        int numRanges = (theRanges != null ? theRanges.size() : 0);

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

        formatter.incIndent();
        formatter.indent(sb);
        sb.append(theTargetTable.getName());

        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.get(i));
                sb.append("\n");
                formatter.indent(sb);
                sb.append("RANGE: ");
                sb.append(theRanges.get(i));
            }
        }

        if (theSecKeys != null) {

            if (theUsesCoveringIndex[theNumAncestors]) {
                sb.append(" via covering index ");
            } else {
                sb.append(" via index ");
            }
            sb.append(theSecKeys.get(0).getIndex().getName());
            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.get(i));
                sb.append("\n");
                formatter.indent(sb);
                sb.append("RANGE: ");
                sb.append(theRanges.get(i));
            }
        }

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

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

        if (thePushedExternals != null) {
            sb.append("\n");
            formatter.indent(sb);
            sb.append("PUSHED EXTERNAL EXPRS: ");
            for (Expr expr : thePushedExternals) {
                sb.append("\n");
                if (expr == null) {
                    formatter.indent(sb);
                    sb.append("null");
                } else {
                    expr.display(sb, formatter);
                }
            }
        }

        if (theTablePreds != null) {

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

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

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

                sb.append("\n\n");
                formatter.indent(sb);
                sb.append("ON Predicate for table ").
                   append(theTables.get(i).getFullName()).
                   append(":\n");
                theTablePreds[i].display(sb, formatter);
            }
        }

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy