void TimeSeries::describeParamsAndColumns(UDRInvocationInfo &info)
{
  InternalColumns internalCols(info);

  // create PARTITION BY output columns, one passthru column
  // for every column that appears in PARTITION BY
  const PartitionInfo &part = info.in().getQueryPartitioning();
  int numPartCols = part.getNumEntries();

  for (int pc=0; pc<numPartCols; pc++)
    info.addPassThruColumns(0, part.getColumnNum(pc), part.getColumnNum(pc));

  // since we work locally in a partition, set the function type
  // of this TMUDF to REDUCER
  info.setFuncType(UDRInvocationInfo::REDUCER);

  // produce the time column, it has the same type as the
  // ORDER BY column that defines the input time value
  // and its name is specified by parameter 0
  const TypeInfo &timeType =
    info.in().getColumn(internalCols.getTimeSliceInColNum()).getType();

  info.out().addColumn(ColumnInfo(info.par().getString(0).c_str(),
                                  timeType));

  // produce aggregate columns
  for (int a=0; a<internalCols.getNumAggrCols(); a++)
    {
      TimeSeriesAggregate tsa = internalCols.getAggrColumn(a);
      std::string outColName(info.par().getString(2*a + 2));
      TypeInfo inColType(
           info.in().getColumn(tsa.getInputColNum()).getType());

      // append suffix to input column name to form the output column
      // name, make those all capitals to avoid delimited identifiers
      outColName += "_";
      if (tsa.isFirstVal())
        outColName += "F";
      else
        outColName += "L";
      if (tsa.isConstInterpol())
        outColName += "C";
      else
        outColName += "L";
      if (tsa.isIgnoreNulls())
        outColName += "I";

      if (tsa.isConstInterpol())
        {
          // add a column with the same data type as the original
          // column, but make it nullable if it isn't already
          inColType.setNullable(true);
          info.out().addColumn(ColumnInfo(outColName.c_str(), inColType));
        }
      else
        // add a "DOUBLE" output column to allow interpolation
        info.out().addColumn(ColumnInfo(
                                  outColName.c_str(),
                                  TypeInfo(TypeInfo::DOUBLE_PRECISION,
                                           0,
                                           true)));
    }

  // add formal parameters with types that match the actual ones
  for (int p=0; p<info.par().getNumColumns(); p++)
    {
      char parName[20];

      snprintf(parName, sizeof(parName), "PAR_%d", p);
      info.addFormalParameter(ColumnInfo(parName,
                                         info.par().getColumn(p).getType()));
    } 
}
Example #2
0
void Sessionize::describeParamsAndColumns(UDRInvocationInfo &info)
{
  // First, do some validation of the parameters and set
  // PARTITION BY and ORDER BY columns
  int idCol = -1;
  int tsCol = -1;

  // Make sure we have exactly one table-valued input, otherwise
  // generate a compile error
  if (info.getNumTableInputs() != 1)
    throw UDRException(38000,
                       "%s must be called with one table-valued input",
                       info.getUDRName().data());

  // check whether the first two arguments identify
  // an arbitrary column and an exact numeric column
  if (info.par().isAvailable(0))
    {
      const PartitionInfo &queryPartInfo = info.in().getQueryPartitioning();
      PartitionInfo newPartInfo;

      // This will raise an error if the column name
      // specified in the first parameter doesn't exist
      idCol = info.in().getColNum(info.par().getString(0));

      // make sure the query didn't specify a conflicting
      // PARTITION BY clause
      if (queryPartInfo.getType() == PartitionInfo::PARTITION &&
          (queryPartInfo.getNumEntries() != 1 ||
           queryPartInfo.getColumnNum(0) != idCol))
        throw UDRException(38001,
                           "Query PARTITION BY not compatible with id column %s",
                           info.par().getString(0).c_str());

      // Set this user id column as the required PARTITION BY column
      newPartInfo.setType(PartitionInfo::PARTITION);
      newPartInfo.addEntry(idCol);
      info.setChildPartitioning(0, newPartInfo);
    }
  else
    throw UDRException(38001,"First scalar parameter must be a string constant");

  // make sure the second parameter specifies the name of
  // an existing input column of type exact numeric
  if (info.par().isAvailable(1))
    {
      // This will raise an error if the column name
      // specified in the second parameter doesn't exist
      tsCol = info.in().getColNum(info.par().getString(1));
      const TypeInfo &typ = info.in().getColumn(tsCol).getType();
      const OrderInfo &queryOrderInfo = info.in().getQueryOrdering();
      OrderInfo newOrderInfo;

      if (typ.getSQLTypeSubClass() != TypeInfo::EXACT_NUMERIC_TYPE)
        throw UDRException(38002, "Second parameter must be the name of an exact numeric column");

      // check for a conflicting ORDER BY in the query
      if (queryOrderInfo.getNumEntries() > 0 &&
          (queryOrderInfo.getColumnNum(0) != tsCol ||
           queryOrderInfo.getOrderType(0) == OrderInfo::DESCENDING))
        throw UDRException(
             38900,
             "Query ORDER BY conflicts with specified timestamp column %s",
             info.par().getString(1).c_str());

      // make a new ORDER BY clause with just the timestamp column
      newOrderInfo.addEntry(tsCol);
      info.setChildOrdering(0, newOrderInfo);
    }
  else
    throw UDRException(38003,"Second scalar parameter must be a string constant");
 
  // To demonstrate state that gets passed between compiler phases and
  // to avoid looking up the id column and timestamp column each time,
  // store those as UDR Writer data in the UDRInvocationInfo object
  /* TBD: uncomment when this is allowed
  info.setUDRWriterCompileTimeData(new InternalColumns(idCol, tsCol));
  */

  // Second, define the output parameters

  // add the columns for session id and sequence number
  // (sequence_no is a unique sequence number within the session)
  info.out().addLongColumn("SESSION_ID");  // column number 0
  info.out().addLongColumn("SEQUENCE_NO"); // column number 1
 
  // Make all the input table columns also output columns,
  // those are called "pass-through" columns. The default
  // parameters of this method add all the columns of the
  // first input table.
  info.addPassThruColumns();

  // set the function type, sessionize behaves like a reducer in
  // MapReduce. Session ids are local within rows that share the
  // same id column value.
  info.setFuncType(UDRInvocationInfo::REDUCER);
}