Search in sources :

Example 1 with QueryType

use of org.voltdb.types.QueryType in project voltdb by VoltDB.

the class PlannerTestAideDeCamp method compile.

/**
     * Compile and cache the statement and plan and return the final plan graph.
     */
private List<AbstractPlanNode> compile(String sql, int paramCount, String joinOrder, boolean inferPartitioning, boolean forceSingle, DeterminismMode detMode) {
    String stmtLabel = "stmt-" + String.valueOf(compileCounter++);
    Statement catalogStmt = proc.getStatements().add(stmtLabel);
    catalogStmt.setSqltext(sql);
    catalogStmt.setSinglepartition(forceSingle);
    // determine the type of the query
    QueryType qtype = QueryType.SELECT;
    catalogStmt.setReadonly(true);
    if (sql.toLowerCase().startsWith("insert")) {
        qtype = QueryType.INSERT;
        catalogStmt.setReadonly(false);
    }
    if (sql.toLowerCase().startsWith("update")) {
        qtype = QueryType.UPDATE;
        catalogStmt.setReadonly(false);
    }
    if (sql.toLowerCase().startsWith("delete")) {
        qtype = QueryType.DELETE;
        catalogStmt.setReadonly(false);
    }
    catalogStmt.setQuerytype(qtype.getValue());
    // name will look like "basename-stmt-#"
    String name = catalogStmt.getParent().getTypeName() + "-" + catalogStmt.getTypeName();
    DatabaseEstimates estimates = new DatabaseEstimates();
    TrivialCostModel costModel = new TrivialCostModel();
    StatementPartitioning partitioning;
    if (inferPartitioning) {
        partitioning = StatementPartitioning.inferPartitioning();
    } else if (forceSingle) {
        partitioning = StatementPartitioning.forceSP();
    } else {
        partitioning = StatementPartitioning.forceMP();
    }
    String procName = catalogStmt.getParent().getTypeName();
    QueryPlanner planner = new QueryPlanner(sql, stmtLabel, procName, db, partitioning, hsql, estimates, false, StatementCompiler.DEFAULT_MAX_JOIN_TABLES, costModel, null, joinOrder, detMode);
    CompiledPlan plan = null;
    planner.parse();
    plan = planner.plan();
    assert (plan != null);
    // Partitioning optionally inferred from the planning process.
    if (partitioning.isInferred()) {
        catalogStmt.setSinglepartition(partitioning.isInferredSingle());
    }
    // We will need to update the system catalogs with this new information
    for (int i = 0; i < plan.parameters.length; ++i) {
        StmtParameter catalogParam = catalogStmt.getParameters().add(String.valueOf(i));
        ParameterValueExpression pve = plan.parameters[i];
        catalogParam.setJavatype(pve.getValueType().getValue());
        catalogParam.setIsarray(pve.getParamIsVector());
        catalogParam.setIndex(i);
    }
    List<PlanNodeList> nodeLists = new ArrayList<>();
    nodeLists.add(new PlanNodeList(plan.rootPlanGraph));
    if (plan.subPlanGraph != null) {
        nodeLists.add(new PlanNodeList(plan.subPlanGraph));
    }
    // Now update our catalog information
    // HACK: We're using the node_tree's hashCode() as it's name. It would be really
    //     nice if the Catalog code give us an guid without needing a name first...
    String json = null;
    try {
        JSONObject jobj = new JSONObject(nodeLists.get(0).toJSONString());
        json = jobj.toString(4);
    } catch (JSONException e2) {
        // TODO Auto-generated catch block
        e2.printStackTrace();
        System.exit(-1);
        return null;
    }
    //
    try {
        BuildDirectoryUtils.writeFile("statement-plans", name + "_json.txt", json, true);
        BuildDirectoryUtils.writeFile("statement-plans", name + ".dot", nodeLists.get(0).toDOTString("name"), true);
    } catch (Exception e) {
        e.printStackTrace();
    }
    List<AbstractPlanNode> plannodes = new ArrayList<>();
    for (PlanNodeList nodeList : nodeLists) {
        plannodes.add(nodeList.getRootPlanNode());
    }
    m_currentPlan = plan;
    return plannodes;
}
Also used : AbstractPlanNode(org.voltdb.plannodes.AbstractPlanNode) Statement(org.voltdb.catalog.Statement) ArrayList(java.util.ArrayList) JSONException(org.json_voltpatches.JSONException) PlanNodeList(org.voltdb.plannodes.PlanNodeList) JSONException(org.json_voltpatches.JSONException) StmtParameter(org.voltdb.catalog.StmtParameter) JSONObject(org.json_voltpatches.JSONObject) ParameterValueExpression(org.voltdb.expressions.ParameterValueExpression) QueryType(org.voltdb.types.QueryType) DatabaseEstimates(org.voltdb.compiler.DatabaseEstimates)

Example 2 with QueryType

use of org.voltdb.types.QueryType in project voltdb by VoltDB.

the class ProcedureCompiler method compileJavaProcedure.

