Función escalar en lenguaje C
El siguiente ejemplo muestra una función escalar simple que suma un conjunto de números. Este ejemplo parte del principio para construir un AE sencillo. Utiliza el siguiente nombre de archivo: ' applyopcpp.cpp
Código
- Tira de la interfaz C++. Esto puede lograrse normalmente utilizando:
#include <nzaefactory.h> - Escribe una principal. Determina una API a utilizar, en este caso una función. Esto puede conseguirse mediante una conexión AE local o una conexión AE remota. Este ejemplo utiliza una conexión AE local. Se pueden utilizar dos interfaces. Uno utiliza "
NzaeFactory" y el otro "NzaeApiGenerator. En este ejemplo, utilice la clase generador, ya que es más simple y también se puede utilizar para el modo remoto. El programa debe aparecer similar a:
Esto recupera una referencia 'include <nzaefactory.hpp> using namespace nz::ae; int main(int argc, char * argv[]) { NzaeApiGenerator helper; NzaeApi &api = helper.getApi(NzaeApi::FUNCTION); return 0; }NzaeApi' que contiene un objeto 'NzaeFunction' utilizable. - Cuando la función esté disponible, añada un stub para llamar a la lógica de la función:
#include <nzaefactory.hpp> using namespace nz::ae; static int run(nz::ae::NzaeFunction *aeFunc); int main(int argc, char * argv[]) { NzaeApiGenerator helper; NzaeApi &api = helper.getApi(NzaeApi::FUNCTION); run(api.aeFunction); return 0; } static int run(NzaeFunction *aeFunc) { return 0; } - Implementa la función.
Hay dos formas de aplicar una función. Utiliza directamente la interfaz del objeto '
NzaeFunction' o implementa una clase derivada de 'NzaeFunctionMessageHandler' que proporcione una interfaz más sencilla. En este ejemplo, se utiliza el manejador de mensajes. El segundo método se trata en otro ejemplo. El gestor de mensajes sólo puede utilizarse para funciones que devuelven una fila de salida por entrada. Además, el manejador cubre algunos detalles del manejo de errores automáticamente.#include <nzaefactory.hpp> using namespace nz::ae; static int run(nz::ae::NzaeFunction *aeFunc); int main(int argc, char * argv[]) { NzaeApiGenerator helper; NzaeApi &api = helper.getApi(NzaeApi::FUNCTION); run(api.aeFunction); return 0; } class MyHandler : public NzaeFunctionMessageHandler { public: enum OperatorEnum { OP_ADD = 1, OP_MULT = 2 } op; void evaluate(NzaeFunction& api, NzaeRecord &input, NzaeRecord &result) { 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_INT32: { NzaeInt32Field &sf = (NzaeInt32Field&)input.get(i); temp = (int32_t)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; } }; static int run(NzaeFunction *aeFunc) { aeFunc->run(new MyHandler()); return 0; }En este ejemplo, la función de ejecución instancia el objeto manejador, y luego invoca el método de ejecución de la clase de función en el manejador. El manejador debe implementar un método evaluate, que toma una referencia a la función, así como referencias al registro de entrada y al registro de salida. Para las funciones normales, hay una sola columna en el registro de salida. El evaluador puede recuperar los campos del registro de entrada y utilizarlos para definir el campo de salida. Este código proporciona una función básica, sin comprobación de errores, que debe tomar como primer argumento el signo más (+) o el asterisco (*) y como argumentos posteriores int32s. A continuación, suma o multiplica int32s, en función del valor del argumento especificado, y devuelve el resultado como un double.
- El último paso de preparación del código añade la comprobación de errores y la compatibilidad con tipos de datos adicionales.
#include <nzaefactory.hpp> using namespace nz::ae; static int run(nz::ae::NzaeFunction *aeFunc); int main(int argc, char * argv[]) { NzaeApiGenerator helper; NzaeApi &api = helper.getApi(NzaeApi::FUNCTION); run(api.aeFunction); 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; }Este código garantiza el número y tipo correctos de argumentos de entrada y el número y tipo correctos de argumentos de salida. Soporta todos los tipos de enteros que son float, double y numeric. Una vez completado el código, hay que compilarlo y registrarlo.
Una compilación
$NZ_EXPORT_DIR/ae/utilities/bin/compile_ae --language cpp --template compile \
--exe applyopcpp --compargs "-g -Wall" --linkargs "-g" applyopcpp.cpp \
--version 3Los argumentos especifican que se está utilizando el lenguaje C++, versión 3, con la plantilla compilar, y creando el ejecutable ' applyopcpp. Además, el compilador y el enlazador utilizan el argumento adicional proporcionado para que el ejecutable ' applyopcpp ' tenga símbolos de depuración.
Registro
$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --sig "applyop_cpp(VARARGS)" \
--return "double" --language cpp --template udf --exe applyopcpp \
--version 3En ejecución
SELECT applyop_cpp('+',1,2);
APPLYOP_CPP
-------------
3
(1 row)
doOnce y se envía un error mediante ' NzaeException. El siguiente ejemplo provoca un error:SELECT applyop_cpp('-',1,2);
ERROR: unexpected operator -