Función escalar en lenguaje C (modo remoto)
Este ejemplo utiliza el siguiente nombre de archivo:
func.c
Código
El código de este ejemplo muestra que, con pequeños cambios, este programa funciona en modo remoto:
El cambio está en lo esencial. Se establece el nombre de la conexión AE remota requerida, obtenida del lanzador, y luego se añade un bucle alrededor de la aceptación para que sea llamada cinco veces. Este código funciona como un EA local y maneja cinco iteraciones como un EA remoto.
#include <stdio.h>
#include <stdlib.h>
#include "nzaeapis.h"
static int run(NZAE_HANDLE h);
int main(int argc, char * argv[])
{
if (nzaeIsLocal())
{
NzaeInitialization arg;
memset(&arg, 0, sizeof(arg));
arg.ldkVersion = NZAE_LDK_VERSION;
if (nzaeInitialize(&arg))
{
fprintf(stderr, "initialization failed\n");
return -1;
}
run(arg.handle);
nzaeClose(arg.handle);
}
else {
NZAECONPT_HANDLE hConpt = nzaeconptCreate();
if (!hConpt)
{
fprintf(stderr, "error creating connection point\n");
fflush(stderr);
return -1;
}
const char * conPtName = nzaeRemprotGetRemoteName();
if (!conPtName)
{
fprintf(stderr, "error getting connection point name\n");
fflush(stderr);
exit(-1);
}
if (nzaeconptSetName(hConpt, conPtName))
{
fprintf(stderr, "error setting connection point name\n");
fflush(stderr);
nzaeconptClose(hConpt);
return -1;
}
NzaeremprotInitialization args;
memset(&args, 0, sizeof(args));
args.ldkVersion = NZAE_LDK_VERSION;
args.hConpt = hConpt;
if (nzaeRemprotCreateListener(&args))
{
fprintf(stderr, "unable to create listener - %s\n", \
args.errorMessage);
fflush(stderr);
nzaeconptClose(hConpt);
return -1;
}
NZAEREMPROT_HANDLE hRemprot = args.handle;
NzaeApi api;
int i;
for (i = 0; i < 5; i++)
{
if (nzaeRemprotAcceptApi(hRemprot, &api))
{
fprintf(stderr, "unable to accept API - %s\n", \
nzaeRemprotGetLastErrorText(hRemprot));
fflush(stderr);
nzaeconptClose(hConpt);
nzaeRemprotClose(hRemprot);
return -1;
}
if (api.apiType != NZAE_API_FUNCTION)
{
fprintf(stderr, "unexpected API returned\n");
fflush(stderr);
nzaeconptClose(hConpt);
nzaeRemprotClose(hRemprot);
return -1;
}
printf("testcapi: accepted a remote request\n");
fflush(stdout);
run(api.handle.function);
}
nzaeRemprotClose(hRemprot);
nzaeconptClose(hConpt);
}
return 0;
}
static int run(NZAE_HANDLE h)
{
NzaeMetadata metadata;
if (nzaeGetMetadata(h, &metadata))
{
fprintf(stderr, "get metadata failed\n");
return -1;
}
#define CHECK(value) \
{ \
NzaeRcCode rc = value; \
if (rc) \
{ \
const char * format = "%s in %s at %d"; \
fprintf(stderr, format, \
nzaeGetLastErrorText(h), __FILE__, __LINE__); \
nzaeUserError(h, format, \
nzaeGetLastErrorText(h), __FILE__, __LINE__); \
exit(-1); \
} \
}
if (metadata.outputColumnCount != 1 ||
metadata.outputTypes[0] != NZUDSUDX_DOUBLE)
{
nzaeUserError(h, "expecting one output column of type double");
nzaeDone(h);
return -1;
}
if (metadata.inputColumnCount < 1)
{
nzaeUserError(h, "expecting at least one input column");
nzaeDone(h);
return -1;
}
if (metadata.inputTypes[0] != NZUDSUDX_FIXED
&& metadata.inputTypes[0] != NZUDSUDX_VARIABLE)
{
nzaeUserError(h, "first input column expected to be a string type");
nzaeDone(h);
return -1;
}
for (;;)
{
int i;
double result = 0;
NzaeRcCode rc = nzaeGetNext(h);
if (rc == NZAE_RC_END)
{
break;
}
NzudsData * input = NULL;
CHECK(nzaeGetInputColumn(h, 0, &input));
const char * opString;
if (input->isNull)
{
nzaeUserError(h, "first input column may not be null");
nzaeDone(h);
return -1;
}
if (input->type == NZUDSUDX_FIXED)
{
opString = input->data.pFixedString;
} else if (input->type == NZUDSUDX_VARIABLE)
{
opString = input->data.pVariableString;
} else
{
nzaeUserError(h, "first input column expected to be a string type");
nzaeDone(h);
return -1;
}
enum OperatorEnum
{
OP_ADD = 1,
OP_MULT = 2
} op;
if (strcmp(opString, "+") == 0)
{
op = OP_ADD;
} else if (strcmp(opString, "*") == 0)
{
op = OP_MULT;
result = 1;
} else
{
nzaeUserError(h, "unexpected operator %s", opString);
nzaeDone(h);
return -1;
}
for (i = 1; i < metadata.inputColumnCount; i++)
{
CHECK(nzaeGetInputColumn(h, i, &input));
if (input->isNull)
{
continue;
}
switch (op)
{
case OP_ADD:
switch(input->type)
{
case NZUDSUDX_INT8:
result += *input->data.pInt8;
break;
case NZUDSUDX_INT16:
result += *input->data.pInt16;
break;
case NZUDSUDX_INT32:
result += *input->data.pInt32;
break;
case NZUDSUDX_INT64:
result += *input->data.pInt64;
break;
case NZUDSUDX_FLOAT:
result += *input->data.pFloat;
break;
case NZUDSUDX_DOUBLE:
result += *input->data.pDouble;
break;
case NZUDSUDX_NUMERIC32:
case NZUDSUDX_NUMERIC64:
case NZUDSUDX_NUMERIC128:
// unlike Java, C has no native numeric data types
break;
default:
// ignore nonnumeric type
break;
}
break;
case OP_MULT:
switch(input->type)
{
case NZUDSUDX_INT8:
result *= *input->data.pInt8;
break;
case NZUDSUDX_INT16:
result *= *input->data.pInt16;
break;
case NZUDSUDX_INT32:
result *= *input->data.pInt32;
break;
case NZUDSUDX_INT64:
result *= *input->data.pInt64;
break;
case NZUDSUDX_FLOAT:
result *= *input->data.pFloat;
break;
case NZUDSUDX_DOUBLE:
result *= *input->data.pDouble;
break;
case NZUDSUDX_NUMERIC32:
case NZUDSUDX_NUMERIC64:
case NZUDSUDX_NUMERIC128:
// unlike Java, C has no native numeric data types
break;
default:
// ignore non-numeric type
break;
}
break;
default:
nzaeUserError(h, "internal error, unexpected operator");
nzaeDone(h);
return -1;
}
}
CHECK(nzaeSetOutputDouble(h, 0, result));
CHECK(nzaeOutputResult(h));
}
nzaeDone(h);
return 0;
}
Una compilación
Utiliza la compilación estándar, como en el modo local:
$NZ_EXPORT_DIR/ae/utilities/bin/compile_ae --language system --version 3 \
--template compile func.c --exe apply
Registro
El registro es ligeramente diferente. Primero, ejecuta esto para registrarte:
$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --language system --version 3 \
--template udf --exe apply --sig "rem_applyop_c(VARARGS)" \
--return "double" --remote --rname testcapi
A continuación, ejecute lo siguiente. Especifica que el código se registre como un AE remoto con un nombre remoto de testcapi, que coincide con el código. Además, registra un lanzador:
$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --language system --version 3 \
--template udtf --exe apply --sig "rem_applyop_launch(int8)" \
--return "TABLE(aeresult varchar(255))" --remote --rname testcapi --launch
A excepción de --rname, --exe y la parte del nombre de --sig, todos los lanzadores se parecen a los anteriores. En este caso se trata de una UDTF, ya que es la interfaz de todos los lanzadores. El valor de --rname debe coincidir con el nombre del código.
En ejecución
Para ejecutar el AE en modo remoto, el ejecutable se ejecuta como "servidor" En este caso, sólo gestiona las consultas ejecutadas en el host. Normalmente, los EA se inician también en las SPU. Iniciar una instancia en el host:
SELECT * FROM TABLE WITH FINAL(rem_applyop_launch(0));
AERESULT
---------------------------------------------------------------------------------
--
tran: 7788 session: 16354 DATA slc: 0 hardware: 0 machine: bdrosendev process:
13117 thread: 13117
(1 row)
Observe que se utiliza una sintaxis diferente para invocar el lanzador de estilo de función de tabla. Esta es la sintaxis utilizada para llamar a cualquier AE basado en UDTF. Ahora ejecute el AE:
SELECT rem_applyop_c('+',1,2);
REM_APPLYOP_C
---------------
3
(1 row)
Por último, provocar un error:
SELECT rem_applyop_c(1);
ERROR: first input COLUMN expected TO be a string type