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

oracle.kv.impl.api.ops.TableQueryHandler 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.api.ops;

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

import oracle.kv.FaultException;
import oracle.kv.impl.api.ops.InternalOperation.OpCode;
import oracle.kv.impl.api.table.FieldValueImpl;
import oracle.kv.impl.api.table.TableMetadata;
import oracle.kv.impl.api.table.TableMetadataHelper;
import oracle.kv.impl.api.table.TupleValue;
import oracle.kv.impl.fault.RNUnavailableException;
import oracle.kv.impl.fault.WrappedClientException;
import oracle.kv.impl.metadata.Metadata.MetadataType;
import oracle.kv.impl.query.QueryException;
import oracle.kv.impl.query.QueryRuntimeException;
import oracle.kv.impl.query.QueryStateException;
import oracle.kv.impl.query.runtime.PlanIter;
import oracle.kv.impl.query.runtime.ResumeInfo;
import oracle.kv.impl.query.runtime.RuntimeControlBlock;
import oracle.kv.impl.query.runtime.server.ServerIterFactoryImpl;
import oracle.kv.impl.security.KVStorePrivilege;
import oracle.kv.impl.security.SystemPrivilege;
import oracle.kv.impl.topo.PartitionId;
import oracle.kv.query.ExecuteOptions;

import com.sleepycat.je.Transaction;
import oracle.kv.impl.api.table.TableVersionException;

/**
 * Server handler for {@link TableQuery}.
 */
class TableQueryHandler extends InternalOperationHandler {

    TableQueryHandler(OperationHandler handler, OpCode opCode) {
        super(handler, opCode, TableQuery.class);
    }

    @Override
    List getRequiredPrivileges(TableQuery op) {
        /*
         * Checks the basic privilege for authentication here, and leave the
         * keyspace checking and the table access checking in
         * {@code verifyTableAccess()}.
         */
        return SystemPrivilege.usrviewPrivList;
    }

    @Override
    Result execute(TableQuery op,
                   Transaction txn,
                   PartitionId partitionId) {

        final int batchSize = op.getBatchSize();

        final List results =
            new ArrayList(batchSize);

        TableMetadataHelper mdHelper = getMetadataHelper();

        ExecuteOptions options = new ExecuteOptions();
        options.setResultsBatchSize(batchSize);
        options.setTraceLevel(op.getTraceLevel());
        options.setMathContext(op.getMathContext());
        options.setMaxReadKB(op.getMaxReadKB());

        RuntimeControlBlock rcb = new RuntimeControlBlock(
            null, /* KVStoreImpl not needed and not available */
            getLogger(),
            mdHelper,
            null, /* partitions */
            null, /* shards */
            options,
            op,
            new ServerIterFactoryImpl(txn, partitionId, operationHandler),
            op.getQueryPlan(),
            op.getNumIterators(),
            op.getNumRegisters(),
            op.getExternalVars());

        ResumeInfo ri = op.getResumeInfo();
        ri.setRCB(rcb);

        if (rcb.getTraceLevel() >= 1) {
            rcb.trace("Executing query on partition " + partitionId);
            rcb.trace(op.getQueryPlan().display());
        }

        if (rcb.getTraceLevel() >= 2) {
            rcb.trace("Resume Info:\n" + ri);
        }

        executeQueryPlan(op, rcb, results, partitionId);

        /*
         * Resume key is both input and output parameter for RCB. If set on
         * output there are more keys to be found in this iteration.
         */
        ri.setNumResultsComputed(results.size());

        byte[] newPrimaryResumeKey = ri.getPrimResumeKey();
        byte[] newSecondaryResumeKey = ri.getSecResumeKey();
        boolean more = (rcb.getReachedLimit() ||
                        (results.size() == batchSize &&
                         (newPrimaryResumeKey != null ||
                          newSecondaryResumeKey != null)));

        if (rcb.getTraceLevel() >= 1) {
            rcb.trace("Produced a batch of " + results.size() +
                      " results on partition " + partitionId +
                      " number of KB read = " + rcb.getReadKB() +
                      " more results = " + more);

            if (rcb.getTraceLevel() >= 2) {
                rcb.trace(ri.toString());
            }
        }

        return new Result.QueryResult(getOpCode(),
                                      rcb.getReadKB(),
                                      rcb.getWriteKB(),
                                      results,
                                      op.getResultDef(),
                                      op.mayReturnNULL(),
                                      more,
                                      ri,
                                      rcb.getReachedLimit());
    }

