SAND CDBMS Tools Reference Guide
Appendix A:
The User-Defined Function Library
 

This Appendix provides information about creating user-defined functions for use with NDL scripts. Note that all function libraries must be compiled in C or C++. Users should therefore have some familiarity with the C/C++ programming languages before attempting to develop their own libraries.

The examples in this section are intended for Windows platforms. The source code is generally applicable to UNIX as well, but UNIX developers should consult the system documentation for platform- and compiler-specific information regarding the creation of C run-time libraries.

Refer to the Custom Library Functions section of the NDL++ guide for details on how to reference defined libraries in an load specification script.


go to:
Definitions and Structures
Defining an Entry Point in Microsoft Visual C++
The LibInit() and LibDone() Functions
User Function Header File Example and Considerations

 

Definitions and Structures

All user functions should have the following definitions:

// User function calling convention
#define USERFUNC_API extern "C" __ (dllexport) int __cdecl

// User function return values
enum
{
    expOk = 0,
    expNullViolation = 3,
    expDivideByZero = 4,
    expOutOfRange = 5,
    expGeneralError = 6,
};

// User function argument type
struct CEVal
{
    struct mem_t
    {
        void *ptr;
        int len;
    };

    union value_t
    {
        long i;
        double f;
        mem_t s;
    };

    int type;
    value_t v;
};

All arguments received or sent by a user function will be of the CEVal structure type defined above.


Defining an Entry Point in Microsoft Visual C++

A dynamic link library (DLL) defined in Microsoft Visual C++ should have the following standard entry point:

BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


The user functions should be coded using the calling convention defined by the USERFUNC_API definition found above. The following is the implementation of the StrCat function:

USERFUNC_API StrCat( CEVal* pvres, CEVal* pv1, CEVal* pv2 )
{
    memcpy( pvres->v.s.ptr, pv1->v.s.ptr, pv1->v.s.len );
    memcpy( (char *)pvres->v.s.ptr + pv1->v.s.len, pv2->v.s.ptr, pv2->v.s.len );
    pvres->v.s.len = pv1->v.s.len + pv2->v.s.len;
    return expOk;
}


Developer Notes:


The LibInit() and LibDone() Functions

The DLL you create can contain an initialization function that will be called before input rows are processed. This function must be called LibInit. If a function with this name exists in the DLL, it will be called by the loader.

In the same fashion, a function can be called to execute some code after all of the rows have been processed. This function must be called LibDone.

The functions should have the following structures:

USERFUNC_API LibInit()
{
    ... // initialization code
    return 0;
}

USERFUNC_API LibDone()
{
    ... // post-processing code here
    return 0;
}

The next example is an NDL script that uses user functions to translate packed decimals to string and floating point values:

IMPORT @C:\work\data\packed.txt
{
library @C:\work\bin\PackDec.dll
{
char PTOS( char, int, char )
float PTOF( char, int, char )
}

record
{
FLD1 4\n
}

packed_test
{
STRING_FMT PTOS(FLD1, 0, 'NONE')
FLOAT_FMT PTOF(FLD1, 0, 'NONE')
}
}

The user functions PTOS() and PTOF() translate packed decimal into either a string format (PTOS) or floating point value (PTOF). The second argument indicates the precision of the input packed decimal. The third value indicates if the input must be translated from ASCII to EBCDIC (A2E) or from EBCDIC to ASCII before processing the input (E2A). No translation will be done on the input string if a value other than "A2E" or "E2A" is supplied as the third argument.

The following code is the implementations of the packed decimal functions (note that the array of numbers at the beginning is used to translate from ASCII to EBCDIC):

// UserFunc.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <stdio.h>
#include "UserFunc.h"

#define NO_CONV 0
#define A2E_CONV 1
#define E2A_CONV 2

#define A2E_TAG "A2E"
#define E2A_TAG "E2A"

/*************************************************************/
/* CONVERSION TABLES FOR ASCII TO EBCDIC */
/*************************************************************/

