C++ language scalar function (remote mode)

This example uses the following file name:

applyopcpp.cpp

Code

The code in this example shows that, with minor changes, this program works in remote mode:

The change is in the main. The required remote AE connection name, testcapi, is set and then a loop is added around getAPI so it is called two times. This code works like a local AE and handles two iterations as a remote AE.

#include <nzaefactory.hpp>

using namespace nz::ae;

static int run(nz::ae::NzaeFunction *aeFunc);

int main(int argc, char * argv[])
{
    NzaeApiGenerator helper;
    // The following line is only needed if a launcher is not used
    helper.setName("testcapi");
    for (int i=0; i < 2; i++) {
        nz::ae::NzaeApi &api = helper.getApi(nz::ae::NzaeApi::FUNCTION);      
        run(api.aeFunction);
        if (!helper.isRemote())
            break;
    }

    return 0;
}

class MyHandler : public NzaeFunctionMessageHandler
{
public:
    MyHandler() {
        m_Once = false;
    }
    enum OperatorEnum
    {
        OP_ADD = 1,
        OP_MULT = 2
    } op;

    void doOnce(NzaeFunction& api) {
        const NzaeMetadata& meta = api.getMetadata();
        if (meta.getOutputColumnCount() != 1 ||
            meta.getOutputType(0) != NzaeDataTypes::NZUDSUDX_DOUBLE) {
            throw NzaeException("expecting one output column of type double");
        }
        if (meta.getInputColumnCount() < 1) {
            throw NzaeException("expecting at least one input column");
        }
        if (meta.getInputType(0) != NzaeDataTypes::NZUDSUDX_FIXED &&
            meta.getInputType(0) != NzaeDataTypes::NZUDSUDX_VARIABLE) {
            throw NzaeException("first input column expected to be a string
type");
        }
        m_Once = true;
    }

        void evaluate(NzaeFunction& api, NzaeRecord &input, NzaeRecord &result) {
    
        if (!m_Once)
            doOnce(api);
        double res = 0;

        NzaeField &field = input.get(0);
        if (field.isNull())
            throw NzaeException("first input column may not be null");
        NzaeStringField &sf = (NzaeStringField&) input.get(0);
        std::string strop = (std::string)sf;
        if (strop == "+")
            op = OP_ADD;
        else if (strop == "*") {
            op = OP_MULT;
            res = 1;
        }
        else
            throw NzaeException(NzaeException::format("unexpected operator %s", \
                strop.c_str()));

        for (int i=1 ; i < input.numFields(); i++) {
            NzaeField &inf = input.get(i);
            if (inf.isNull())
                continue;
            double temp = 0;
            bool ignore = false;
            switch (inf.type()) {
                case NzaeDataTypes::NZUDSUDX_INT8:
                {
                    NzaeInt8Field &sf = (NzaeInt8Field&)input.get(i);
                    temp = (int8_t)sf;
                    break;
                }
                case NzaeDataTypes::NZUDSUDX_INT16:
                {
                    NzaeInt16Field &sf = (NzaeInt16Field&)input.get(i);
                    temp = (int16_t)sf;
                    break;
                }
                case NzaeDataTypes::NZUDSUDX_INT32:
                {
                    NzaeInt32Field &sf = (NzaeInt32Field&)input.get(i);
                    temp = (int32_t)sf;
                    break;
                }
                case NzaeDataTypes::NZUDSUDX_INT64:
                {
                    NzaeInt64Field &sf = (NzaeInt64Field&)input.get(i);
                    temp = (int64_t)sf;
                    break;
                }
                case NzaeDataTypes::NZUDSUDX_FLOAT:
                {
                    NzaeFloatField &sf = (NzaeFloatField&)input.get(i);
                    temp = (float)sf;
                    break;
                }
                case NzaeDataTypes::NZUDSUDX_DOUBLE:
                {
                    NzaeDoubleField &sf = (NzaeDoubleField&)input.get(i);
                    temp = (double)sf;
                    break;
                }
                case NzaeDataTypes::NZUDSUDX_NUMERIC32:
                case NzaeDataTypes::NZUDSUDX_NUMERIC64:
                case NzaeDataTypes::NZUDSUDX_NUMERIC128:
                {
                    NzaeNumericField &sf = (NzaeNumericField&)input.get(i);
                    temp = (double)sf;
                    break;
                }
            default:
                ignore = true;
                // ignore
                break;
            }
            if (!ignore) {
                if (op == OP_ADD)
                    res += temp;
                else
                    res *= temp;
            }
        }
        NzaeDoubleField &f = (NzaeDoubleField&)result.get(0);
        f = res;
    }
    bool m_Once;
};

static int run(NzaeFunction *aeFunc)
{
    aeFunc->run(new MyHandler());
    return 0;
}

Compilation

Use the standard compile, as in local mode:

$NZ_EXPORT_DIR/ae/utilities/bin/compile_ae --language cpp --template compile \
     --exe applyopcpp --compargs "-g -Wall" --linkargs "-g" applyopcpp.cpp \
     --version 3

Registration

Registration is slightly different. First run this to register:
$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --sig "rem_applyop_cpp(VARARGS)" \
     --return "double" --language cpp --template udf --exe applyopcpp \
     --version 3 --remote --rname testcapi
Then run the following. It specifies the code be registered as a remote AE with a remote name of testcapi, which matches the code. In addition, it registers a launcher:
$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --sig "rem_applyop_launch(int8)" \
     --return "TABLE(aeresult varchar(255))" --language cpp --template udtf \
     --exe applyopcpp --version 3 --remote --launch --rname testcapi

With the exception of the --rname, --exe and the name portion of --sig, all launchers look like the above. In this case it is a UDTF since that is the interface for all launchers. The value of --rname must match the name in the code.

Running

To run the AE in remote mode, the executable is run as a “server.” In this instance, it handles only queries run on the host. Usually, AEs are started on the SPUs as well. Start an instance on the host:

SELECT * FROM TABLE WITH FINAL(rem_applyop_launch(0));
AERESULT
-------------------------------------------------------------------------------
tran: 7192 DATA slc: 0 hardware: 0 machine: spubox1 process: 8306 thread: 8306
(1 row)

Notice that a different syntax is used to invoke the table function style launcher. This is the syntax used to call any UDTF-based AE. Now run the AE:

SELECT rem_applyop_cpp('+', 4,5,1.1);
REM_APPLYOP_CPP
-----------------
10.1
(1 row)

Finally, cause an error:

SELECT rem_applyop_cpp(1);
ERROR: first input COLUMN expected TO be a string type