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())); } }
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); }