Fonction scalaire en langage C++ (mode distant)

Cet exemple utilise le nom de fichier suivant :

applyopcpp.cpp

Coder

Le code de cet exemple montre que, moyennant quelques modifications mineures, ce programme fonctionne en mode distant :

Le changement se situe au niveau de l'essentiel. Le nom de la connexion AE distante requise, testcapi, est défini, puis une boucle est ajoutée autour de getAPI afin qu'il soit appelé deux fois. Ce code fonctionne comme un AE local et gère deux itérations comme un AE distant.

#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

Utiliser la compilation standard, comme en mode local :

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

Enregistrement

L'inscription est légèrement différente. Exécutez d'abord cette procédure pour vous enregistrer :
$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
Exécutez ensuite la procédure suivante. Il spécifie que le code doit être enregistré en tant qu'AE distant avec un nom distant de testcapi, qui correspond au code. En outre, il enregistre un lanceur :
$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

À l'exception de --rname, --exe et de la partie nom de --sig, tous les lanceurs ressemblent à ce qui précède. Dans ce cas, il s'agit d'un UDTF, puisque c'est l'interface de tous les lanceurs. La valeur de --rname doit correspondre au nom dans le code.

En cours d'exécution

Pour exécuter l'AE en mode distant, l'exécutable est exécuté en tant que "serveur" Dans ce cas, il ne traite que les requêtes exécutées sur l'hôte. En général, les AE sont également lancés sur les SPU. Démarrer une instance sur l'hôte :

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)

Notez qu'une syntaxe différente est utilisée pour invoquer le lanceur de style de fonction de table. Il s'agit de la syntaxe utilisée pour appeler tout AE basé sur l'UDTF. Exécutez maintenant l'AE :

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

Enfin, provoquer une erreur :

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