static void compileJavaProcedure(VoltCompiler compiler, HSQLInterface hsql, DatabaseEstimates estimates, Database db, ProcedureDescriptor procedureDescriptor, InMemoryJarfile jarOutput) throws VoltCompiler.VoltCompilerException {
    final String className = procedureDescriptor.m_className;
    // Load the class given the class name
    Class<?> procClass = procedureDescriptor.m_class;
    // get the short name of the class (no package)
    String shortName = deriveShortProcedureName(className);
    // add an entry to the catalog
    final Procedure procedure = db.getProcedures().add(shortName);
    for (String groupName : procedureDescriptor.m_authGroups) {
        final Group group = db.getGroups().get(groupName);
        if (group == null) {
            throw compiler.new VoltCompilerException("Procedure " + className + " allows access by a role " + groupName + " that does not exist");
        }
        final GroupRef groupRef = procedure.getAuthgroups().add(groupName);
        groupRef.setGroup(group);
    }
    procedure.setClassname(className);
    // sysprocs don't use the procedure compiler
    procedure.setSystemproc(false);
    procedure.setDefaultproc(procedureDescriptor.m_builtInStmt);
    procedure.setHasjava(true);
    ProcedureAnnotation pa = (ProcedureAnnotation) procedure.getAnnotation();
    if (pa == null) {
        pa = new ProcedureAnnotation();
        procedure.setAnnotation(pa);
    }
    // get the annotation
    // first try to get one that has been passed from the compiler
    ProcInfoData info = compiler.getProcInfoOverride(shortName);
    // check if partition info was set in ddl
    ProcInfoData ddlInfo = null;
    if (procedureDescriptor.m_partitionString != null && !procedureDescriptor.m_partitionString.trim().isEmpty()) {
        ddlInfo = new ProcInfoData();
        ddlInfo.partitionInfo = procedureDescriptor.m_partitionString;
        ddlInfo.singlePartition = true;
    }
    // and create a ProcInfo.Data instance for it
    if (info == null) {
        info = new ProcInfoData();
        ProcInfo annotationInfo = procClass.getAnnotation(ProcInfo.class);
        // error out if partition info is present in both ddl and annotation
        if (annotationInfo != null) {
            if (ddlInfo != null) {
                String msg = "Procedure: " + shortName + " has partition properties defined both in ";
                msg += "class \"" + className + "\" and in the schema definition file(s)";
                throw compiler.new VoltCompilerException(msg);
            }
            // Prevent AutoGenerated DDL from including PARTITION PROCEDURE for this procedure.
            pa.classAnnotated = true;
            info.partitionInfo = annotationInfo.partitionInfo();
            info.singlePartition = annotationInfo.singlePartition();
        } else if (ddlInfo != null) {
            info = ddlInfo;
        }
    } else {
        pa.classAnnotated = true;
    }
    assert (info != null);
    // make sure multi-partition implies no partitioning info
    if (info.singlePartition == false) {
        if ((info.partitionInfo != null) && (info.partitionInfo.length() > 0)) {
            String msg = "Procedure: " + shortName + " is annotated as multi-partition";
            msg += " but partitionInfo has non-empty value: \"" + info.partitionInfo + "\"";
            throw compiler.new VoltCompilerException(msg);
        }
    }
    // if the procedure is non-transactional, then take this special path here
    if (VoltNonTransactionalProcedure.class.isAssignableFrom(procClass)) {
        compileNTProcedure(compiler, procClass, procedure, jarOutput);
        return;
    }
    // if still here, that means the procedure is transactional
    procedure.setTransactional(true);
    // track if there are any writer statements and/or sequential scans and/or an overlooked common partitioning parameter
    boolean procHasWriteStmts = false;
    boolean procHasSeqScans = false;
    // procWantsCommonPartitioning == true but commonPartitionExpression == null signifies a proc
    // for which the planner was requested to attempt to find an SP plan, but that was not possible
    // -- it had a replicated write or it had one or more partitioned reads that were not all
    // filtered by the same partition key value -- so it was planned as an MP proc.
    boolean procWantsCommonPartitioning = true;
    AbstractExpression commonPartitionExpression = null;
    String exampleSPstatement = null;
    Object exampleSPvalue = null;
    // iterate through the fields and get valid sql statements
    VoltProcedure procInstance;
    try {
        procInstance = (VoltProcedure) procClass.newInstance();
    } catch (InstantiationException e) {
        throw new RuntimeException("Error instantiating procedure \"%s\"" + procClass.getName(), e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException("Error instantiating procedure \"%s\"" + procClass.getName(), e);
    }
    Map<String, SQLStmt> stmtMap = getValidSQLStmts(compiler, procClass.getSimpleName(), procClass, procInstance, true);
    ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
    builder.putAll(stmtMap);
    // find the run() method and get the params
    Method procMethod = null;
    Method[] methods = procClass.getDeclaredMethods();
    for (final Method m : methods) {
        String name = m.getName();
        if (name.equals("run")) {
            assert (m.getDeclaringClass() == procClass);
            // if not null, then we've got more than one run method
            if (procMethod != null) {
                String msg = "Procedure: " + shortName + " has multiple public run(...) methods. ";
                msg += "Only a single run(...) method is supported.";
                throw compiler.new VoltCompilerException(msg);
            }
            if (Modifier.isPublic(m.getModifiers())) {
                // found it!
                procMethod = m;
            } else {
                compiler.addWarn("Procedure: " + shortName + " has non-public run(...) method.");
            }
        }
    }
    if (procMethod == null) {
        String msg = "Procedure: " + shortName + " has no run(...) method.";
        throw compiler.new VoltCompilerException(msg);
    }
    // check the return type of the run method
    if ((procMethod.getReturnType() != VoltTable[].class) && (procMethod.getReturnType() != VoltTable.class) && (procMethod.getReturnType() != long.class) && (procMethod.getReturnType() != Long.class)) {
        String msg = "Procedure: " + shortName + " has run(...) method that doesn't return long, Long, VoltTable or VoltTable[].";
        throw compiler.new VoltCompilerException(msg);
    }
    builder.put("@run", procMethod);
    Map<String, Object> fields = builder.build();
    // determine if proc is read or read-write by checking if the proc contains any write sql stmts
    boolean readWrite = false;
    for (Object field : fields.values()) {
        if (!(field instanceof SQLStmt))
            continue;
        SQLStmt stmt = (SQLStmt) field;
        QueryType qtype = QueryType.getFromSQL(stmt.getText());
        if (!qtype.isReadOnly()) {
            readWrite = true;
            break;
        }
    }
    // default to FASTER determinism mode, which may favor non-deterministic plans
    // but if it's a read-write proc, use a SAFER planning mode wrt determinism.
    final DeterminismMode detMode = readWrite ? DeterminismMode.SAFER : DeterminismMode.FASTER;
    for (Entry<String, Object> entry : fields.entrySet()) {
        if (!(entry.getValue() instanceof SQLStmt))
            continue;
        String stmtName = entry.getKey();
        SQLStmt stmt = (SQLStmt) entry.getValue();
        // add the statement to the catalog
        Statement catalogStmt = procedure.getStatements().add(stmtName);
        // compile the statement
        StatementPartitioning partitioning = info.singlePartition ? StatementPartitioning.forceSP() : StatementPartitioning.forceMP();
        boolean cacheHit = StatementCompiler.compileFromSqlTextAndUpdateCatalog(compiler, hsql, db, estimates, catalogStmt, stmt.getText(), stmt.getJoinOrder(), detMode, partitioning);
        // if this was a cache hit or specified single, don't worry about figuring out more partitioning
        if (partitioning.wasSpecifiedAsSingle() || cacheHit) {
            // Don't try to infer what's already been asserted.
            procWantsCommonPartitioning = false;
        // The planner does not currently attempt to second-guess a plan declared as single-partition, maybe some day.
        // In theory, the PartitioningForStatement would confirm the use of (only) a parameter as a partition key --
        // or if the partition key was determined to be some other constant (expression?) it might display an informational
        // message that the passed parameter is assumed to be equal to the hard-coded partition key constant (expression).
        // Validate any inferred statement partitioning given the statement's possible usage, until a contradiction is found.
        } else if (procWantsCommonPartitioning) {
            // conflict with the partitioning of prior statements.
            if (partitioning.getCountOfIndependentlyPartitionedTables() == 1) {
                AbstractExpression statementPartitionExpression = partitioning.singlePartitioningExpressionForReport();
                if (statementPartitionExpression != null) {
                    if (commonPartitionExpression == null) {
                        commonPartitionExpression = statementPartitionExpression;
                        exampleSPstatement = stmt.getText();
                        exampleSPvalue = partitioning.getInferredPartitioningValue();
                    } else if (commonPartitionExpression.equals(statementPartitionExpression) || (statementPartitionExpression instanceof ParameterValueExpression && commonPartitionExpression instanceof ParameterValueExpression)) {
                    // Any constant used for partitioning would have to be the same for all statements, but
                    // any statement parameter used for partitioning MIGHT come from the same proc parameter as
                    // any other statement's parameter used for partitioning.
                    } else {
                        // appears to be different partitioning for different statements
                        procWantsCommonPartitioning = false;
                    }
                } else {
                    // There is a statement with a partitioned table whose partitioning column is
                    // not equality filtered with a constant or param. Abandon all hope.
                    procWantsCommonPartitioning = false;
                }
            // Usually, replicated-only statements in a mix with others have no effect on the MP/SP decision
            } else if (partitioning.getCountOfPartitionedTables() == 0) {
                // but SP is strictly forbidden for DML, to maintain the consistency of the replicated data.
                if (partitioning.getIsReplicatedTableDML()) {
                    procWantsCommonPartitioning = false;
                }
            } else {
                // There is a statement with a partitioned table whose partitioning column is
                // not equality filtered with a constant or param. Abandon all hope.
                procWantsCommonPartitioning = false;
            }
        }
        // if a single stmt is not read only, then the proc is not read only
        if (catalogStmt.getReadonly() == false) {
            procHasWriteStmts = true;
        }
        if (catalogStmt.getSeqscancount() > 0) {
            procHasSeqScans = true;
        }
    }
    // MIGHT the planner have uncovered an overlooked opportunity to run all statements SP?
    if (procWantsCommonPartitioning && (commonPartitionExpression != null)) {
        String msg = null;
        if (commonPartitionExpression instanceof ParameterValueExpression) {
            msg = "This procedure might benefit from an @ProcInfo annotation designating parameter " + ((ParameterValueExpression) commonPartitionExpression).getParameterIndex() + " of statement '" + exampleSPstatement + "'";
        } else {
            String valueDescription = null;
            if (exampleSPvalue == null) {
                // Statements partitioned on a runtime constant. This is likely to be cryptic, but hopefully gets the idea across.
                valueDescription = "of " + commonPartitionExpression.explain("");
            } else {
                // A simple constant value COULD have been a parameter.
                valueDescription = exampleSPvalue.toString();
            }
            msg = "This procedure might benefit from an @ProcInfo annotation referencing an added parameter passed the value " + valueDescription;
        }
        compiler.addInfo(msg);
    }
    // set the read onlyness of a proc
    procedure.setReadonly(procHasWriteStmts == false);
    procedure.setHasseqscans(procHasSeqScans);
    checkForDeterminismWarnings(compiler, shortName, procedure, procHasWriteStmts);
    // set procedure parameter types
    CatalogMap<ProcParameter> params = procedure.getParameters();
    Class<?>[] paramTypes = procMethod.getParameterTypes();
    for (int i = 0; i < paramTypes.length; i++) {
        Class<?> cls = paramTypes[i];
        ProcParameter param = params.add(String.valueOf(i));
        param.setIndex(i);
        // handle the case where the param is an array
        if (cls.isArray()) {
            param.setIsarray(true);
            cls = cls.getComponentType();
        } else
            param.setIsarray(false);
        // boxed types are not supported parameters at this time
        if ((cls == Long.class) || (cls == Integer.class) || (cls == Short.class) || (cls == Byte.class) || (cls == Double.class) || (cls == Character.class) || (cls == Boolean.class)) {
            String msg = "Procedure: " + shortName + " has a parameter with a boxed type: ";
            msg += cls.getSimpleName();
            msg += ". Replace this parameter with the corresponding primitive type and the procedure may compile.";
            throw compiler.new VoltCompilerException(msg);
        } else if ((cls == Float.class) || (cls == float.class)) {
            String msg = "Procedure: " + shortName + " has a parameter with type: ";
            msg += cls.getSimpleName();
            msg += ". Replace this parameter type with double and the procedure may compile.";
            throw compiler.new VoltCompilerException(msg);
        }
        VoltType type;
        try {
            type = VoltType.typeFromClass(cls);
        } catch (VoltTypeException e) {
            // handle the case where the type is invalid
            String msg = "Procedure: " + shortName + " has a parameter with invalid type: ";
            msg += cls.getSimpleName();
            throw compiler.new VoltCompilerException(msg);
        } catch (RuntimeException e) {
            String msg = "Procedure: " + shortName + " unexpectedly failed a check on a parameter of type: ";
            msg += cls.getSimpleName();
            msg += " with error: ";
            msg += e.toString();
            throw compiler.new VoltCompilerException(msg);
        }
        param.setType(type.getValue());
    }
    // parse the procinfo
    procedure.setSinglepartition(info.singlePartition);
    if (info.singlePartition) {
        parsePartitionInfo(compiler, db, procedure, info.partitionInfo);
        if (procedure.getPartitionparameter() >= paramTypes.length) {
            String msg = "PartitionInfo parameter not a valid parameter for procedure: " + procedure.getClassname();
            throw compiler.new VoltCompilerException(msg);
        }
        // check the type of partition parameter meets our high standards
        Class<?> partitionType = paramTypes[procedure.getPartitionparameter()];
        Class<?>[] validPartitionClzzes = { Long.class, Integer.class, Short.class, Byte.class, long.class, int.class, short.class, byte.class, String.class, byte[].class };
        boolean found = false;
        for (Class<?> candidate : validPartitionClzzes) {
            if (partitionType == candidate)
                found = true;
        }
        if (!found) {
            String msg = "PartitionInfo parameter must be a String or Number for procedure: " + procedure.getClassname();
            throw compiler.new VoltCompilerException(msg);
        }
        VoltType columnType = VoltType.get((byte) procedure.getPartitioncolumn().getType());
        VoltType paramType = VoltType.typeFromClass(partitionType);
        if (!columnType.canExactlyRepresentAnyValueOf(paramType)) {
            String msg = "Type mismatch between partition column and partition parameter for procedure " + procedure.getClassname() + " may cause overflow or loss of precision.\nPartition column is type " + columnType + " and partition parameter is type " + paramType;
            throw compiler.new VoltCompilerException(msg);
        } else if (!paramType.canExactlyRepresentAnyValueOf(columnType)) {
            String msg = "Type mismatch between partition column and partition parameter for procedure " + procedure.getClassname() + " does not allow the full range of partition key values.\nPartition column is type " + columnType + " and partition parameter is type " + paramType;
            compiler.addWarn(msg);
        }
    }
    // put the compiled code for this procedure into the jarfile
    // need to find the outermost ancestor class for the procedure in the event
    // that it's actually an inner (or inner inner...) class.
    // addClassToJar recursively adds all the children, which should include this
    // class
    Class<?> ancestor = procClass;
    while (ancestor.getEnclosingClass() != null) {
        ancestor = ancestor.getEnclosingClass();
    }
    compiler.addClassToJar(jarOutput, ancestor);
}
Also used : Group(org.voltdb.catalog.Group) VoltProcedure(org.voltdb.VoltProcedure) VoltTypeException(org.voltdb.VoltTypeException) ProcedureAnnotation(org.voltdb.compilereport.ProcedureAnnotation) ProcInfoData(org.voltdb.ProcInfoData) StatementPartitioning(org.voltdb.planner.StatementPartitioning) VoltProcedure(org.voltdb.VoltProcedure) VoltNonTransactionalProcedure(org.voltdb.VoltNonTransactionalProcedure) Procedure(org.voltdb.catalog.Procedure) VoltCompilerException(org.voltdb.compiler.VoltCompiler.VoltCompilerException) ParameterValueExpression(org.voltdb.expressions.ParameterValueExpression) ProcParameter(org.voltdb.catalog.ProcParameter) ProcInfo(org.voltdb.ProcInfo) SQLStmt(org.voltdb.SQLStmt) Statement(org.voltdb.catalog.Statement) Method(java.lang.reflect.Method) VoltTable(org.voltdb.VoltTable) ImmutableMap(com.google_voltpatches.common.collect.ImmutableMap) AbstractExpression(org.voltdb.expressions.AbstractExpression) VoltType(org.voltdb.VoltType) GroupRef(org.voltdb.catalog.GroupRef) QueryType(org.voltdb.types.QueryType)

Example 3 with QueryType

use of org.voltdb.types.QueryType in project voltdb by VoltDB.

the class StatementCompiler method compileStatementAndUpdateCatalog.

/**
     * This static method conveniently does a few things for its caller:
     * - Formats the statement by replacing newlines with spaces
     *     and appends a semicolon if needed
     * - Updates the catalog Statement with metadata about the statement
     * - Plans the statement and puts the serialized plan in the catalog Statement
     * - Updates the catalog Statment with info about the statement's parameters
     * Upon successful completion, catalog statement will have been updated with
     * plan fragments needed to execute the statement.
     *
     * @param  compiler     The VoltCompiler instance
     * @param  hsql         Pass through parameter to QueryPlanner
     * @param  catalog      Pass through parameter to QueryPlanner
     * @param  db           Pass through parameter to QueryPlanner
     * @param  estimates    Pass through parameter to QueryPlanner
     * @param  catalogStmt  Catalog statement to be updated with plan
     * @param  xml          XML for statement, if it has been previously parsed
     *                      (may be null)
     * @param  stmt         Text of statement to be compiled
     * @param  joinOrder    Pass through parameter to QueryPlanner
     * @param  detMode      Pass through parameter to QueryPlanner
     * @param  partitioning Partition info for statement
    */
static boolean compileStatementAndUpdateCatalog(VoltCompiler compiler, HSQLInterface hsql, Database db, DatabaseEstimates estimates, Statement catalogStmt, VoltXMLElement xml, String stmt, String joinOrder, DeterminismMode detMode, StatementPartitioning partitioning) throws VoltCompiler.VoltCompilerException {
    // Cleanup whitespace newlines for catalog compatibility
    // and to make statement parsing easier.
    stmt = stmt.replaceAll("\n", " ");
    stmt = stmt.trim();
    compiler.addInfo("Compiling Statement: " + stmt);
    // put the data in the catalog that we have
    if (!stmt.endsWith(";")) {
        stmt += ";";
    }
    // if this key + sql is the same, then a cached stmt can be used
    String keyPrefix = compiler.getKeyPrefix(partitioning, detMode, joinOrder);
    // if the key is cache-able, look for a previous statement
    if (keyPrefix != null) {
        Statement previousStatement = compiler.getCachedStatement(keyPrefix, stmt);
        // check if the stmt exists and if it's the same sql text
        if (previousStatement != null) {
            catalogStmt.setAnnotation(previousStatement.getAnnotation());
            catalogStmt.setAttachment(previousStatement.getAttachment());
            catalogStmt.setCachekeyprefix(previousStatement.getCachekeyprefix());
            catalogStmt.setCost(previousStatement.getCost());
            catalogStmt.setExplainplan(previousStatement.getExplainplan());
            catalogStmt.setIscontentdeterministic(previousStatement.getIscontentdeterministic());
            catalogStmt.setIsorderdeterministic(previousStatement.getIsorderdeterministic());
            catalogStmt.setNondeterminismdetail(previousStatement.getNondeterminismdetail());
            catalogStmt.setQuerytype(previousStatement.getQuerytype());
            catalogStmt.setReadonly(previousStatement.getReadonly());
            catalogStmt.setReplicatedtabledml(previousStatement.getReplicatedtabledml());
            catalogStmt.setSeqscancount(previousStatement.getSeqscancount());
            catalogStmt.setSinglepartition(previousStatement.getSinglepartition());
            catalogStmt.setSqltext(previousStatement.getSqltext());
            catalogStmt.setTablesread(previousStatement.getTablesread());
            catalogStmt.setTablesupdated(previousStatement.getTablesupdated());
            catalogStmt.setIndexesused(previousStatement.getIndexesused());
            for (StmtParameter oldSp : previousStatement.getParameters()) {
                StmtParameter newSp = catalogStmt.getParameters().add(oldSp.getTypeName());
                newSp.setAnnotation(oldSp.getAnnotation());
                newSp.setAttachment(oldSp.getAttachment());
                newSp.setIndex(oldSp.getIndex());
                newSp.setIsarray(oldSp.getIsarray());
                newSp.setJavatype(oldSp.getJavatype());
                newSp.setSqltype(oldSp.getSqltype());
            }
            for (PlanFragment oldFrag : previousStatement.getFragments()) {
                PlanFragment newFrag = catalogStmt.getFragments().add(oldFrag.getTypeName());
                newFrag.setAnnotation(oldFrag.getAnnotation());
                newFrag.setAttachment(oldFrag.getAttachment());
                newFrag.setHasdependencies(oldFrag.getHasdependencies());
                newFrag.setMultipartition(oldFrag.getMultipartition());
                newFrag.setNontransactional(oldFrag.getNontransactional());
                newFrag.setPlanhash(oldFrag.getPlanhash());
                newFrag.setPlannodetree(oldFrag.getPlannodetree());
            }
            return true;
        }
    }
    // determine the type of the query
    QueryType qtype = QueryType.getFromSQL(stmt);
    catalogStmt.setReadonly(qtype.isReadOnly());
    catalogStmt.setQuerytype(qtype.getValue());
    // might be null if not cacheable
    catalogStmt.setCachekeyprefix(keyPrefix);
    catalogStmt.setSqltext(stmt);
    catalogStmt.setSinglepartition(partitioning.wasSpecifiedAsSingle());
    String name = catalogStmt.getParent().getTypeName() + "-" + catalogStmt.getTypeName();
    String sql = catalogStmt.getSqltext();
    String stmtName = catalogStmt.getTypeName();
    String procName = catalogStmt.getParent().getTypeName();
    TrivialCostModel costModel = new TrivialCostModel();
    CompiledPlan plan = null;
    QueryPlanner planner = new QueryPlanner(sql, stmtName, procName, db, partitioning, hsql, estimates, false, DEFAULT_MAX_JOIN_TABLES, costModel, null, joinOrder, detMode);
    try {
        try {
            if (xml != null) {
                planner.parseFromXml(xml);
            } else {
                planner.parse();
            }
            plan = planner.plan();
            assert (plan != null);
        } catch (PlanningErrorException e) {
            // These are normal expectable errors -- don't normally need a stack-trace.
            String msg = "Failed to plan for statement (" + catalogStmt.getTypeName() + ") \"" + catalogStmt.getSqltext() + "\".";
            if (e.getMessage() != null) {
                msg += " Error: \"" + e.getMessage() + "\"";
            }
            throw compiler.new VoltCompilerException(msg);
        } catch (Exception e) {
            e.printStackTrace();
            throw compiler.new VoltCompilerException("Failed to plan for stmt: " + catalogStmt.getTypeName());
        }
        // There is a hard-coded limit to the number of parameters that can be passed to the EE.
        if (plan.parameters.length > CompiledPlan.MAX_PARAM_COUNT) {
            throw compiler.new VoltCompilerException("The statement's parameter count " + plan.parameters.length + " must not exceed the maximum " + CompiledPlan.MAX_PARAM_COUNT);
        }
        // Check order and content determinism before accessing the detail which
        // it caches.
        boolean orderDeterministic = plan.isOrderDeterministic();
        catalogStmt.setIsorderdeterministic(orderDeterministic);
        boolean contentDeterministic = plan.isContentDeterministic() && (orderDeterministic || !plan.hasLimitOrOffset());
        catalogStmt.setIscontentdeterministic(contentDeterministic);
        String nondeterminismDetail = plan.nondeterminismDetail();
        catalogStmt.setNondeterminismdetail(nondeterminismDetail);
        catalogStmt.setSeqscancount(plan.countSeqScans());
        // We will need to update the system catalogs with this new information
        for (int i = 0; i < plan.parameters.length; ++i) {
            StmtParameter catalogParam = catalogStmt.getParameters().add(String.valueOf(i));
            catalogParam.setJavatype(plan.parameters[i].getValueType().getValue());
            catalogParam.setIsarray(plan.parameters[i].getParamIsVector());
            catalogParam.setIndex(i);
        }
        catalogStmt.setReplicatedtabledml(plan.replicatedTableDML);
        // output the explained plan to disk (or caller) for debugging
        // Initial capacity estimate.
        StringBuilder planDescription = new StringBuilder(1000);
        planDescription.append("SQL: ").append(plan.sql);
        // Cost seems to only confuse people who don't understand how this number is used/generated.
        if (VoltCompiler.DEBUG_MODE) {
            planDescription.append("\nCOST: ").append(plan.cost);
        }
        planDescription.append("\nPLAN:\n");
        planDescription.append(plan.explainedPlan);
        String planString = planDescription.toString();
        // only write to disk if compiler is in standalone mode
        if (compiler.standaloneCompiler) {
            BuildDirectoryUtils.writeFile(null, name + ".txt", planString, false);
        }
        compiler.captureDiagnosticContext(planString);
        // build usage links for report generation and put them in the catalog
        CatalogUtil.updateUsageAnnotations(db, catalogStmt, plan.rootPlanGraph, plan.subPlanGraph);
        // set the explain plan output into the catalog (in hex) for reporting
        catalogStmt.setExplainplan(Encoder.hexEncode(plan.explainedPlan));
        // compute a hash of the plan
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            assert (false);
            // should never happen with healthy jvm
            System.exit(-1);
        }
        // Now update our catalog information
        PlanFragment planFragment = catalogStmt.getFragments().add("0");
        planFragment.setHasdependencies(plan.subPlanGraph != null);
        // mark a fragment as non-transactional if it never touches a persistent table
        planFragment.setNontransactional(!fragmentReferencesPersistentTable(plan.rootPlanGraph));
        planFragment.setMultipartition(plan.subPlanGraph != null);
        byte[] planBytes = writePlanBytes(compiler, planFragment, plan.rootPlanGraph);
        md.update(planBytes, 0, planBytes.length);
        // compute the 40 bytes of hex from the 20 byte sha1 hash of the plans
        md.reset();
        md.update(planBytes);
        planFragment.setPlanhash(Encoder.hexEncode(md.digest()));
        if (plan.subPlanGraph != null) {
            planFragment = catalogStmt.getFragments().add("1");
            planFragment.setHasdependencies(false);
            planFragment.setNontransactional(false);
            planFragment.setMultipartition(true);
            byte[] subBytes = writePlanBytes(compiler, planFragment, plan.subPlanGraph);
            // compute the 40 bytes of hex from the 20 byte sha1 hash of the plans
            md.reset();
            md.update(subBytes);
            planFragment.setPlanhash(Encoder.hexEncode(md.digest()));
        }
        // Planner should have rejected with an exception any statement with an unrecognized type.
        int validType = catalogStmt.getQuerytype();
        assert (validType != QueryType.INVALID.getValue());
        return false;
    } catch (StackOverflowError error) {
        String msg = "Failed to plan for statement (" + catalogStmt.getTypeName() + ") \"" + catalogStmt.getSqltext() + "\". Error: \"Encountered stack overflow error. " + "Try reducing the number of predicate expressions in the query.\"";
        throw compiler.new VoltCompilerException(msg);
    }
}
Also used : CompiledPlan(org.voltdb.planner.CompiledPlan) PlanningErrorException(org.voltdb.planner.PlanningErrorException) Statement(org.voltdb.catalog.Statement) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) PlanFragment(org.voltdb.catalog.PlanFragment) QueryPlanner(org.voltdb.planner.QueryPlanner) VoltCompilerException(org.voltdb.compiler.VoltCompiler.VoltCompilerException) PlanningErrorException(org.voltdb.planner.PlanningErrorException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) StmtParameter(org.voltdb.catalog.StmtParameter) TrivialCostModel(org.voltdb.planner.TrivialCostModel) VoltCompilerException(org.voltdb.compiler.VoltCompiler.VoltCompilerException) MessageDigest(java.security.MessageDigest) QueryType(org.voltdb.types.QueryType)