static unsigned char a2e[] =
{
  0,   1,   2,   3,  55,  45,  46,  47,  22,   5,  37,  11,  12,  13,  14,  15,
 16,  17,  18,  19,  60,  61,  50,  38,  24,  25,  28,  39,   7,  29,  30,  31,
 64,  90, 127, 123,  91, 108,  80, 125,  77,  93,  92,  78, 107,  96,  75,  97,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 122,  94,  76, 126, 110, 111,
124, 193, 194, 195, 196, 197, 198, 199, 200, 201, 209, 210, 211, 212, 213, 214,
215, 216, 217, 226, 227, 228, 229, 230, 231, 232, 233, 186, 224, 187, 176, 109,
121, 129, 130, 131, 132, 133, 134, 135, 136, 137, 145, 146, 147, 148, 149, 150,
151, 152, 153, 162, 163, 164, 165, 166, 167, 168, 169, 192,  79, 208, 161,  63,
104, 220,  81,  66,  67,  68,  71,  72,  82,  83,  84,  87,  86,  88,  99, 103,
113, 156, 158, 203, 204, 205, 219, 221, 223, 236, 252, 112, 177, 128, 191, 255,
 69,  85, 206, 222,  73, 105, 154, 155, 171, 175,  95, 184, 183, 170, 138, 139,
 43,  44,   9,  33,  40, 101,  98, 100, 180,  56,  49,  52,  51,  74, 178,  36,
 34,  23,  41,   6,  32,  42,  70, 102,  26,  53,   8,  57,  54,  48,  58, 159,
140, 172, 114, 115, 116,  10, 117, 118, 119,  35,  21,  20,   4, 106, 120,  59,
238,  89, 235, 237, 207, 239, 160, 142, 174, 254, 251, 253, 141, 173, 188, 190,
202, 143,  27, 185, 182, 181, 225, 157, 144, 189, 179, 218, 250, 234,  62,  65

};

static unsigned char e2a[] =
{
  0,   1,   2,   3, 220,   9, 195,  28, 202, 178, 213,  11,  12,  13,  14,  15,
 16,  17,  18,  19, 219, 218,   8, 193,  24,  25, 200, 242,  26,  29,  30,  31,
196, 179, 192, 217, 191,  10,  23,  27, 180, 194, 197, 176, 177,   5,   6,   7,
205, 186,  22, 188, 187, 201, 204,   4, 185, 203, 206, 223,  20,  21, 254, 127,
 32, 255, 131, 132, 133, 160, 198, 134, 135, 164, 189,  46,  60,  40,  43, 124,
 38, 130, 136, 137, 138, 161, 140, 139, 141, 225,  33,  36,  42,  41,  59, 170,
 45,  47, 182, 142, 183, 181, 199, 143, 128, 165, 221,  44,  37,  95,  62,  63,
155, 144, 210, 211, 212, 214, 215, 216, 222,  96,  58,  35,  64,  39,  61,  34,
157,  97,  98,  99, 100, 101, 102, 103, 104, 105, 174, 175, 208, 236, 231, 241,
248, 106, 107, 108, 109, 110, 111, 112, 113, 114, 166, 167, 145, 247, 146, 207,
230, 126, 115, 116, 117, 118, 119, 120, 121, 122, 173, 168, 209, 237, 232, 169,
 94, 156, 190, 250, 184, 245, 244, 172, 171, 243,  91,  93, 238, 249, 239, 158,
123,  65,  66,  67,  68,  69,  70,  71,  72,  73, 240, 147, 148, 149, 162, 228,
125,  74,  75,  76,  77,  78,  79,  80,  81,  82, 251, 150, 129, 151, 163, 152,
 92, 246,  83,  84,  85,  86,  87,  88,  89,  90, 253, 226, 153, 227, 224, 229,
 48,  49,  50,  51,  52,  53,  54,  55,  56,  57, 252, 234, 154, 235, 233, 159

};


/***************************************************************/
/* L O C A L F U N C T I O N S */
/***************************************************************/

#define ISPACKNEG(PACKED_DEC, PACKED_SIZE) \ (PACKED_DEC[(PACKED_SIZE / 2) - 1] & 0x0f ) == 0x0d

/*
* Extracts a decimal from a packed decimal byte.
* Can perform a conversion from
* ASCII to EBCDIC or EBCDIC to ASCII on the received byte
*/
static int prv_extract_decimal( char *data, int pos, int conv_opt )
{
char packed_number;

switch(conv_opt)
{
case A2E_CONV:
   packed_number = a2e[data[pos / 2]];
   break;
case E2A_CONV:
   packed_number = e2a[data[pos / 2]];
   break;
default:
   packed_number = data[pos / 2];
   break;
}

if ( (pos % 2) == 0 )
   packed_number = packed_number >> 4;

packed_number &= 0x0f;

return((int) packed_number);

}


/* Analyzes the received option to check if a conversion from
* ASCII to EBCDIC or EBCDIC to ASCII must be performed on
* the input before extracting the packed decimal */


static int prv_chk_cnv_opt(CEVal* option)
{
    int conv_opt = NO_CONV;
    if (option->v.s.len == (int)strlen(A2E_TAG) &&
        memcmp(option->v.s.ptr, A2E_TAG, strlen(A2E_TAG)) == 0)
    {
        conv_opt = A2E_CONV;
    }
    else if (option->v.s.len == (int)strlen(E2A_TAG) &&
        memcmp(option->v.s.ptr, E2A_TAG, strlen(E2A_TAG)) == 0)
    {
        conv_opt = E2A_CONV;
    }

    return(conv_opt);
}