    /**
     * Returns a TableMetadataHelper instance available on this node.
     */
    private TableMetadataHelper getMetadataHelper() {
        final TableMetadata md =
            (TableMetadata) getRepNode().getMetadata(MetadataType.TABLE);
        if (md == null) {
            final String msg = "Query execution unable to get metadata";
            getLogger().warning(msg);

            /*
             * Wrap this exception into one that can be thrown to the client.
             */
            new QueryStateException(msg).throwClientException();
        }
        return md;
    }

    private void executeQueryPlan(
        TableQuery op,
        RuntimeControlBlock rcb,
        List results,
        PartitionId pid) {

        final int batchSize = op.getBatchSize();
        final PlanIter queryPlan = op.getQueryPlan();
        boolean noException = false;

        try {
            queryPlan.open(rcb);

            while ((batchSize == 0 || results.size() < batchSize) &&
                   queryPlan.next(rcb)) {

                FieldValueImpl res = rcb.getRegVal(queryPlan.getResultReg());
                if (res.isTuple()) {
                    res = ((TupleValue)res).toRecord();
                }

                if (rcb.getTraceLevel() >= 2) {
                    rcb.trace("Produced result on " + pid + " :\n" +
                              res);
                }

                results.add(res);
            }

            noException = true;
            
        /*
         * The following multi-catch is so that some expected RuntimeExceptions
         * can be propagated up to allow the request handler to deal with
         * them as it does for other operations. Such exceptions, if not
         * caught here, will be wrapped below in the catch of RuntimeException
         * and will be logged (with stack trace) and re-thrown. As new
         * exceptions of this nature are found, please add to this catch.
         *
         * FaultExceptions are likely ResourceLimitExceptions and should
         * be passed up to the user as-is.
         * 
         * A RNUE is likely due to the RN being in the middle of updating
         * state associated with tables and indexes.
         * 
         * TableVersionException is due to the client and store table
         * metadata being out of sync with each other.
         */
        } catch (FaultException |
                 RNUnavailableException |
                 TableVersionException re) {
            throw re;
        } catch (QueryException qe) {

            /*
             * For debugging and tracing this can temporarily use level INFO
             */
            getLogger().fine("Query execution failed: " + qe);

            /*
             * Turn this into a wrapped IllegalArgumentException so that it can
             * be passed to the client.
             */
            throw qe.getWrappedIllegalArgument();
        } catch (QueryStateException qse) {

            /*
             * This exception indicates a bug or problem in the engine. Log
             * it. It will be thrown through to the client side.
             */
            getLogger().warning(qse.toString());

            /*
             * Wrap this exception into one that can be thrown to the client.
             */
            qse.throwClientException();

        } catch (IllegalArgumentException e) {
            throw new WrappedClientException(e);
        } catch (RuntimeException re) {
            
            /*
             * RuntimeException should not be caught here. REs should be
             * propagated up to the request handler as many are explicitly
             * handled there. The issue is that the query code can throw REs
             * which it should catch and turn into something specific (such as
             * an IAE). Until that time this needs to remain to avoid the
             * RN restarting due to some minor query issue.
             */
            throw new QueryRuntimeException(re);
        } finally {
            try {
                queryPlan.close(rcb);
            } catch (RuntimeException re) {
                if (noException) {
                    throw new QueryRuntimeException(re);
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy