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;
  }
Exemple #5
0
  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());
  }