Search in sources :

Example 1 with SQLStmt

use of org.voltdb.SQLStmt in project voltdb by VoltDB.

the class LoadSinglepartitionTable method run.

/**
     * These parameters, with the exception of ctx, map to user provided values.
     *
     * @param ctx Internal API provided to all system procedures.
     * @param partitionParam Partitioning parameter used to match invocation to partition.
     * @param tableName Name of persistent, parititoned table receiving data.
     * @param table A VoltTable with schema matching the target table containing data to load.
     *              It's assumed that each row in this table partitions to the same partition
     *              as the other rows, and to the same partition as the partition parameter.
     * @param upsertMode True if using upsert instead of insert. If using insert, this proc
     *              will fail if there are any uniqueness constraints violated.
     * @return The number of rows modified. This will be inserts in insert mode, but in upsert
     *              mode, this will be the sum of inserts and updates.
     * @throws VoltAbortException on any failure, but the most common failures are non-matching
     *              partitioning or unique constraint violations.
     */
public long run(SystemProcedureExecutionContext ctx, byte[] partitionParam, String tableName, byte upsertMode, VoltTable table) throws VoltAbortException {
    // if tableName is replicated, fail.
    // otherwise, create a VoltTable for each partition and
    // split up the incoming table .. then send those partial
    // tables to the appropriate sites.
    // Get the metadata object for the table in question from the global metadata/config
    // store, the Catalog.
    Table catTable = ctx.getDatabase().getTables().getIgnoreCase(tableName);
    if (catTable == null) {
        throw new VoltAbortException("Table not present in catalog.");
    }
    // if tableName is replicated, fail.
    if (catTable.getIsreplicated()) {
        throw new VoltAbortException(String.format("LoadSinglepartitionTable incompatible with replicated table %s.", tableName));
    }
    // convert from 8bit signed integer (byte) to boolean
    boolean isUpsert = (upsertMode != 0);
    // upsert requires a primary key on the table to work
    if (isUpsert) {
        boolean hasPkey = false;
        for (Constraint c : catTable.getConstraints()) {
            if (c.getType() == ConstraintType.PRIMARY_KEY.getValue()) {
                hasPkey = true;
                break;
            }
        }
        if (!hasPkey) {
            throw new VoltAbortException(String.format("The --update argument cannot be used for LoadingSinglePartionTable because the table %s does not have a primary key. " + "Either remove the --update argument or add a primary key to the table.", tableName));
        }
    }
    // action should be either "insert" or "upsert"
    final String action = (isUpsert ? "upsert" : "insert");
    // fix any case problems
    tableName = catTable.getTypeName();
    // check that the schema of the input matches
    int columnCount = table.getColumnCount();
    //////////////////////////////////////////////////////////////////////
    // Find the insert/upsert statement for this table
    // This is actually the big trick this procedure does.
    // It borrows the insert plan from the auto-generated insert procedure
    // named "TABLENAME.insert" or "TABLENAME.upsert".
    // We don't like it when users do this stuff, but it is safe in this
    // case.
    //
    // Related code to read is org.voltdb.DefaultProcedureManager, which
    // manages all of the default (CRUD) procedures created lazily for
    // each table in the database, including the plans used here.
    //
    String crudProcName = String.format("%s.%s", tableName, action);
    Procedure p = ctx.ensureDefaultProcLoaded(crudProcName);
    if (p == null) {
        throw new VoltAbortException(String.format("Unable to locate auto-generated CRUD %s statement for table %s", action, tableName));
    }
    // statements of all single-statement procs are named "sql"
    Statement catStmt = p.getStatements().get(VoltDB.ANON_STMT_NAME);
    if (catStmt == null) {
        throw new VoltAbortException(String.format("Unable to find SQL statement for found table %s: BAD", tableName));
    }
    // Create a SQLStmt instance on the fly
    // This unusual to do, as they are typically required to be final instance variables.
    // This only works because the SQL text and plan is identical from the borrowed procedure.
    SQLStmt stmt = new SQLStmt(catStmt.getSqltext());
    m_runner.initSQLStmt(stmt, catStmt);
    long queued = 0;
    long executed = 0;
    // make sure at the start of the table
    table.resetRowPosition();
    // iterate over the rows queueing a sql statement for each row to insert
    for (int i = 0; table.advanceRow(); ++i) {
        Object[] params = new Object[columnCount];
        // get the parameters from the volt table
        for (int col = 0; col < columnCount; ++col) {
            params[col] = table.get(col, table.getColumnType(col));
        }
        // queue an insert and count it
        voltQueueSQL(stmt, params);
        ++queued;
        // 100 is an arbitrary number
        if ((i % 100) == 0) {
            executed += executeSQL();
        }
    }
    // execute any leftover batched statements
    if (queued > executed) {
        executed += executeSQL();
    }
    return executed;
}
Also used : SQLStmt(org.voltdb.SQLStmt) Table(org.voltdb.catalog.Table) VoltTable(org.voltdb.VoltTable) Constraint(org.voltdb.catalog.Constraint) Statement(org.voltdb.catalog.Statement) VoltSystemProcedure(org.voltdb.VoltSystemProcedure) Procedure(org.voltdb.catalog.Procedure) Constraint(org.voltdb.catalog.Constraint)

