C language scalar function
The following example shows a simple scalar function that sums a set of numbers. This example starts from the beginning to construct a simple AE. It uses the following file name: func.c
Code
- Pull in the C interface. This can typically be accomplished by
using:
#include "nzaeapis.h" - Write a main. Determine an API to use, in this case a function. Select either a local AE
connection or a remote AE connection. This example uses a local AE
connection:
#include <stdio.h> #include <stdlib.h> #include "nzaeapis.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; } nzaeClose(arg.handle); } else { fprintf(stderr, "Expecting Local AE\n"); return -1; } return 0; }This sets up
nzae, which enables the ability to work with AE functions. - Add a stub for calling the function logic:
#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 { fprintf(stderr, "Expecting Local AE\n"); return -1; } return 0; } static int run(NZAE_HANDLE h) { return 0; } - Implement the full logic:
#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 { fprintf(stderr, "Expecting Local AE\n"); return -1; } 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"); return -1; } if (metadata.inputColumnCount < 1) { nzaeUserError(h, "expecting at least one input column"); return -1; } if (metadata.inputTypes[0] != NZUDSUDX_FIXED && metadata.inputTypes[0] != NZUDSUDX_VARIABLE) { nzaeUserError(h, "first input column expected to be a string type"); 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"); 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"); 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); 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"); return -1; } } CHECK(nzaeSetOutputDouble(h, 0, result)); CHECK(nzaeOutputResult(h)); } nzaeDone(h); return 0; }When the code is complete, it must be compiled and registered.
Compilation
$NZ_EXPORT_DIR/ae/utilities/bin/compile_ae --language system --version 3 \
--template compile func.c --exe applyThe arguments specify the system language—C—using version 3 with the template compile. Additionally, they specify that the process should produce an executable apply.
Registration
$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --language system --version 3 \
--template udf --exe apply --sig "applyop_c(VARARGS)" --return "double"Running
SELECT applyop_c('+',1,2);
APPLYOP_C
-----------
3
(1 row)
nzaeUserError if it is
incorrect. The following example triggers an error:SELECT applyop_c('-',1,2);
ERROR: unexpected operator -