public static void initSQLStmt(SQLStmt stmt, Statement catStmt) { stmt.catStmt = catStmt; stmt.numFragGUIDs = catStmt.getFragments().size(); PlanFragment fragments[] = new PlanFragment[stmt.numFragGUIDs]; stmt.fragGUIDs = new long[stmt.numFragGUIDs]; int i = 0; for (PlanFragment frag : stmt.catStmt.getFragments()) { fragments[i] = frag; stmt.fragGUIDs[i] = CatalogUtil.getUniqueIdForFragment(frag); i++; } stmt.numStatementParamJavaTypes = stmt.catStmt.getParameters().size(); // StmtParameter parameters[] = new StmtParameter[stmt.numStatementParamJavaTypes]; stmt.statementParamJavaTypes = new byte[stmt.numStatementParamJavaTypes]; for (StmtParameter param : stmt.catStmt.getParameters()) { // parameters[i] = param; stmt.statementParamJavaTypes[param.getIndex()] = (byte) param.getJavatype(); i++; } }
protected ParameterSet getCleanParams(SQLStmt stmt, Object... args) { final int numParamTypes = stmt.numStatementParamJavaTypes; final byte stmtParamTypes[] = stmt.statementParamJavaTypes; if (args.length != numParamTypes) { throw new ExpectedProcedureException( "Number of arguments provided was " + args.length + " where " + numParamTypes + " was expected for statement " + stmt.getText()); } for (int ii = 0; ii < numParamTypes; ii++) { // this only handles null values if (args[ii] != null) continue; VoltType type = VoltType.get(stmtParamTypes[ii]); if (type == VoltType.TINYINT) args[ii] = Byte.MIN_VALUE; else if (type == VoltType.SMALLINT) args[ii] = Short.MIN_VALUE; else if (type == VoltType.INTEGER) args[ii] = Integer.MIN_VALUE; else if (type == VoltType.BIGINT) args[ii] = Long.MIN_VALUE; else if (type == VoltType.FLOAT) args[ii] = VoltType.NULL_FLOAT; else if (type == VoltType.TIMESTAMP) args[ii] = new TimestampType(Long.MIN_VALUE); else if (type == VoltType.STRING) args[ii] = VoltType.NULL_STRING_OR_VARBINARY; else if (type == VoltType.VARBINARY) args[ii] = VoltType.NULL_STRING_OR_VARBINARY; else if (type == VoltType.DECIMAL) args[ii] = VoltType.NULL_DECIMAL; else throw new ExpectedProcedureException( "Unknown type " + type + " can not be converted to NULL representation for arg " + ii + " for SQL stmt " + stmt.getText()); } final ParameterSet params = new ParameterSet(); params.setParameters(args); return params; }
protected void reflect() { // fill in the sql for single statement procs if (m_catProc.getHasjava() == false) { try { Map<String, Field> stmtMap = ProcedureCompiler.getValidSQLStmts(null, m_procedureName, m_procedure.getClass(), true); Field f = stmtMap.get(VoltDB.ANON_STMT_NAME); assert (f != null); SQLStmt stmt = (SQLStmt) f.get(m_procedure); Statement statement = m_catProc.getStatements().get(VoltDB.ANON_STMT_NAME); stmt.sqlText = statement.getSqltext().getBytes(VoltDB.UTF8ENCODING); m_cachedSingleStmt.stmt = stmt; int numParams = m_catProc.getParameters().size(); m_paramTypes = new Class<?>[numParams]; m_paramTypeIsPrimitive = new boolean[numParams]; m_paramTypeIsArray = new boolean[numParams]; m_paramTypeComponentType = new Class<?>[numParams]; for (ProcParameter param : m_catProc.getParameters()) { VoltType type = VoltType.get((byte) param.getType()); if (type == VoltType.INTEGER) { type = VoltType.BIGINT; } else if (type == VoltType.SMALLINT) { type = VoltType.BIGINT; } else if (type == VoltType.TINYINT) { type = VoltType.BIGINT; } else if (type == VoltType.NUMERIC) { type = VoltType.FLOAT; } m_paramTypes[param.getIndex()] = type.classFromType(); m_paramTypeIsPrimitive[param.getIndex()] = m_paramTypes[param.getIndex()].isPrimitive(); m_paramTypeIsArray[param.getIndex()] = param.getIsarray(); assert (m_paramTypeIsArray[param.getIndex()] == false); m_paramTypeComponentType[param.getIndex()] = null; // rtb: what is broken (ambiguous?) that is being patched here? // hack to fixup varbinary support for statement procedures if (m_paramTypes[param.getIndex()] == byte[].class) { m_paramTypeComponentType[param.getIndex()] = byte.class; m_paramTypeIsArray[param.getIndex()] = true; } } } catch (Exception e) { // shouldn't throw anything outside of the compiler e.printStackTrace(); } } else { // parse the java run method Method[] methods = m_procedure.getClass().getDeclaredMethods(); for (final Method m : methods) { String name = m.getName(); if (name.equals("run")) { if (Modifier.isPublic(m.getModifiers()) == false) continue; m_procMethod = m; m_paramTypes = m.getParameterTypes(); int tempParamTypesLength = m_paramTypes.length; m_paramTypeIsPrimitive = new boolean[tempParamTypesLength]; m_paramTypeIsArray = new boolean[tempParamTypesLength]; m_paramTypeComponentType = new Class<?>[tempParamTypesLength]; for (int ii = 0; ii < tempParamTypesLength; ii++) { m_paramTypeIsPrimitive[ii] = m_paramTypes[ii].isPrimitive(); m_paramTypeIsArray[ii] = m_paramTypes[ii].isArray(); m_paramTypeComponentType[ii] = m_paramTypes[ii].getComponentType(); } } } if (m_procMethod == null) { throw new RuntimeException( "No \"run\" method found in: " + m_procedure.getClass().getName()); } } // iterate through the fields and deal with sql statements Map<String, Field> stmtMap = null; try { stmtMap = ProcedureCompiler.getValidSQLStmts(null, m_procedureName, m_procedure.getClass(), true); } catch (Exception e1) { // shouldn't throw anything outside of the compiler e1.printStackTrace(); return; } Field[] fields = new Field[stmtMap.size()]; int index = 0; for (Field f : stmtMap.values()) { fields[index++] = f; } for (final Field f : fields) { String name = f.getName(); Statement s = m_catProc.getStatements().get(name); if (s != null) { try { /* * Cache all the information we need about the statements in this stored * procedure locally instead of pulling them from the catalog on * a regular basis. */ SQLStmt stmt = (SQLStmt) f.get(m_procedure); // done in a static method in an abstract class so users don't call it initSQLStmt(stmt, s); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } // LOG.fine("Found statement " + name); } } }
/* * Execute a batch of homogeneous queries, i.e. all reads or all writes. */ VoltTable[] executeSlowHomogeneousBatch(final List<QueuedSQL> batch, final boolean finalTask) { BatchState state = new BatchState(batch.size(), m_txnState, m_site.getCorrespondingSiteId(), finalTask); // iterate over all sql in the batch, filling out the above data structures for (int i = 0; i < batch.size(); ++i) { QueuedSQL queuedSQL = batch.get(i); assert (queuedSQL.stmt != null); // Figure out what is needed to resume the proc int collectorOutputDepId = m_txnState.getNextDependencyId(); state.m_depsToResume[i] = collectorOutputDepId; // Build the set of params for the frags FastSerializer fs = new FastSerializer(); try { fs.writeObject(queuedSQL.params); } catch (IOException e) { throw new RuntimeException( "Error serializing parameters for SQL statement: " + queuedSQL.stmt.getText() + " with params: " + queuedSQL.params.toJSONString(), e); } ByteBuffer params = fs.getBuffer(); assert (params != null); // populate the actual lists of fragments and params if (queuedSQL.stmt.catStmt != null) { // Pre-planned query. int numFrags = queuedSQL.stmt.catStmt.getFragments().size(); assert (numFrags > 0); assert (numFrags <= 2); /* * This numfrags == 1 code is for routing multi-partition reads of a * replicated table to the local site. This was a broken performance * optimization. see https://issues.voltdb.com/browse/ENG-1232. * * The problem is that the fragments for the replicated read are not correctly * interleaved with the distributed writes to the replicated table that might * be in the same batch of SQL statements. We do end up doing the replicated * read locally but we break up the batches in the face of mixed reads and * writes */ Iterator<PlanFragment> fragmentIter = queuedSQL.stmt.catStmt.getFragments().iterator(); if (numFrags == 1) { PlanFragment frag = fragmentIter.next(); state.addFragment(i, frag, params); } else { // collector/aggregator pair (guaranteed above that numFrags==2 here) PlanFragment frag1 = fragmentIter.next(); assert (frag1 != null); PlanFragment frag2 = fragmentIter.next(); assert (frag2 != null); // frags with no deps are usually collector frags that go to all partitions // figure out which frag is which type if (frag1.getHasdependencies() == false) { state.addFragmentPair(i, frag1, frag2, params); } else { state.addFragmentPair(i, frag2, frag1, params); } } } else { /* * Unplanned custom query. Requires an attached plan. * Set up collector and dependent aggregator fragments. */ SQLStmtPlan plan = queuedSQL.stmt.getPlan(); assert (plan != null); byte[] collectorFragment = plan.getCollectorFragment(); byte[] aggregatorFragment = plan.getAggregatorFragment(); assert (aggregatorFragment != null); if (collectorFragment == null) { // Multi-partition/non-replicated with collector and aggregator. state.addCustomFragment(i, aggregatorFragment, params); } else { // Multi-partition/replicated with just an aggregator fragment. state.addCustomFragmentPair(i, collectorFragment, aggregatorFragment, params); } } } // instruct the dtxn what's needed to resume the proc m_txnState.setupProcedureResume(finalTask, state.m_depsToResume); // create all the local work for the transaction for (int i = 0; i < state.m_depsForLocalTask.length; i++) { if (state.m_depsForLocalTask[i] < 0) continue; state.m_localTask.addInputDepId(i, state.m_depsForLocalTask[i]); } // note: non-transactional work only helps us if it's final work m_txnState.createLocalFragmentWork( state.m_localTask, state.m_localFragsAreNonTransactional && finalTask); if (!state.m_distributedTask.isEmpty()) { m_txnState.createAllParticipatingFragmentWork(state.m_distributedTask); } // recursively call recursableRun and don't allow it to shutdown Map<Integer, List<VoltTable>> mapResults = m_site.recursableRun(m_txnState); assert (mapResults != null); assert (state.m_depsToResume != null); assert (state.m_depsToResume.length == batch.size()); // build an array of answers, assuming one result per expected id for (int i = 0; i < batch.size(); i++) { List<VoltTable> matchingTablesForId = mapResults.get(state.m_depsToResume[i]); assert (matchingTablesForId != null); assert (matchingTablesForId.size() == 1); state.m_results[i] = matchingTablesForId.get(0); // get isReplicated flag from either the catalog statement or the plan, // depending on whether it's pre-planned or unplanned final SQLStmt stmt = batch.get(i).stmt; boolean isReplicated; if (stmt.catStmt != null) { isReplicated = stmt.catStmt.getReplicatedtabledml(); } else { final SQLStmtPlan plan = stmt.getPlan(); assert (plan != null); isReplicated = plan.isReplicatedTableDML(); } // if replicated divide by the replication factor if (isReplicated) { long newVal = state.m_results[i].asScalarLong() / m_site.getReplicatedDMLDivisor(); state.m_results[i] = new VoltTable(new VoltTable.ColumnInfo("modified_tuples", VoltType.BIGINT)); state.m_results[i].addRow(newVal); } } return state.m_results; }
VoltTable runSQLWithSubstitutions( final SQLStmt stmt, ParameterSet params, byte[] paramJavaTypes) { // HSQLProcedureWrapper does nothing smart. it just implements this interface with // runStatement() StringBuilder sqlOut = new StringBuilder(stmt.getText().length() * 2); assert (paramJavaTypes != null); int lastIndex = 0; String sql = stmt.getText(); // if there's no ? in the statmemt, then zero out any auto-parameterization int paramCount = StringUtils.countMatches(sql, "?"); if (paramCount == 0) { params = ParameterSet.emptyParameterSet(); paramJavaTypes = new byte[0]; } Object[] paramObjs = params.toArray(); for (int i = 0; i < paramObjs.length; i++) { int nextIndex = sql.indexOf('?', lastIndex); if (nextIndex == -1) throw new RuntimeException("SQL Statement has more arguments than params."); sqlOut.append(sql, lastIndex, nextIndex); lastIndex = nextIndex + 1; VoltType type = VoltType.get(paramJavaTypes[i]); if (VoltType.isNullVoltType(paramObjs[i])) { sqlOut.append("NULL"); } else if (paramObjs[i] instanceof TimestampType) { if (type != VoltType.TIMESTAMP) throw new RuntimeException("Inserting date into mismatched column type in HSQL."); TimestampType d = (TimestampType) paramObjs[i]; // convert VoltDB's microsecond granularity to millis. Timestamp t = new Timestamp(d.getTime() / 1000); sqlOut.append('\'').append(t.toString()).append('\''); } else if (paramObjs[i] instanceof byte[]) { if (type == VoltType.STRING) { // Convert from byte[] -> String; escape single quotes try { sqlOut.append(sqlEscape(new String((byte[]) paramObjs[i], "UTF-8"))); } catch (UnsupportedEncodingException e) { // should NEVER HAPPEN System.err.println("FATAL: Your JVM doens't support UTF-&"); System.exit(-1); } } else if (type == VoltType.VARBINARY) { // Convert from byte[] -> String; using hex sqlOut.append(sqlEscape(Encoder.hexEncode((byte[]) paramObjs[i]))); } else { throw new RuntimeException( "Inserting string/varbinary (bytes) into mismatched column type in HSQL."); } } else if (paramObjs[i] instanceof String) { if (type != VoltType.STRING) throw new RuntimeException("Inserting string into mismatched column type in HSQL."); // Escape single quotes sqlOut.append(sqlEscape((String) paramObjs[i])); } else { if (type == VoltType.TIMESTAMP) { long t = Long.parseLong(paramObjs[i].toString()); TimestampType d = new TimestampType(t); // convert VoltDB's microsecond granularity to millis Timestamp ts = new Timestamp(d.getTime() * 1000); sqlOut.append('\'').append(ts.toString()).append('\''); } else sqlOut.append(paramObjs[i].toString()); } } sqlOut.append(sql, lastIndex, sql.length()); return runDML(sqlOut.toString()); }