Example 2 with SQLStmt

use of org.voltdb.SQLStmt in project voltdb by VoltDB.

the class AdHocBase method runAdHoc.

/**
     * Call from derived class run() method for implementation.
     *
     * @param ctx
     * @param aggregatorFragments
     * @param collectorFragments
     * @param sqlStatements
     * @param replicatedTableDMLFlags
     * @return
     */
public VoltTable[] runAdHoc(SystemProcedureExecutionContext ctx, byte[] serializedBatchData) {
    Pair<Object[], AdHocPlannedStatement[]> data = decodeSerializedBatchData(serializedBatchData);
    Object[] userparams = data.getFirst();
    AdHocPlannedStatement[] statements = data.getSecond();
    if (statements.length == 0) {
        return new VoltTable[] {};
    }
    for (AdHocPlannedStatement statement : statements) {
        if (!statement.core.wasPlannedAgainstHash(ctx.getCatalogHash())) {
            @SuppressWarnings("deprecation") String msg = String.format("AdHoc transaction %d wasn't planned " + "against the current catalog version. Statement: %s", DeprecatedProcedureAPIAccess.getVoltPrivateRealTransactionId(this), new String(statement.sql, Constants.UTF8ENCODING));
            throw new VoltAbortException(msg);
        }
        // Don't cache the statement text, since ad hoc statements
        // that differ only by constants reuse the same plan, statement text may change.
        long aggFragId = ActivePlanRepository.loadOrAddRefPlanFragment(statement.core.aggregatorHash, statement.core.aggregatorFragment, null);
        long collectorFragId = 0;
        if (statement.core.collectorFragment != null) {
            collectorFragId = ActivePlanRepository.loadOrAddRefPlanFragment(statement.core.collectorHash, statement.core.collectorFragment, null);
        }
        SQLStmt stmt = SQLStmtAdHocHelper.createWithPlan(statement.sql, aggFragId, statement.core.aggregatorHash, true, collectorFragId, statement.core.collectorHash, true, statement.core.isReplicatedTableDML, statement.core.readOnly, statement.core.parameterTypes, m_site);
        Object[] params = paramsForStatement(statement, userparams);
        voltQueueSQL(stmt, params);
    }
    return voltExecuteSQL(true);
}
Also used : SQLStmt(org.voltdb.SQLStmt) AdHocPlannedStatement(org.voltdb.compiler.AdHocPlannedStatement) VoltTable(org.voltdb.VoltTable)

Example 3 with SQLStmt

use of org.voltdb.SQLStmt in project voltdb by VoltDB.