/*************************************************************/
/* P U B L I C F U N C T I O N S */
/*************************************************************/

BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
    break;
    }

    return TRUE;
}


USERFUNC_API LibInit( )
{
    // No initialization code; this function is optional

    return 0;
}


USERFUNC_API LibDone( )
{
    // No post-processing code; this function is optional

    return 0;
}


/* Converts a packed decimal format to a string format.
*  "A2E" performs an ASCII to EBCDIC conversion
*  "E2A" performs an EBCDIC to ASCII conversion
*/
USERFUNC_API PTOS( CEVal* pvres, CEVal* data, CEVal* precision, CEVal* option )
{
    int incr, jncr;
    long int_len;
    int number;
    int decimals;
    int conv_opt;
    bool start = false;
    bool dot = false;
    char * result = (char *)pvres->v.s.ptr;
    char * pack_dec = (char *)data->v.s.ptr;
    int pack_size = data->v.s.len * 2;
    conv_opt = prv_chk_cnv_opt(option);
    jncr = 0;
    if ( ISPACKNEG(pack_dec, pack_size) )
/* if negative number */
        result[jncr++] = '-';

    int_len = pack_size - precision->v.i - 1;
    for ( incr = 0; incr < int_len; incr++ )
/* interger part of the packed decimal */
    {
        number = prv_extract_decimal(pack_dec, incr, conv_opt);

        if ( number != 0 || start || incr == int_len - 1 )
        {
            result[jncr++] = number + 48;
            start = true;
        }
    }

    decimals = 0;

    if ( precision->v.i != 0 )
/* decimal part of the packed decimal */
    {
        start = false;
        for ( incr = pack_size - 2; incr >= int_len; incr-- )
        {
            number = prv_extract_decimal(pack_dec, incr, conv_opt);

            if ( number != 0 || start )
            {
                if ( !dot )
                {
                    result[jncr++] = '.';
                    dot = true;
                }

                result[jncr + (incr - int_len) ] = number + 48;
                decimals++;
                start = true;
            }
        }
    }

    pvres->v.s.len = jncr + decimals;

    return expOk;
}


/* Converts a packed decimal format to a float format.
*  A conversion can be performed on the packed decimal
*  before the extraction by setting the * option to:
*  "A2E" performs an ASCII to EBCDIC conversion
*  "E2A" performs an EBCDIC to ASCII conversion
*/

USERFUNC_API PTOF( CEVal* pvres, CEVal* data, CEVal* precision, CEVal* option )
{
    int incr;
    int int_len;
    int dec_end;
    int base = 10;
    int conv_opt;
    char * pack_dec = (char *)data->v.s.ptr;
    int pack_size = data->v.s.len * 2;

    conv_opt = prv_chk_cnv_opt(option);

    pvres->v.f = 0.0;

    int_len = pack_size - precision->v.i - 1;

    for ( incr = 0; incr < int_len; incr++ )
    {
        pvres->v.f *= 10.0;
        pvres->v.f += (int)prv_extract_decimal(pack_dec, incr,
        conv_opt);
    }

    if ( precision->v.i != 0 )
/* decimal part of the packed decimal */
    {
        dec_end = pack_size - 2;
        for ( ; incr < dec_end; incr++ )
        {
            pvres->v.f += (float)prv_extract_decimal(pack_dec, incr, conv_opt) / (float)base;

            base *= 10;
        }
    }

    if ( ISPACKNEG(pack_dec, pack_size) )
/* if negative number */
        pvres->v.f *= -1.0;

    return expOk;
}


User Function Header File Example and Considerations

The UserFunc.h header file would contain the following:

/////////////////////////////////////////////////////////////////

#define USERFUNC_API extern "C" __declspec(dllexport) int __cdecl

/////////////////////////////////////////////////////////////////

enum
{
    expOk = 0,
    expNullViolation = 3,
    expDivideByZero = 4,
    expOutOfRange = 5,
    expGeneralError = 6,
};


struct CEVal
{
    struct mem_t
    {
        void *ptr;
        int len;
    };

    union value_t
    {
        long i;
        double f;
        mem_t s;
    };

    int type;
    value_t v;
};

USERFUNC_API LibInit( );
USERFUNC_API LibDone( );

USERFUNC_API PTOS( CEVal* pvres,
                   CEVal* data,
                   CEVal* precision,
                   CEVal* option );

USERFUNC_API PTOF( CEVal* pvres,
                   CEVal* data,
                   CEVal* precision,
                   CEVal* option );