Example 4 with QueryType

use of org.voltdb.types.QueryType in project voltdb by VoltDB.

the class StatementCompiler method compileDefaultProcedure.

/**
     * This procedure compiles a shim org.voltdb.catalog.Procedure representing a default proc.
     * The shim has no plan and few details that are expensive to compute.
     * The returned proc instance has a full plan and can be used to create a ProcedureRunner.
     * Note that while there are two procedure objects here, none are rooted in a real catalog;
     * they are entirely parallel to regular, catalog procs.
     *
     * This code could probably go a few different places. It duplicates a bit too much of the
     * StatmentCompiler code for my taste, so I put it here. Next pass could reduce some of the
     * duplication?
     */
public static Procedure compileDefaultProcedure(PlannerTool plannerTool, Procedure catProc, String sqlText) {
    // fake db makes it easy to create procedures that aren't part of the main catalog
    Database fakeDb = new Catalog().getClusters().add("cluster").getDatabases().add("database");
    Table table = catProc.getPartitiontable();
    // determine the type of the query
    QueryType qtype = QueryType.getFromSQL(sqlText);
    StatementPartitioning partitioning = catProc.getSinglepartition() ? StatementPartitioning.forceSP() : StatementPartitioning.forceMP();
    CompiledPlan plan = plannerTool.planSqlCore(sqlText, partitioning);
    Procedure newCatProc = fakeDb.getProcedures().add(catProc.getTypeName());
    newCatProc.setClassname(catProc.getClassname());
    newCatProc.setDefaultproc(true);
    newCatProc.setEverysite(false);
    newCatProc.setHasjava(false);
    newCatProc.setPartitioncolumn(catProc.getPartitioncolumn());
    newCatProc.setPartitionparameter(catProc.getPartitionparameter());
    newCatProc.setPartitiontable(catProc.getPartitiontable());
    newCatProc.setReadonly(catProc.getReadonly());
    newCatProc.setSinglepartition(catProc.getSinglepartition());
    newCatProc.setSystemproc(false);
    if (catProc.getPartitionparameter() >= 0) {
        newCatProc.setAttachment(new ProcedurePartitionInfo(VoltType.get((byte) catProc.getPartitioncolumn().getType()), catProc.getPartitionparameter()));
    }
    CatalogMap<Statement> statements = newCatProc.getStatements();
    assert (statements != null);
    Statement stmt = statements.add(VoltDB.ANON_STMT_NAME);
    stmt.setSqltext(sqlText);
    stmt.setReadonly(catProc.getReadonly());
    stmt.setQuerytype(qtype.getValue());
    stmt.setSinglepartition(catProc.getSinglepartition());
    stmt.setIscontentdeterministic(true);
    stmt.setIsorderdeterministic(true);
    stmt.setNondeterminismdetail("NO CONTENT FOR DEFAULT PROCS");
    stmt.setSeqscancount(plan.countSeqScans());
    stmt.setReplicatedtabledml(!catProc.getReadonly() && table.getIsreplicated());
    // We will need to update the system catalogs with this new information
    for (int i = 0; i < plan.parameters.length; ++i) {
        StmtParameter catalogParam = stmt.getParameters().add(String.valueOf(i));
        catalogParam.setIndex(i);
        ParameterValueExpression pve = plan.parameters[i];
        catalogParam.setJavatype(pve.getValueType().getValue());
        catalogParam.setIsarray(pve.getParamIsVector());
    }
    PlanFragment frag = stmt.getFragments().add("0");
    // compute a hash of the plan
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        assert (false);
        // should never happen with healthy jvm
        System.exit(-1);
    }
    byte[] planBytes = writePlanBytes(frag, plan.rootPlanGraph);
    md.update(planBytes, 0, planBytes.length);
    // compute the 40 bytes of hex from the 20 byte sha1 hash of the plans
    md.reset();
    md.update(planBytes);
    frag.setPlanhash(Encoder.hexEncode(md.digest()));
    if (plan.subPlanGraph != null) {
        frag.setHasdependencies(true);
        frag.setNontransactional(true);
        frag.setMultipartition(true);
        frag = stmt.getFragments().add("1");
        frag.setHasdependencies(false);
        frag.setNontransactional(false);
        frag.setMultipartition(true);
        byte[] subBytes = writePlanBytes(frag, plan.subPlanGraph);
        // compute the 40 bytes of hex from the 20 byte sha1 hash of the plans
        md.reset();
        md.update(subBytes);
        frag.setPlanhash(Encoder.hexEncode(md.digest()));
    } else {
        frag.setHasdependencies(false);
        frag.setNontransactional(false);
        frag.setMultipartition(false);
    }
    // set the procedure parameter types from the statement parameter types
    int paramCount = 0;
    for (StmtParameter stmtParam : CatalogUtil.getSortedCatalogItems(stmt.getParameters(), "index")) {
        // name each parameter "param1", "param2", etc...
        ProcParameter procParam = newCatProc.getParameters().add("param" + String.valueOf(paramCount));
        procParam.setIndex(stmtParam.getIndex());
        procParam.setIsarray(stmtParam.getIsarray());
        procParam.setType(stmtParam.getJavatype());
        paramCount++;
    }
    return newCatProc;
}
Also used : CompiledPlan(org.voltdb.planner.CompiledPlan) ProcedurePartitionInfo(org.voltdb.CatalogContext.ProcedurePartitionInfo) Table(org.voltdb.catalog.Table) Statement(org.voltdb.catalog.Statement) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) Catalog(org.voltdb.catalog.Catalog) PlanFragment(org.voltdb.catalog.PlanFragment) StmtParameter(org.voltdb.catalog.StmtParameter) Database(org.voltdb.catalog.Database) StatementPartitioning(org.voltdb.planner.StatementPartitioning) Procedure(org.voltdb.catalog.Procedure) ParameterValueExpression(org.voltdb.expressions.ParameterValueExpression) MessageDigest(java.security.MessageDigest) QueryType(org.voltdb.types.QueryType) ProcParameter(org.voltdb.catalog.ProcParameter)

Aggregations

Statement (org.voltdb.catalog.Statement)4 QueryType (org.voltdb.types.QueryType)4 StmtParameter (org.voltdb.catalog.StmtParameter)3 ParameterValueExpression (org.voltdb.expressions.ParameterValueExpression)3 MessageDigest (java.security.MessageDigest)2 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)2 PlanFragment (org.voltdb.catalog.PlanFragment)2 ProcParameter (org.voltdb.catalog.ProcParameter)2 Procedure (org.voltdb.catalog.Procedure)2 VoltCompilerException (org.voltdb.compiler.VoltCompiler.VoltCompilerException)2 CompiledPlan (org.voltdb.planner.CompiledPlan)2 StatementPartitioning (org.voltdb.planner.StatementPartitioning)2 ImmutableMap (com.google_voltpatches.common.collect.ImmutableMap)1 Method (java.lang.reflect.Method)1 ArrayList (java.util.ArrayList)1 JSONException (org.json_voltpatches.JSONException)1 JSONObject (org.json_voltpatches.JSONObject)1 ProcedurePartitionInfo (org.voltdb.CatalogContext.ProcedurePartitionInfo)1 ProcInfo (org.voltdb.ProcInfo)1 ProcInfoData (org.voltdb.ProcInfoData)1