the class LoadMultipartitionTable method run.

/**
     * These parameters, with the exception of ctx, map to user provided values.
     *
     * @param ctx
     *            Internal. Not a user-supplied parameter.
     * @param tableName
     *            Name of persistent table receiving data.
     * @param table
     *            A VoltTable with schema matching tableName containing data to
     *            load.
     * @param upsertMode
     *            True if using upsert instead of insert
     * @return {@link org.voltdb.VoltSystemProcedure#STATUS_SCHEMA}
     * @throws VoltAbortException
     */
public long run(SystemProcedureExecutionContext ctx, String tableName, byte upsertMode, VoltTable table) throws VoltAbortException {
    // if tableName is replicated, just send table everywhere.
    // otherwise, create a VoltTable for each partition and
    // split up the incoming table .. then send those partial
    // tables to the appropriate sites.
    Table catTable = ctx.getDatabase().getTables().getIgnoreCase(tableName);
    if (catTable == null) {
        throw new VoltAbortException("Table not present in catalog.");
    }
    boolean isUpsert = (upsertMode != 0);
    if (isUpsert) {
        boolean hasPkey = false;
        for (Constraint c : catTable.getConstraints()) {
            if (c.getType() == ConstraintType.PRIMARY_KEY.getValue()) {
                hasPkey = true;
                break;
            }
        }
        if (!hasPkey) {
            throw new VoltAbortException(String.format("The --update argument cannot be used for LoadMultipartitionTable because the table %s does not have a primary key. " + "Either remove the --update argument or add a primary key to the table.", tableName));
        }
    }
    // action should be either "insert" or "upsert"
    final String action = (isUpsert ? "upsert" : "insert");
    // fix any case problems
    tableName = catTable.getTypeName();
    // check that the schema of the input matches
    int columnCount = table.getColumnCount();
    // find the insert statement for this table
    String crudProcName = String.format("%s.%s", tableName.toUpperCase(), action);
    Procedure proc = ctx.ensureDefaultProcLoaded(crudProcName);
    if (proc == null) {
        throw new VoltAbortException(String.format("Unable to locate auto-generated CRUD %s statement for table %s", action, tableName));
    }
    // ensure MP fragment tasks load the plan for the table loading procedure
    m_runner.setProcNameToLoadForFragmentTasks(crudProcName);
    Statement catStmt = proc.getStatements().get(VoltDB.ANON_STMT_NAME);
    if (catStmt == null) {
        throw new VoltAbortException(String.format("Unable to find SQL statement for found table %s: BAD", tableName));
    }
    // create a SQLStmt instance on the fly (unusual to do)
    SQLStmt stmt = new SQLStmt(catStmt.getSqltext());
    m_runner.initSQLStmt(stmt, catStmt);
    if (catTable.getIsreplicated()) {
        long queued = 0;
        long executed = 0;
        // make sure at the start of the table
        table.resetRowPosition();
        for (int i = 0; table.advanceRow(); ++i) {
            Object[] params = new Object[columnCount];
            // get the parameters from the volt table
            for (int col = 0; col < columnCount; ++col) {
                params[col] = table.get(col, table.getColumnType(col));
            }
            // queue an insert and count it
            voltQueueSQL(stmt, params);
            ++queued;
            // 100 is an arbitrary number
            if ((i % 100) == 0) {
                executed += executeSQL();
            }
        }
        // execute any leftover batched statements
        if (queued > executed) {
            executed += executeSQL();
        }
        return executed;
    } else {
        throw new VoltAbortException("LoadMultipartitionTable no longer supports loading partitioned tables" + " use CRUD procs instead");
    }
}
Also used : SQLStmt(org.voltdb.SQLStmt) Table(org.voltdb.catalog.Table) VoltTable(org.voltdb.VoltTable) Constraint(org.voltdb.catalog.Constraint) Statement(org.voltdb.catalog.Statement) VoltSystemProcedure(org.voltdb.VoltSystemProcedure) Procedure(org.voltdb.catalog.Procedure) Constraint(org.voltdb.catalog.Constraint)

Example 4 with SQLStmt

use of org.voltdb.SQLStmt in project voltdb by VoltDB.

the class ProcedureCompiler method getValidSQLStmts.

public static Map<String, SQLStmt> getValidSQLStmts(VoltCompiler compiler, String procName, Class<?> procClass, Object procInstance, boolean withPrivate) throws VoltCompilerException {
    Map<String, SQLStmt> retval = new HashMap<>();
    Field[] fields = procClass.getDeclaredFields();
    for (Field f : fields) {
        // skip non SQL fields
        if (f.getType() != SQLStmt.class)
            continue;
        int modifiers = f.getModifiers();
        // skip private fields if asked (usually a superclass)
        if (Modifier.isPrivate(modifiers) && (!withPrivate))
            continue;
        // don't allow non-final SQLStmts
        if (Modifier.isFinal(modifiers) == false) {
            String msg = "Procedure " + procName + " contains a non-final SQLStmt field.";
            if (procClass.getSimpleName().equals(procName) == false) {
                msg = "Superclass " + procClass.getSimpleName() + " of procedure " + procName + " contains a non-final SQLStmt field.";
            }
            if (compiler != null)
                throw compiler.new VoltCompilerException(msg);
            else
                new VoltLogger("HOST").warn(msg);
        }
        f.setAccessible(true);
        SQLStmt stmt = null;
        try {
            stmt = (SQLStmt) f.get(procInstance);
        }// it's weird, but seems rather hard to hit
         catch (Exception e) {
            e.printStackTrace();
            continue;
        }
        retval.put(f.getName(), stmt);
    }
    Class<?> superClass = procClass.getSuperclass();
    if (superClass != null) {
        Map<String, SQLStmt> superStmts = getValidSQLStmts(compiler, procName, superClass, procInstance, false);
        for (Entry<String, SQLStmt> e : superStmts.entrySet()) {
            if (retval.containsKey(e.getKey()) == false)
                retval.put(e.getKey(), e.getValue());
        }
    }
    return retval;
}
Also used : Field(java.lang.reflect.Field) SQLStmt(org.voltdb.SQLStmt) HashMap(java.util.HashMap) VoltLogger(org.voltcore.logging.VoltLogger) VoltCompilerException(org.voltdb.compiler.VoltCompiler.VoltCompilerException) VoltCompilerException(org.voltdb.compiler.VoltCompiler.VoltCompilerException) VoltTypeException(org.voltdb.VoltTypeException)

Example 5 with SQLStmt

use of org.voltdb.SQLStmt 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)

Aggregations

SQLStmt (org.voltdb.SQLStmt)5 VoltTable (org.voltdb.VoltTable)4 Procedure (org.voltdb.catalog.Procedure)3 Statement (org.voltdb.catalog.Statement)3 VoltSystemProcedure (org.voltdb.VoltSystemProcedure)2 VoltTypeException (org.voltdb.VoltTypeException)2 Constraint (org.voltdb.catalog.Constraint)2 Table (org.voltdb.catalog.Table)2 VoltCompilerException (org.voltdb.compiler.VoltCompiler.VoltCompilerException)2 ImmutableMap (com.google_voltpatches.common.collect.ImmutableMap)1 Field (java.lang.reflect.Field)1 Method (java.lang.reflect.Method)1 HashMap (java.util.HashMap)1 VoltLogger (org.voltcore.logging.VoltLogger)1 ProcInfo (org.voltdb.ProcInfo)1 ProcInfoData (org.voltdb.ProcInfoData)1 VoltNonTransactionalProcedure (org.voltdb.VoltNonTransactionalProcedure)1 VoltProcedure (org.voltdb.VoltProcedure)1 VoltType (org.voltdb.VoltType)1 Group (org.voltdb.catalog.Group)1