NOTE: | If you are using PSM mechanism to create User Defined Functions, you may skip the rest of the chapters of this document. Defining UDF at the kernel level interface mechanism was provided since 1983, way before the existence of the Standard. For those users who are customed to use this interface and for the new users who are interested in the internal implementations of the Persistent Stored Modules may proceed with the rest of the document. |
When a Query Language command is sent to Empress, the input is parsed and actions taken based on the keywords it contains. Empress allows you to add keywords to invoke your own C routines to perform these specialized operations.
Empress keeps a list of user defined keywords in user keyword tables. When an input line is parsed, the user keyword tables are searched in an attempt to identify the keyword. If the tables contain an entry for the keyword, the input line is parsed according to the syntax set for that keyword.
Empress allows User Defined Functions to be used in several places:
In the Empress SQL they can be used in:
Their usage is the same as Build-In Functions in Empress.
In the Empress 4GL they can be used in:
The usage is the same as Build-In Functions and called 4GL procedures in Empress.
In Empress Report Writer, they can be used in:
Their usage is the same as Build-In Functions in Empress.
In Empress Hypermedia - Internet Application Toolkit, they can be used in:
Their usage is the same as Build-In Functions in Empress.
User Defined Functions can also be used in C programs through expressions built with mr expression routines. (See Empress Host Language: C Kernel Level Interface mr Routines).
There are two classes of User Defined Functions: Data Type (DT) and Standard C (SC). The Standard C User Defined Functions are called using an argc, argv convention. This class is created for the 4GL. For the Persistent Stored Modules, the parameters are used and always interface to the Data Type UDF mechanism.
The Standard C mechanism is always slower than the DT mechanism because the argc, argv interface is always ASCII; everything is converted to ASCII on calling the UDF and the UDF has to return ASCII which is re-converted to whatever data type it's supposed to be. The DT mechanism only converts if necessary.
Each class has its own Keyword Table and User Function Table. SC functions have higher priority than DT functions. A keyword that invokes a User Defined Function may contain entries for both DT and SC functions; but if so only the SC function will be invoked to evaluate the function result. The priority of DT and SC functions will be described in more detail in Priority of User Defined Functions.
User Defined Functions can operate on either tuples or aggregates. Simple tuple functions can be defined as DT or SC functions, but aggregate functions may only be defined as DT type functions.
In general, User Defined Functions of the simple tuple type can be defined either through the DT interface, or the SC interface. There are several more steps required to define DT type functions than SC functions. As well, there are more structures that must be referenced, and the level of difficulty in coding DT functions is greater than with SC functions. However, there are some significant advantages in writing DT type functions. Execution time is faster for DT type functions and there is much greater flexibility in the handling of null values and errors. Of great significance to the applications programmer is the fact that DT type functions allow for a much finer control over the data types of both the function's input and output.
Three source files and a header file are provided with Empress distributions which make User Defined Functions easy to define. They are located in the Empress directory ($EMPRESSPATH) under rdbms/custom/src/usrfns and under include directories.
The source files under custom/src/usrfns directory are:
usrfns.c | is used to define DT type User Defined Functions. | |||
usrstdfn.c | is used to define SC type User Defined Functions. | |||
usrtabs.c | contains the tables which determine the priority of DT, SC and system math functions. Normally, this file does not need to be modified, but it is possible to do so. |
The include (.h) file under include directory is:
usrfns.h | contains User Keyword Table and User Function Table structure definitions. |
Empress keeps a list of user defined keywords in user keyword tables. The user keyword tables are listed in the table usrktabs in the file usrtabs.c. When an input line is parsed, the user keyword tables are searched in an attempt to identify the keyword. If one of the tables contains an entry for the keyword, the search stops at the first instance of the keyword and based on the grammar rules for that keyword, further actions can then be taken.
The default search order of keyword tables in usrktabs is:
If the keyword is not encountered in any of these tables, the Empress internal keyword tables are then searched. The default search order (for user keyword tables) can be changed simply by changing the order of the entries in usrktabs.
If a keyword is located in one of the user keyword tables, the user function tables are then searched to locate the function entry. The default search order of user function tables is:
Entries in usrsftabs and usrxftabs may be modified and new entries may be added. The SC function tables have priority over DT function tables. This default priority can be changed by changing the order of entries in the table usrktabs in the file usrtabs.c.
Note that Empress keywords which are used to invoke functions may be redefined by creating an entry in one of the user keyword tables with the same name as the Empress keyword. All of the Empress keywords are listed in the file include/parseql.h. This file should be searched to ensure that a user defined keyword will not override an existing Empress keyword.
The relationship between the keyword tables and function tables is described in the following diagram:
Figure 3-1 Relationship between the Keyword Tables and the Function Tables
There are several structures which Empress references when an SC UDF is invoked. These structures are defined in the file usrfns.h in the include directory. The purpose of these structures is summarized in the following table:
Table 3-1: SC User Defined Function Structures
Structure | Usage |
keyitem | Entries in the User Keyword Table (in array usrsktab for Standard C functions). |
dtstdfn | Entries in the User Function Table (in array usrstab). |
The structures are listed and briefly described below:
The keyitem structure defines entries for keywords added to Empress. typedef struct { char *keyname; /* keyword name */ int keytag; /* grammar rule for parsing the keyword */ } keyitem; The dtstdfn structure defines entries for function definitions. typedef struct { char *fnName; /* function/operator name */ char *(*fnFunc) (); /* procedure */ char fnType;; /* return type */ int fnArgs; /* number of arguments */ } dtstdfn;
Schematically, the structures are related in the following way:
Figure 3-2 Relationship of the structure tables of the SC Type User Defined Function
The following sections illustrate the building of SC type UDFs. Each step of the procedure will be explained in detail. Argument and result value handling and returning error messages and null values will be discussed.
The example program below performs a factorial calculation. It accepts a single integer argument and returns a long integer result value. The procedure shows how a simple standalone program can be modified to work as a UDF.
# include <stdio.h> main(argc, argv) int argc; char *argv[]; { int num; num = atoi(argv[1]); num = num * factor(num -1); printf("factorial is %d\n", num); } long factor(n) int n; { if (n > 1) n = n * fact(n - 1); return(n); }
To create an SC type UDF, invoke system editor to edit usrstdfn.c under custom/src/usrfns directory and do the he following steps:
This entry specifies the grammar rules for the keyword, and in the case of an operator, precedence and number of operands.
Null values and customized error messages can be returned from the UDF.
SC type functions are automatically available to Empress SQL, Empress Report Writer and to C programs containing mr routines in Empress. SC functions can be made available in Empress 4GL. In the 4GL, SC functions can be used everywhere that 4GL functions can be used, except in call statements.
The first step in defining an SC type UDF is to establish the name of the keyword which will invoke the function. This keyword may be a new name, or it may override an existing system keyword. In general, it is not recommended that system keywords be redefined unless the programmer is confident of the actions being taken.
An entry in the User Keyword Table is defined by the C structure keyitem in usrfns.h include file:
typedef struct { char *keyname; /* the name of the function/operator */ int keytag; /* specifies a grammar rule */ } keyitem;
For each keyword, an entry must be made in the table usrsktab in the file usrstdfn.c. An entry consists of two parts:
The keyword (keyname) to be added.
The name of the keyword must be entered as a quoted string, and the entries in the Keyword Table must be in ascending ASCII collating order.
The keytag (keytag) specification for the function or operator.
The keytag is a code that specifies a grammar rule for evaluating the keyword. The rule determines the following:
FUNCTION (arg1, arg2, ... , argn)
Operators are invoked as:
arg1 OPERATOR arg2 arg OPERATOR OPERATOR arg
The valid keytags are in the file parseql.h. This file should never be modified by the user. Please refer to Table 3-1 Keytag Specification in this chapter.
The following is an example of the SC User Keyword Table for the factorial calculation.
keyitem usrsktab[] = { { "!", op1_xp }, /* factorial operator */ { (char *)0 } };
It indicates that ! will be used as keyword for this function. For example, in SQL:
SELECT (attr_name)! FROM table_name; SELECT * FROM table_name WHERE (attr_name)! > 2001;
There must be one entry in the usrsktab table for each keyword that is added. The last entry in the table must be null, i.e., (char *)0.
The next step in creating an Standard C UDF is to make an entry in the User Function Table usrstab in the file usrstdfn.c.
An entry in the User Function Table is defined by the C structure dtstdfn in usrfns.h include file:
typedef struct { char *fnName; /* function/operator name */ char *(*fnFunc) (); /* procedure */ char fnType;; /* return type */ int fnArgs; /* number of arguments */ } dtstdfn;
Entries in this table consists of four parts:
It is entered as a quoted string and corresponds to the keyname in the User Keyword Table usrsktab.
When specified in the User Function Table, this character value must be quoted using single quot ('). The program from which the UDF was invoked will convert the result value of the function to the appropriate return data type. The following are valid return type:
q | character |
z | date |
d | decimal |
f | float |
i | integer |
v | null |
If the function can accept a variable number of arguments, this should be set to -1. Note that user defined operators are described at this point in the same way as user defined functions. For unary operators, the number 1 should be entered in the User Function Table to indicate the number of expected operands, and for binary operators the number should be 2.
An example of this table containing entries for the functions whose keywords were defined above is:
dtstdfn usrstab[] = { { "!", fact, 'i', 1 }, /* factorial operator*/ (char *) 0 };
Through this table, Empress will invoke a routine named fact in association with the keyword "!". It expects one argument. Since it is a SC function, a conversion will occur if the input data is not a character string. Another conversion to the generic integer value will occur upon receiving the return value since the return type is indicated as an integer ('i').
There must be exactly one entry in the usrstab table for each SC function or operator that is added. The last entry in the table must be null (i.e., (char *)0).
To incorporate this routine into a UDF, the following should be noted:
argc is an integer containing the number of arguments passed to the function plus one for the function name itself. argv is a pointer to a list of strings comprising the arguments passed. The value of argv[0] is the name of the function; argv[1] is the first argument, argv[2] the second argument, and so on, up to argv[argc -1].
Regardless of the data types of the arguments passed to the function, they are converted to strings, so argv always references strings.
The following is an example for the factorial routine fact:
static char *fact (argc, argv) /* 1) char * declaration */ int argc; /* argc - argument count including the name of the function */ char *argv[]; /* argv - the arguments are stored in an array of character arrays */ { int num; static char result[30]; extern msbool cvai(); /* Empress routine to convert string to integer */ extern char *cvix(); /* Empress routine to convert integer to string */ if (!cvai (&num, argv[1])) /* 2) convert the argument to integer */ { dtudferr("fact: cannot convert argument to integer"); return (CHARNIL); /* if an error occurs return an appropriate error message */ } num = num * fact1(num -1); cvix(result, num); /* 3) convert the result to a string */ return (result); /* 4) return (result) to indicate */ /* successful completion of */ /* the function */ } static long fact1(n) int n; { if (n > 1) n = n * fact1(n - 1); return(n); }
The following is the complete listing of usrstdfn.c file for the above example:
/*********************************************************************** * (c) Copyright Empress Software Inc. 1983, 2003 ***********************************************************************/ /*********************************************************************** * WARNING: as of version 6.4, this file MUST be compiled with "empcc" * (not "cc") in order to find the correct header files. ***********************************************************************/ # include <usrfns.h> /****************************************************************************** * USRSKTAB * * Structure: Table structure * * Purpose: contains the name and parsing information for each * of the standard C user defined functions to be * used from the QL level. * Should be kept in ascending alphabetical order * according to function name. * ******************************************************************************/ keyitem usrsktab[] = { { "!", op1_xp }, /* factorial operator */ { (char *)0 } }; int usrzsktab = sizeof (usrsktab) / sizeof (keyitem) - 1; /****************************************************************************** * * Standard C User Defined Functions * - using argc, argv style arguments * ******************************************************************************/ /****************************************************************************** * FACTORIAL (!) * * Purpose: Illustrate the construction of a Standard C User * Defined Function. This function produces the factorial * of a LONGINTEGER argument. * * Arguments: argc - number of arguments (must be 2) * argv[0] or *argv - pointer to character string * containing the argument. * * Returns: pointer to the character string to be printed. * ******************************************************************************/ static char *fact (argc, argv) /* 1) char * declaration */ int argc; /* argc - argument count including the name of the function */ char *argv[]; /* argv - the arguments are stored in an array of character arrays */ { int num; static char result[30]; extern msbool cvai(); /* Empress routine to convert string to integer */ extern char *cvix(); /* Empress routine to convert integer to string */ if (!cvai (&num, argv[1])) /* 2) convert the argument to integer */ { dtudferr("fact: cannot convert argument to integer"); return (CHARNIL); /* if an error occurs return an appropriate error message */ } num = num * fact1(num -1); cvix(result, num); /* 3) convert the result to a string */ return (result); /* 4) return (result) to indicate */ /* successful completion of */ /* the function */ } static long fact1(n) int n; { if (n > 1) n = n * fact1(n - 1); return(n); } /****************************************************************************** * * The Standard C User Defined Functions tables and variables * ******************************************************************************/ dtstdfn usrstab[] = { { "!", fact, 'i', 1 }, /* factorial operator*/ (char *) 0 }; int usrzstab = sizeof (usrstab) / sizeof (dtstdfn) - 1;
The Standard C UDF interface supports variable number of arguments. The following example illustrates a function that can handle a variable number of arguments. This function will concatenate all the argument values into one string. To handle different numbers of arguments, the C routine can check the value of argc and perform different actions based on that value.
static char *attach(argc, argv) int argc; char *argv[]; { static char buffer[256]; int arg_num, position, arg_char; if (argc < 10) { position = 0; /* position in buffer */ arg_char = 0; /* character position of current argument */ for(arg_num = 1; arg_num < argc; arg_num++) /* for each argument copy the characters into a buffer - maximum size is 256 */ { while (( argv[arg_num][arg_char] != '\0') && (position < 256) ) { buffer[position] = argv[arg_num][arg_char]; position++; arg_char++; } arg_char = 0; } buffer[position] = '\0'; return(buffer); } else { dtudferr("attach: maximum of 8 arguments"); return (CHARNIL); } }
For a function to be able to handle a variable number of arguments, the entry in the User Function Table for the function must be set to -1 for the number of arguments.
The entry in the User Function Table for the attach function is as follows:
dtstdfn usrstab[] = { . . . { "attach", attach, 'q', -1 }, . . . (char *) 0 };
The procedure for returning error messages and null values is very simple.
As in the example for the fact function, to return an error message, the routine dtudferr() must be called, and the function must return CHARNIL. For example:
else { dtudferr("fact: cannot convert argument to integer"); return (CHARNIL); }
For every invocation of the function which generates this error condition, the following error message will be reported:
*** User Error *** fact: cannot convert argument to integer
To return a null value to the calling process, the return statement in the C routine should simply return the empty string as in the following branch of code.
else { return( "" ); }
Once the file usrstdfn.c has been modified to include the necessary entries for defining a UDF, the following steps must be taken to make the UDFs available to Empress.
For UNIX systems:
For Windows NT:
There are several structures which Empress references when a DT UDF is invoked. These structures are defined in the file usrfns.h in the include directory. The purpose of these structures is summarized in the following table:
Table 3-2 DT User Defined Function Structures
Structure | Usage |
keyitem | Entries in the User Keyword Table (in array usrxktab for DT functions). |
dtxdes | Entries in the User Function Table (in array usrxtab). |
dtxades | Entries in Function Specification Table (one array for each new function). |
dtxpdes | Entry in Function Procedures Table (list all five actual pointers which correspond to five separate procedures for each UDF). |
dtpar | Data type specification (for input arguments and result). |
dtxd | Input arguments/operands and result of function/operator. |
dtxr | Identifies the C routines and contains pointers to input arguments/operands and result. |
The structures are listed and briefly described below:
The keyitem structure defines entries for keywords added to Empress. typedef struct { char *keyname; /* keyword name */ int keytag; /* grammar rule for parsing the keyword */ } keyitem; The dtxdes structure defines entries for function definitions. typedef struct { char *dtxname; /* function/operator name */ int dtxnargs; /* number of arguments*/ dtxades *dtxpades; /* pointer to specification table */ int dtxinput; /* argument handling flags */ msbool dtxaval; /* aggregate function flag */ int *dtxargmodes; /* array of argument modes; NIL means all input only */ } dtxdes; The dtxades structure defines entries for the function I/O table. typedef struct { dtxpdes *dtxprocs; /* pointer to procedures tables */ void *dtxparam; /* parameter to procedures */ dtpar *dtxresult; /* result data type */ dtpar **dtxargs; /* argument data types */ } dtxades; The dtxpdes structure defines entries for Function Procedures Tables. typedef struct { msbool (*dtxifns)(); /* operator init procedure */ msbool (*dtxigrp)(); /* begin group procedure */ msbool (*dtxrfns)(); /* run operator procedure */ msbool (*dtxtgrp)(); /* end group procedure */ msbool (*dtxtfns)(); /* operator cleanup procedure */ } dtxpdes; The dtpar structure defines entries for data type identifiers. typedef struct { int dttypeid; /* type identifier */ int dtpar1, /* parameters */ dtpar2, dtpar3, dtpar4; void *dtpfptr; /* pointer for file operations */ } dtpar; The dtxr structure provides access to the function arguments or operands and the result of the function or operator. typedef struct { struct dtxr *dtxrnext; /* next operator */ dtxpdes *dtxrprocs; /* pointer to procedures */ msbool (*dtxrrun)(); /* duplicate pointer to rfns */ void *dtxrparam; /* parameter to procedures */ unsigned int dtxrnargs : BITSPERBYTE; /* number of arguments */ unsigned int dtxraval : 1; /* a-valued operator */ unsigned int dtxrdval : 1; /* delayed operator */ unsigned int dtxrnullok : 1; /* procedures handle nulls */ unsigned int dtxrerrok : 1; /* procedures handle errors */ unsigned int dtxrconvt : 1; /* conversion operator */ unsigned int dtxrufunc : 1; /* M-Builder type operator */ unsigned int dtxrifns : 1; /* was ifns run ? */ void *dtxrwksp; /* workspace */ dtxd *dtxrresult; /* the result */ dtxd *dtxds[1]; /* the operands */ /* space for operands */ } dtxr; The dtxd structure provides access to the function arguments or operands and contains other important information about the function arguments. typedef struct { dtpar *dtxddtp; /* the data type */ unsigned int dtxdnull : BITSPERBYTE; /* null (run-time) type */ unsigned int dtxdtype : BITSPERBYTE; /* operand (static) type */ unsigned int dtxdanydt : 1; /* opr will handle any dt */ unsigned int dtxdconsflag : 1; /* constant ? */ unsigned int dtxdcvar : 1; /* control var used ? */ unsigned int dtxdnucheck : 1; /* check for null ? */ unsigned int dtxdresult : 1; /* result operand ? */ unsigned int dtxdgsbuf : 1; /* generic string buffer ? */ unsigned int dtxdlinkcount : 2; /* link count */ void *dtxdnuval; /* the null value */ long dtxdzval; /* size of value */ void **dtxdival; /* indirect pointer to value */ void *dtxdval; /* pointer to value */ } dtxd;
Schematically, the structures are related in the following way:
Figure 3-3 Relationship of the structure tables of the DT Type User Defined Function
The following sections illustrate the building of DT type UDFs. Each step of the procedure will be explained in detail. At the end of this section, error and null value handling will be discussed.
The example program below performs a exponential calculation. It accepts a float argument and returns a float result value. The procedure shows how a simple standalone program can be modified to work as a UDF.
#include <math.h> #include <stdio.h> #include <stdlib.h> main (int argc, char** argv) { double x; double y; double res; if (argc != 3) { printf("USAGE: expon base power\n"); exit (0); }; sscanf (argv[1], "%lf", &x); sscanf (argv[2], "%lf", &y); res = pow (x,y); printf("%lf^%lf = %lf\n", x, y, res); }
To create a DT type UDF, invoke system editor to edit usrfns.c under custom/src/usrfns directory and do the following steps:
This entry specifies the grammar rules for the keyword, and in the case of an operator, precedence and number of operands.
Checking for null and error values can be handled either by Empress or explicitly by the user.
Null values and customized error messages can be returned from the UDF.
Additional routines and variables are available for performing data conversion and space allocation.
DT type functions are automatically available to Empress SQL, Empress 4GL and to C programs containing mr routines in Empress. In the 4GL, DT functions can be used everywhere that 4GL functions can be used, except in call statements.
The first step in defining a DT type UDF is to establish the name of the keyword which will invoke the function. This keyword may be a new name, or it may override an existing system keyword. In general it is not recommended that system keywords be redefined unless the programmer is confident of the actions being taken.
An entry in the User Keyword Table is defined by the C structure keyitem in usrfns.h include file:
typedef struct { char *keyname; /* the name of the function/operator */ int keytag; /* specifies a grammar rule */ } keyitem;
For each keyword, an entry must be made in the table usrxktab in the file usrfns.c. An entry consists of two parts:
The keyword (keyname) to be added.
The name of the keyword must be entered as a quoted string, and the entries in the Keyword Table must be in ascending ASCII collating order.
The keytag (keytag) specification for the function or operator.
The keytag is a code that specifies a grammar rule for evaluating the keyword. The rule determines the following:
FUNCTION (arg1, arg2, ... , argn)
Operators are invoked as:
arg1 OPERATOR arg2 arg OPERATOR OPERATOR arg
The valid keytags are listed in the file parseql.h. This file should never be modified by the user.
Table 3-3: Keytag Specifications
Keytag | Type | Arguments | Result | Also Specifies |
op2_b2 | binary operator | boolean | boolean | precedence 2 |
op2_b1 | binary operator | boolean | boolean | precedence 1 |
op1_b | unary operator | boolean | boolean | |
op2_c | binary operator | arithmetic | boolean | comparison |
op2_ceq | binary operator | arithmetic | boolean | equality comparison |
op2_x6 | binary operator | arithmetic | arithmetic | precedence 6 |
op2_x5 | binary operator | arithmetic | arithmetic | precedence 5 |
op2_x4 | binary operator | arithmetic | arithmetic | precedence 4 |
op2_x3 | binary operator | arithmetic | arithmetic | precedence 3 |
op2_x2 | binary operator | arithmetic | arithmetic | precedence 2 |
op2_x1 | binary operator | arithmetic | arithmetic | precedence 1 |
op2_xr | binary operator | arithmetic | arithmetic | right associative |
op1_xp | unary operator | arithmetic | arithmetic | postfix |
op1_x | unary operator | arithmetic | arithmetic | prefix |
opf_b | function | any # > 0 | boolean | |
opf0_b | function | none | boolean | |
opf_x | function | any # > 0 | arithmetic | |
opf0_x | function | none | arithmetic | |
opf_ax | aggregate function | any # > 0 | arithmetic |
The following is an example of the DT User Keyword Table containing keyword for the exponential function.
keyitem usrxktab[] = { { "**", op2_x1 }, /* binary exponential operator */ /* with precedence 1 */ { (char *)0 } }
The keyword list must be sorted in ascending ASCII collating order. Both tuple and aggregate functions are listed in this table. There must be one entry in the usrxktab table for each keyword that is added. The last entry in the table must be null, i.e., (char *)0.
The next step in creating a DT UDF is to make one or more entries in the User Function Table usrxtab in the file usrfns.c.
An entry in the User Function Table is defined by the C structure dtxdes in usrfns.h include file:
typedef struct { char *dtxname; /* the name of the function/operator */ int dtxnargs; /* # of arguments the function/operator takes */ dtxades *dtxpades; /* pointer to the function spec table */ int dtxinput; /* indicates whether the function handles nulls and/or errors as arguments */ msbool dtxaval; /* TRUE for an aggregate function */ /* FALSE if not an aggregate function */ int *dtxargmodes; /* array of argument modes; NIL means all input only */ } dtxdes;
Entries in this table consists of five parts:
It is entered as a quoted string. The name corresponds to the keyname in the User Keyword Table usrxktab.
Note that user defined operators are described at this point in the same way as user defined functions. For unary operators, the number 1 should be entered in the User Function Table to indicate the number of expected operands, and for binary operators the number should be 2.
There must be a unique Function I/O Table for each entry in the User Function Table.
The possible values for this field are:
NNEN | indicates "Null NO Error NO". This means the function will not handle null value and no error checking. |
NNEY | indicates "Null NO Error Yes". This means the function will not handle null value but does error handling. |
NYEN | indicates "Null Yes Error NO". This means the function will handle null value but no error checking. |
NYEY | indicates "Null Yes Error Yes". This means the function will handle null value and error checking. |
The example of this table for the exponential function whose keyword was defined above is:
dtxdes usrxtab[] = { { "**", 2, expontab, NNEN, false }, { (char *)0 } };
There must be at least one entry in the usrxtab table for each DT function or operator that is added. The last entry in the table must be null (i.e., (char *)0).
For each UDF that is added, it is necessary to create a Function I/O Table. If the function can be invoked with a variable number of arguments, there must be a separate Function I/O Table for each case.
An entry in the Function I/O Table is defined by the C structure dtxades in usrfns.h include file:
typedef struct { msbool (*dtxproc)(); /* the name of the function procedures table */ addr dtxparam; /* second parameter to the routine */ dtpar *dtxresult; /* data type of the result */ dtpa **dtxargs; /* data type of the arguments */ } dtxades;
Each entry in the Function I/O Table consists of four parts:
An example entry for the expon function could be made as follows:
dtxades expontab[] = { { & expontabproc[0], (addr)0, & dtgflt, gfltgflt }, { NULLENTRY } };
For the expon function, there must be two entries in the function I/O table, to handle the 2 cases of different argument data types (i.e., dist() can be invoked with either bulk or integer arguments). The result data type of the function (dtgint) is the same in either case.
The procedure for defining the data type identifiers for the arguments/results is described in the next section.
Data Type Identifiers describe the data types of the arguments/operands for the function or operator and the data type of the result value.
Data Type Identifiers are defined by the structure dtpar in usrfns.h include file:
typedef struct { int dttypeid; /* data type identifier */ int dtpar1, /* parameters */ dtpar2, dtpar3, dtpar4; addr dtpfptr; /* pointer for file operations */ /* no entry is required for this pointer */ } dtpar;
The definition of a Data Type Identifier for the function arguments is an array of pointers to individual Data Type Identifiers for each argument. The Data Type Identifier for each individual argument is a structure of type dtpar.
The definition of a Data Type Identifier for the function's result is simply a structure of type dtpar.
Data Type Identifiers may be used by more than one function I/O table, and the same identifier may be used for both argument and result values.
An entry for a Data Type Identifier consists of six parts:
Empress generic data types are defined as (the definitions are listed in usrfns.h include file):
Table 3-4 Empress Generic Data Types
Generic Data Type | Description |
DTGCHAR | is char *, pointer to a character string. |
DTGINTEGER | is long, a longinteger. |
DTGDECIMAL | is gen_dec, a structure containing character string. |
DTGFLOAT | is double, a C type double. |
DTGDATE | is gen_date, a structure containing a series longinteger values for year, month, day, hour, minute, second and microsecond. |
DTGBOOLEAN | is int, an integer. |
DTGEXTERNAL | is char *, pointer to a character string. |
DTGINTERVAL | is gen_date, a structure containing a series longinteger values for year, month, day, hour, minute, second and microsecond. |
DTGBINARY | is gen_binary, a structure containing a series binary segment structure, length and number of segments. |
Empress SQL data types are defined as (the definitions are listed in usrfns/generic.h include file):
Table 3-5 Empress SQL Data Types
Empress SQL Data Type | Description |
DTCHAR DTCHARACTER |
is char *, pointer to a character string. |
DTSHORTINT DTSMALLINT |
is short, a short integer. |
DTINTEGER DTINT |
is long, a long integer. |
DTLONGINT | is long, a long integer. |
DTDATE | is gen_date, a structure containing a series longinteger values for year, month, day, hour, minute, second and microsecond. |
DTDOLLAR | is gen_dec, a structure containing character string. |
DTFLOAT | is float, a floating point number. |
DTLONGFLOAT DTPERCFLOAT DTDBLPERCISION |
is double, a double precision floating point number. |
DTTEXT | is char *, pointer to a character string. |
DTBULK | is gen_binary, a structure containing a series binary segment structure, length and number of segments. |
DTDECIMAL DTDEC |
is gen_dec, a structure containing character string. |
DTTIME | is gen_date, a structure containing a series longinteger values for year, month, day, hour, minute, second and microsecond. |
DTNLSCHAR DTNLSCHARACTR |
is char *, pointer to a character string. |
DTNLSTEXT | is char *, pointer to a character string. |
DTWON | is gen_dec, a structure containing character string. |
DTMICROTIMESTAMP | is gen_date, a structure containing a series longinteger values for year, month, day, hour, minute, second and microsecond. |
This is only applicable for the user data type. If there is no parameter required, the value is 0. Generic data types do not require parameters, therefore, the value should set to 0.
This is only applicable for the user data type. If there is no parameter required, the value is 0. Generic data types do not require parameters, therefore, the value should be t to 0.
This is only applicable for the user data type. If there is no parameter required, the value is 0. Generic data types do not require parameters, therefore, the value should be t to 0.
This is only applicable for the user data type. If there is no parameter required, the value is 0. Generic data types do not require parameters, therefore, the value should be t to 0.
This field is not used.
The example below define the data types of the arguments and result value for the expon function:
static dtpar dtgflt = { DTGFLOAT, 0, 0, 0, 0 }, *gfltgflt[] = { &dtgflt, &dtgflt };
Two data type identifiers are required to describe the argument and result value data types for this function. dtgflt is the data type of the result, and gfltgflt is the data type of the argument list.
After the Function I/O Table has been defined for the function, a Function Procedures Table must be created. For each entry in the Function I/O Table, there must be one entry in the Function Procedures Table. An entry in a Function Procedures Table simply names the routines that will be invoked to execute the tuple or aggregate function.
The Function Procedures Table is defined by the C structure dtxpdes in usrfns.h include file:
typedef struct { msbool (*dtxifns)(); /* operator init procedure */ msbool (*dtxigrp)(); /* begin group procedure */ msbool (*dtxrfns)(); /* run operator procedure */ msbool (*dtxtgrp)(); /* end group procedure */ msbool (*dtxtfns)(); /* operator cleanup procedure */ } dtxpdes;
Each entry in the Function Procedures Table consists of five parts:
An example of this table for the expon function is:
dtxpdes expontabproc[] = { { NILPROC, NILPROC, expon, NILPROC, NILPROC } };
NILPROC represents a null entry.
The actual C routine is the last component of the UDF. The easiest way to incorporate the C routine into the UDF is to write the routine so that it can run as either a standalone program, or as a standard function, and then modify it to allow for argument and result value handling, any necessary data conversions, and null and error value handling.
The following examples illustrate how functions or standalone programs can be incorporated into the UDF interface as DT functions.
To incorporate this routine into a UDF, only a few simple changes need to be made:
static msbool function_name (opr, param) dtxr *opr; addr param;
Note that the second argument is not actually used in this example.
The dtxr structure contains all the information required to invoke the function or operator. It provides access to the function arguments or operands and the result of the function or operator.
typedef struct { struct dtxr *dtxrnext; /* next operator */ dtxpdes *dtxrprocs; /* pointer to procedures */ msbool (*dtxrrun)(); /* duplicate pointer to rfns */ void *dtxrparam; /* parameter to procedures */ unsigned int dtxrnargs : BITSPERBYTE; /* number of arguments */ unsigned int dtxraval : 1; /* a-valued operator */ unsigned int dtxrdval : 1; /* delayed operator */ unsigned int dtxrnullok : 1; /* procedures handle nulls */ unsigned int dtxrerrok : 1; /* procedures handle errors */ unsigned int dtxrconvt : 1; /* conversion operator */ unsigned int dtxrufunc : 1; /* M-Builder type operator */ unsigned int dtxrifns : 1; /* was ifns run ? */ void *dtxrwksp; /* workspace */ dtxd *dtxrresult; /* the result */ dtxd *dtxds[1]; /* the operands */ /* space for operands */ } dtxr;
The dtxd structure is used to define and store the function arguments or operands and the result value of the function.
typedef struct { dtpar *dtxddtp; /* the data type */ unsigned int dtxdnull : BITSPERBYTE; /* null (run-time) type */ unsigned int dtxdtype : BITSPERBYTE; /* operand (static) type */ unsigned int dtxdanydt : 1; /* opr will handle any dt */ unsigned int dtxdconsflag : 1; /* constant ? */ unsigned int dtxdcvar : 1; /* control var used ? */ unsigned int dtxdnucheck : 1; /* check for null ? */ unsigned int dtxdresult : 1; /* result operand ? */ unsigned int dtxdgsbuf : 1; /* generic string buffer ? */ unsigned int dtxdlinkcount : 2; /* link count */ void *dtxdnuval; /* the null value */ long dtxdzval; /* size of value */ void **dtxdival; /* indirect pointer to value */ void *dtxdval; /* pointer to value */ } dtxd;
The following is an example for the exponential routine expon:
#include <math.h> static msbool expon(opr, params) dtxr *opr; addr params; { gen_float x,y; dtxd *opd; opd = opr->dtxds[0]; x = *(gen_float *)*(opd->dtxdival); opd = opr->dtxds[1]; y = *(gen_float *)*(opd->dtxdival); *(gen_float *)*(opr->dtxrresult->dtxdival) = pow(x,y); /* pow is the system math function to raise a number to a power */ return( true ); }
The following is the complete listing of usrfns.c file for the above example:
/******************************************************************* * * (c) Copyright Empress Software Inc. 1983, 2003 * *****************************************************************/ /*********************************************************************** * WARNING: as of version 6.4, this file MUST be compiled with "empcc" * (not "cc") in order to find the correct header files. ***********************************************************************/ # include "usrfns.h" /****************************************************************************** * USRXKTAB * * Structure: Table structure * * Purpose: contains the name and parsing information for each * user defined function to be used from the QL level. * Should be kept in ascending alphabetical order * according to function name. * ******************************************************************************/ keyitem usrxktab[] = { { "**", op2_x1 }, /* exponentiation operator */ { (char *)0 } }; int usrzxktab = sizeof (usrxktab) / sizeof (keyitem) - 1; /****************************************************************************** * * DT User Defined Functions * - using dtxr (and dtxd) structures * ******************************************************************************/ /****************************************************************************** * EXPONENTIAL (**) * * Purpose: Raises a number to a power. * * Arguments: Operator - contains two float arguments. * * Returns: true, if calculation is successful; false, if not. * ******************************************************************************/ #include <math.h> static msbool expon(opr, params) dtxr *opr; addr params; { gen_float x,y; dtxd *opd; opd = opr->dtxds[0]; x = *(gen_float *)*(opd->dtxdival); opd = opr->dtxds[1]; y = *(gen_float *)*(opd->dtxdival); *(gen_float *)*(opr->dtxrresult->dtxdival) = pow(x,y); /* pow is the system math function to raise a number to a power */ return( true ); } /****************************************************************************** * * DT User Defined Functions structures and tables * ******************************************************************************/ static dtpar dtgflt = { DTGFLOAT , 0, 0, 0, 0 }, gfltgflt[] = { &dtgflt, &dtgflt }; dtxpdes expontabproc[] = { { NILPROC, NILPROC, expon, NILPROC, NILPROC } }; dtxades expontab[] = { { & expontabproc[0], (addr)0, & dtgflt, gfltgflt }, { NULLENTRY } }; dtxdes usrxtab[] = { { "**", 2, expontab, NNEN, false }, { (char *)0 } }; int usrzxtab = (sizeof (usrxtab) / sizeof (dtxdes)) - 1;
Errors may occur as the result of invalid operand/argument values or invalid operand/argument data types. Error conditions may also arise within the UDF itself.
The following types of error conditions can be handled within the UDF:
In the user function table of usrfns.c (usrxtab), for each function entry there is a flag (dtxinput) indicating the action to be taken when an error condition for an argument/operand is passed to the UDF.
If you wish to handle input errors in the function, this flag should be set to either NNEY or NYEY. NNEY indicates that null values are not valid as input arguments, but errors are. NYEY indicates that both null and error values are valid input arguments. (i.e., NNEY - null values no, error values yes)
If this flag is not set to accept errors as input to the function, and an argument/operand of invalid data type is passed to the UDF, the following message will be reported:
*** User Error *** operator/function 'NAME' invalid or argument(s) incompatible
This error will be reported only once, and the function will not be evaluated.
If the flag is not set to accept errors as input to the function, and if an argument value of the correct data type, but which evaluates to an error is passed to the function, the following message will be issued for each invocation of the function:
*** User Error *** Expression Error: cannot evaluate expression - Skipping record
To indicate that an error condition has been detected in the function, the UDF must return the value false to the program which invoked it.
For each invocation of the function which returns the value false, the same error message (as above) will be reported:
*** User Error *** Expression Error: cannot evaluate expression - Skipping record
To return a more meaningful error message when the UDF returns false, the routine dtudferr() can be used. This routine accepts one argument which must evaluate to a string, and this message will be printed for every invocation of the function that returns false.
The following branch of code shows how a function can return a specific error message with dtudferr().
if ( *(gen_integer *)*(opd->dtxdival) == 0 ) { dtudferr( "div: zero denominator" ); return(false); }
For every invocation of the function for which the argument value evaluates to 0, the error message will be reported as follows:
*** User Error *** div: zero denominator - skipping record
To test for error values in the UDF itself, the routine dtxnuerr is available. The error handling flag must be set in the user function table in order to use this feature.
The following example illustrates handling of:
if ( dtxnuerr(opd->dtxdnull) == DTXERR ) /* invalid argument value (expression error) */ { *(gen_integer *)*(opr->dtxrresult-dtxdival) = INFINITY; return(true); } else if ( *(gen_integer *)*(opd->dtxdival) == 0 ) /* invalid argument value - would result in division by 0 */ { dtudferr( "div: zero denominator" ); return(false); } else /* valid argument data type/value */ { y = *(gen_integer *)*(opd->dtxdival); *(gen_integer *)*(opr->dtxrresult->dtxdival) = x/y; } return( true );
Arguments to user defined functions which evaluate to null can be handled similarly to error values.
In the user function table of usrfns.c (usrxtab), for each function entry the dtxinput flag can be set to indicate the action to be taken when a null value is passed as an argument to a user defined function.
If null values are to be allowed as acceptable values for the C routine, this flag should be set to either NYEN or NYEY. NYEN indicates that null values are valid as input arguments, but error values are not. NYEY indicates that both null and error values are valid input arguments.
If the dtxinput flag is set to either of the above values, when a null value is encountered, the C routine for the user defined function will be invoked. If the flag is not set, the C routine will not be invoked, and the result value of the function will be set to null.
If the dtxinput flag is set, null values can be tested for in the C routine as in the following example using the routine dtxnunul:
if ( dtxnunul(opd->dtxdnull) == DTXNULL ) { dtudferr( "vacated: found a null value" ); return ( false ); } else *(gen_integer *)*(opr->dtxrresult->dtxdival) = 0;
In this example, for each invocation of the function where a null value is detected, an error message will be printed.
Alternatively, the UDF can return a null value by assigning a null value to the result field of the dtxr structure, as in the following example:
if ( dtxnunul(opd->dtxdnull) == DTXNULL ) opr->dtxrresult->dtxdnull = DTXOPDNUL; else *(gen_integer *)*(opr->dtxrresult->dtxdival) = 0;
If a function returns a null value, any error messages that the function would return are suppressed, as are any other result assignments.
Once the file usrfns.c has been modified to include the necessary entries for defining a UDF, the following steps must be taken to make the UDFs available to Empress.
For UNIX systems:
For Windows NT:
The following is the listing of the User Defined Function include file usrfns.h in the include directory:
/*********************************************************************** * (c) Copyright Empress Software Inc. 1983, 2003 ***********************************************************************/ #include "c2.h" #include "public.h" #include "mscc.h" /* * for the keyword table */ #include <usrfns/dtpar.h> #include <usrfns/dttypes.h> #include <usrfns/dtxoper.h> #include <usrfns/dtxstdfns.h> #include <usrfns/generic.h> #include <usrfns/keyitem.h> #include <usrfns/dtparvar.hx> #ifdef __cplusplus extern "C" { #endif #include <usrfns/mrtrig.hx> #define mspsm_trig_abort_operation msmrtrig_abort_operation #define mspsm_trig_get_record_info msmrtrig_get_record_info /* * useful extern's */ global_shared_func msbool dtchkpar (); global_shared_func msbool dtqf (); global_shared_func msbool dttype (); global_shared_func char* dtputex (); global_shared_func char* dtputgex (); global_shared_func char* dtputgin (); global_shared_func char* dtputin (); global_shared_func int dtcmp (); global_shared_func void dtcontrol (); global_shared_func void dtfadd (); global_shared_func void dtfclose (); global_shared_func void dtfdel (); global_shared_func void dtfgethdr (); global_shared_func void dtfopen (); global_shared_func void dtfputhdr (); global_shared_func void dtgetex (); global_shared_func void dtgetgex (); global_shared_func void dtgetgin (); global_shared_func void dtgetin (); global_shared_func void dtgeninfo (); global_shared_func void dtinfo (); global_shared_func void dtvcontrol (); /* * routine for setting error messages */ global_shared_func void dtudferr(); global_shared_func void dtudfsqlstate(); /* * Space allocation routine */ global_shared_func void* spmget (int); global_shared_func void spfree (void*); #define dtalloc(n) spmget(n) /* * useful string handling routines */ global_shared_func char* msstr_save(char*); #define strsave(s) msstr_save (s) #define strfree(s) spfree(s) #ifdef __cplusplus } #endif
/******************************************************************* * (c) Copyright Empress Software Inc. 1983, 2003 * *****************************************************************/ # include "../include/usrfns.h"
usrsktab
Structure: | Table structure. |
Purpose: | Contains the name and parsing information for each of the Standard C user defined functions to be used from the QL level. This should be kept in ascending alphabetical order according to function name. |
keyitem usrsktab[] = { { "!", op1_xp }, /* factorial operator */ { "attach", opf_x }, { "echo", opf_x }, { "odd", opf_x }, { "ret_null", opf0_x }, { (char *)0 } }; int usrzsktab = sizeof (usrsktab) / sizeof (keyitem) - 1;
attach
Purpose: | Illustrate the construction of a Standard C User Defined Function that can handle a variable number of arguments. All the arguments to a maximum of 8 are concatenated into one string. |
Arguments: | argc -- number of arguments (must be from 2 to 9) argv[1] - argv[8] or *++argv -- pointer to character string containing the argument. |
Returns: | Pointer to the character string to be printed. |
static char *attach(argc, argv) int argc; char *argv[]; { static char buffer[256]; /* return a value with maximum 256 characters */ int arg_num, position, offset; if (argc > 9) { dtudferr("attach: maximum of 8 arguments"); return (CHARNIL); } else { position = 0; arg_num = 2; offset = 0; for(arg_num = 1; arg_num < argc; arg_num++) /* for each argument, copy the characters into a buffer (ignore everything beyond 256 characters) */ { while (( argv[arg_num][offset] != '\0') && (position < 256) ) { buffer[position] = argv[arg_num][offset]; position++; offset++; } offset = 0; } buffer[position] = '\0'; return(buffer); } }
echo
Purpose: | Illustrate the construction of a Standard C User Defined Function. This function simply prints the argument contained in argv[1]. |
Arguments: | argc -- number of arguments (must be 2) argv[1] or *++argv -- pointer to character string containing the argument. |
Returns: | pointer to the character string to be printed. |
static char echo ( argc, argv ) int argc; char **argv; { /* Echo whatever is contained in the string argument */ if ( argc == 2 ) return( *++argv ); else { dtudferr( "echo: incorrect number of arguments" ); return ( CHARNIL ); } }
factorial ( ! )
Purpose: | Illustrate the construction of a Standard C User Defined Operator. This operator produces the factorial of a LONGINTEGER argument. |
Arguments: | argc -- number of arguments (must be 2) argv[1] or *++argv -- pointer to character string containing the argument. |
Returns: | Pointer to the character string to be printed. |
char *fact1 (argc, argv) int argc; char **argv; { extern msbool cvai(); extern char *cvlx(); static char result[11]; int x; long y; if ( ! (cvai(&x, argv[1]))) /* convert string to integer */ { dtudferr("fact: invalid argument"); return(CHARNIL); } y = factorial(x); cvlx(result, y); /* convert longinteger to string */ return( result ); /* return the result as a string */ } static long factorial(a) int a; { return a <= 0 ? 1L : a == 1 ? (long) a : (long) a * factorial(a-1); }
odd
Purpose: | To illustrate a function to accept integer arguments and to handle its own error reporting. This function returns "1" for odd integer arguments and "0" for even integer arguments. | ||
Arguments: | argc -- number of arguments (must be 1) argv[1] or *++argc -- pointer to character string with integer in character form. |
Returns: | Pointer to character string containing either 1 if the argument is odd; otherwise, the string contains 0. If an error occurred a CHARNIL is returned. |
static char *odd ( argc, argv ) int argc; char **argv; { extern msbool cval(); extern char *cvlx(); gen_integer i; static char str[30]; if ( argc == 2 ) { if ( cval (&i, argv[1]) ) { i = ( (i % 2) != 0 ); cvlx ( str, i ); return( str ); } else { dtudferr( "odd: argument is not an integer" ); return( CHARNIL ); } } else { dtudferr( "odd: incorrect number of arguments" ); return ( CHARNIL ); } }
ret_null
Purpose: | Illustrate the construction of a Standard C User Defined Function that returns a null value. Useful for generating null values arbitrarily. |
Arguments: | argc -- number of arguments is 1. |
Returns: | Pointer to the character string to be printed (a null string). |
char *ret_null (argc, argv) int argc; char *argv; { return( "" ); }
dtstdfn usrstab[] = { { "!", fact1, 'i', 1 }, /* factorial operator */ { "attach", attach, 'q', -1 }, { "echo", echo, 'q', -1 }, { "odd", odd, 'q', -1 }, { "ret_null", ret_null,'v', 0 }, /* invoked with no ()s */ (char *) 0 }; int usrzstab = sizeof (usrstab) / sizeof (dtstdfn) - 1;
/******************************************************************* * (c) Copyright Empress Software Inc. 1983, 2003 *****************************************************************/ # include <usrfns.h>
usrxktab
Structure: | Table structure. |
Purpose: | Contains the name and parsing information for each user defined function to be used from the QL level. |
keyitem usrxktab[] = { { "**", op2_x1 }, /* exponentiation operator */ { "append", opf_x }, { "dist", opf_x }, { "div", opf_x }, { "ssd", op1_x }, { "vacated", opf_x }, { "xlate", opf_x }, { (char *)0 } }; int usrzxktab = sizeof (usrxktab) / sizeof (keyitem) - 1;
stats
Purpose: | Defines the format of a statistics buffer for a DT User Defined Function. |
typedef struct { double n; /* Size of the sample of statistic x */ double xsum; /* Sum of single statistic x samples */ double x2sum; /* Sum of the squares of single statistic x samples */ } stats;
ssdifns
Purpose: | To illustrate the use of the Function initialization procedure segment of the Aggregate User Defined Function. This function allocate the necessary workspace to contain the structures to accumulate the statistics. |
Arguments: | Operator -- at this point does not contain any valid data, but may have useful information such as data types that may be needed for executing a function initialization setup. In this case, it is not required. |
Returns: | The actual function (ssdifns) will return true if the function initialization was successful; otherwise, false. This function normally allocates the workspace required to perform internal calculations. |
static msbool ssdifns( opr, param ) dtxr *opr; addr param; { /* Allocate the necessary space required for internal statistics gathering. */ opr->dtxrwksp = dtalloc( sizeof(stats) ); return(true); }
ssdigrp
Purpose: | To illustrate the use of the Group initialization procedure segment of the AGGREGATE User Defined Function. This function initializes the structures to accumulate the statistics. |
Arguments: | Operator -- at this point does not contain any valid data, but may have useful information such as data types that may be needed for executing a group initialization setup. In this case, it is not required. |
Returns: | The actual function (ssdigrp) will return true if the group initialization was successful; otherwise, false. This function normally initializes the workspace created by the ssdifns function. |
static msbool ssdigrp( opr, param ) dtxr *opr; addr param; { stats *statistic; /* Obtain the statistic record */ statistic = (stats *) opr->dtxrwksp; /* Initialize the size of the sample of single statistic x to 0 */ statistic->n = 0.0; /* Initialize the sum of the samples of single statistic x to 0 */ statistic->xsum = 0.0; /* Initialize the sum of the squares of the samples of the single statistic x to 0 */ statistic->x2sum = 0.0; return(true); }
ssdrfns
Purpose: | To illustrate the use of the actual function execution procedure segment of the Aggregate User Defined Function. This function performs the actual accumulation of the statistics. |
Arguments: | Operator -- at this point contains any valid argument data, that are needed for the accumulative calculations performed in this function. In particular, it contains the statistic x data. |
Returns: | The actual function (ssdifns) will return true if the execution/accumulation was successful; otherwise, false. |
static msbool ssdrfns( opr, param ) dtxr *opr; addr param; { dtxd *opd; /* Operand */ stats *statistic; /* Statistic record */ double x; /* Statistic x */ /* Obtain the statistic record */ statistic = (stats *) opr->dtxrwksp; /* Obtain the first operand information structure */ opd = opr->dtxds[0]; /* Obtain the x value from the operand information structure */ x = *(gen_float *)*(opd->dtxdival); /* Accumulate the sum of x-squared samples */ statistic->x2sum += x*x; /* Accumulate the sum of x samples */ statistic->xsum += x; /* Increment the value of the size of the sample */ statistic->n += 1.0; return( true ); }
ssdtgrp
Purpose: | To illustrate the use of the terminate group function procedure segment of the Aggregate User Defined Function. This function returns the final aggregate value associated with the accumulated calculations performed by the ssdrfns function. |
Arguments: | Operator -- at this point, is ready to accept the result as determined by the accumulated calculations. In particular, it will accept the squared standard deviation of the statistic x data. |
Returns: | The actual function (ssdtgrp) will return true if the result determination was successful; otherwise, false. |
static msbool ssdtgrp( opr, param ) dtxr *opr; addr param; { stats *statistic; /* Obtain the statistic record */ statistic = (stats *) opr->dtxrwksp; /* Squared standard deviation (SSD) is (sum of squares)/n - [ (sum of x)/n ]^2 */ if (statistic->n==0.0) *(gen_float *)*(opr->dtxrresult->dtxdival) = 0.0; else *(gen_float *)*(opr->dtxrresult->dtxdival) = (gen_float)(( statistic->x2sum - statistic->xsum * statistic->xsum / statistic->n) /statistic->n); return (true); }
ssdtfns
Purpose: | To illustrate the use of the terminate function procedure segment of the Aggregate User Defined Function. This function performs cleanups and usually frees up any workspace used by the function. |
Arguments: | Operator -- at this point, the only structure pertinent is the workspace. In particular, it will free the workspace. |
Returns: | The actual function (ssdtfns) will return true if the cleanup was successful; otherwise, false. |
static msbool ssdtfns( opr, param ) dtxr *opr; addr param; { extern void spfree (); spfree ( opr->dtxrwksp ); return( true ); }
point
Purpose: | Defines the structure of bulk containing the x and y coordinates of the point (Type definition for distproc). |
typedef struct { int pt_nbytes; /* number of bytes */ long pt_x; /* x coordinate */ long pt_y; /* y coordinate */ } point;
distproc
Purpose: | Finds the square of the distance between two 2-D points. This function also handles its own errors. |
Arguments: | Operator -- contains two bulk arguments each containing a point specification. |
Returns: | True, if calculation is successful; false, if not. |
msbool distproc (opr, param) dtxr *opr; addr param; { int i; dtxd *opd; long x1, x2, xd, y1, y2, yd; point *p; opd = opr->dtxds[0]; dtgetin (opd->dtxddtp, *(opd->dtxdival)); p = (point *)dtval; if (p->pt_nbytes != 2 * sizeof (long)) { dtudferr( "Dist : Error in size of X1-Y1 co-ordinates\n" ); return (false); } x1 = p->pt_x, y1 = p->pt_y; opd = opr->dtxds[1]; dtgetin (opd->dtxddtp, *(opd->dtxdival)); p = (point *)dtval; if (p->pt_nbytes != 2 * sizeof (long)) { dtudferr( "Dist : Error in size of X2-Y2 co-ordinates\n" ); return (false); } x2 = p->pt_x, y2 = p->pt_y; xd = x1 - x2, yd = y1 - y2; *(gen_integer *)*(opr->dtxrresult->dtxdival) = xd * xd + yd * yd; return (true); }
exponential
Purpose: | Raises a number to a power. |
Arguments: | Operator -- contains two float arguments. |
Returns: | True, if calculation is successful; false, if not. |
#include <math.h> static msbool expon(opr, params) dtxr *opr; addr params; { gen_float x,y; dtxd *opd; opd = opr->dtxds[0]; x = *(gen_float *)*(opd->dtxdival); opd = opr->dtxds[1]; y = *(gen_float *)*(opd->dtxdival); *(gen_float *)*(opr->dtxrresult->dtxdival) = pow(x,y); /* pow is the system math function to raise a number to a power */ return( true ); }
append
Purpose: | Illustrate how to return a variable length result. |
Arguments: | Operator -- contains two character string arguments. |
Returns: | True, if calculation is successful; false, if not. |
static msbool append (opr, param) dtxr *opr; addr param; { dtxd *opd1; dtxd *opd2; addr *ival; addr buf; char null[1]; int sze, sze1, sze2; extern char *memcpy(); *null = '\0'; opd1 = opr->dtxds[0]; opd2 = opr->dtxds[1]; sze1 = strlen(*opd1->dtxdival); sze2 = strlen(*opd2->dtxdival); buf = (char *) dtalloc ((sze1 + sze2 + 1) * sizeof(char)); memcpy(buf, *opd1->dtxdival, sze1); memcpy(buf + sze1, *opd2->dtxdival, sze2); memcpy(buf + sze1 + sze2, null,1); ival = opr->dtxrresult->dtxdival; strfree (*ival); /* free the current space */ *ival = strsave(buf); /* assign the result value */ spfree (buf); return (true); }
distp2
Purpose: | Finds the difference of two integers. |
static msbool distp2 (opr, param) dtxr *opr; addr param; { dtxd *opd; long x1, x2, xd; opd = opr->dtxds[0]; x1 = *(gen_integer *)*(opd->dtxdival); opd = opr->dtxds[1]; x2 = *(gen_integer *)*(opd->dtxdival); xd = x1 - x2; *(gen_integer *)*(opr->dtxrresult->dtxdival) = xd; return( true ); }
func_xlate
Purpose: | Illustrate use of the param argument and handling of null values. |
Arguments: | Operator -- contains three integer arguments or float arguments. |
Returns: | True, if calculation is successful; false, if not. |
static msbool func_xlate(opr, param) dtxr *opr; addr param; { dtxd *opd; long x1, x2, x3; float f1, f2, f3; msbool x1_null, x2_null, x3_null; int iparam; iparam = *( int * ) param; x1_null = x2_null = x3_null = false; opr->dtxrresult->dtxdnull = DTXVALUE; opd = opr->dtxds[0]; if ( dtxnunul(opd->dtxdnull) == DTXNULL ) x1_null = true; else { switch( iparam ) { case 0 : x1 = *(gen_integer *)*(opd->dtxdival); break; case 1 : f1 = *(gen_float *)*(opd->dtxdival); break; } } opd = opr->dtxds[1]; if ( dtxnunul(opd->dtxdnull) == DTXNULL ) x2_null = true; else { switch( iparam ) { case 0 : x2 = *(gen_integer *)*(opd->dtxdival); break; case 1 : f2 = *(gen_float *)*(opd->dtxdival); break; } } opd = opr->dtxds[2]; if ( dtxnunul(opd->dtxdnull) == DTXNULL ) x3_null = true; else { switch( iparam ) { case 0 : x3 = *(gen_integer *)*(opd->dtxdival); break; case 1 : f3 = *(gen_float *)*(opd->dtxdival); break; } } if(( x1_null && x2_null ) || ( !x1_null && !x2_null && ( (iparam==0) ? (x1==x2) : f1 == f2 ) ) ) { if ( x3_null ) opr->dtxrresult->dtxdnull = DTXOPDNUL; else { switch( iparam ) { case 0 : *(gen_integer *)*(opr->dtxrresult->dtxdival) = x3; break; case 1 : *(gen_float *)*(opr->dtxrresult->dtxdival) = f3; break; } } } else { if ( x1_null ) opr->dtxrresult->dtxdnull = DTXOPDNUL; else { switch( iparam ) { case 0 : *(gen_integer *)*(opr->dtxrresult->dtxdival) = x1; break; case 1 : *(gen_float *)*(opr->dtxrresult->dtxdival) = f1; break; } } } return( true ); }
vacated
Purpose: | Illustrate use of the handling of null values. |
Arguments: | Operator -- contains one numeric argument. |
Returns: | True, if calculation is successful; false, if not. |
static msbool vacated (opr, params) dtxr *opr; addr params; { gen_integer x; dtxd *opd; opd = opr->dtxds[0]; /* If the operand is null then return a NULL otherwise, return a 0 */ if ( dtxnunul(opd->dtxdnull) == DTXNULL ) { dtudferr( "vacated: found a null value" ); return(false); } else *(gen_integer *)*(opr->dtxrresult->dtxdival) = 0; return( true ); }
div
Purpose: | Illustrate use of the handling of error values. |
Arguments: | Operator -- contains one numeric argument. |
Returns: | True, if calculation is successful; false, if not. |
# define INFINITY (0x7FFFFFFF) static msbool div (opr, params) dtxr *opr; addr params; { gen_integer x,y; dtxd *opd; opd = opr->dtxds[0]; /* Checks the numerator for expression errors */ if ( dtxnuerr(opd->dtxdnull) == DTXERR ) { *(gen_integer *)*(opr->dtxrresult->dtxdival) = INFINITY; return(true); } else x = *(gen_integer *)*(opd->dtxdival); opd = opr->dtxds[1]; /* Checks the denominator for expression errors */ if ( dtxnuerr(opd->dtxdnull) == DTXERR ) { *(gen_integer *)*(opr->dtxrresult->dtxdival) = INFINITY; return(true); } /* Otherwise if the denominator is zero then report error */ else if ( *(gen_integer *)*(opd->dtxdival) == 0 ) { dtudferr( "div: zero denominator" ); return(false); } else { y = *(gen_integer *)*(opd->dtxdival); *(gen_integer *)*(opr->dtxrresult->dtxdival) = x/y; } return( true ); }
static dtpar dtbulk = { DTBULK, 2, 10, 16, 1 }, dtgint = { DTGINTEGER, 0, 0, 0, 0 }, dtgflt = { DTGFLOAT , 0, 0, 0, 0 }, dtgchar = { DTGCHAR , 0, 0, 0, 0 }, gflt[] = { & dtgflt }, gint[] = { & dtgint }, buk[] = { & dtbulk }, gchargchar[] = { &dtgchar, &dtgchar}, gintgint[] = { & dtgint, & dtgint }, gfltgflt[] = { & dtgflt, & dtgflt }, bukbuk[] = { & dtbulk, & dtbulk }, gfgfgf[] = { & dtgflt, & dtgflt, & dtgflt }, gigigi[] = { & dtgint, & dtgint, & dtgint }; static int intdt = 0; static int fltdt = 1; dtxpdes ssdtabproc[] = { { ssdifns, ssdigrp, ssdrfns, ssdtgrp, ssdtfns } }; dtxpdes expontabproc[] = { { NILPROC, NILPROC, expon, NILPROC, NILPROC } }; dtxpdes appendtabproc[] = { { NILPROC, NILPROC, append, NILPROC, NILPROC } }; dtxpdes disttabproc[] = { { NILPROC, NILPROC, distproc, NILPROC, NILPROC }, { NILPROC, NILPROC, distp2, NILPROC, NILPROC } }; dtxpdes divtabproc[] = { { NILPROC, NILPROC, div, NILPROC, NILPROC } }; dtxpdes vactabproc[] = { { NILPROC, NILPROC, vacated, NILPROC, NILPROC } }; dtxpdes xlatetabproc[] = { { NILPROC, NILPROC, func_xlate, NILPROC, NILPROC } }; dtxades disttab[] = { { & disttabproc[0], (addr)0, & dtgint, bukbuk }, { & disttabproc[1], (addr)0, & dtgint, gintgint }, { NULLENTRY } }; dtxades expontab[] = { { & expontabproc[0], (addr)0, & dtgflt, gfltgflt }, { NULLENTRY } }; dtxades appendtab[] = { { & appendtabproc[0], (addr)0, & dtgchar, gchargchar }, { NULLENTRY } }; dtxades divtab[] = { { & divtabproc[0], (addr)0, & dtgint, gintgint }, { NULLENTRY } }; dtxades ssdtab[] = { { & ssdtabproc[0], (addr)0, & dtgflt, gflt }, { NULLENTRY } }; dtxades vactab[] = { { & vactabproc[0], (addr)0, & dtgint, gint }, { & vactabproc[0], (addr)0, & dtgint, gflt }, { & vactabproc[0], (addr)0, & dtgint, buk }, { NULLENTRY } }; static dtxades xlatetab[] = { { & xlatetabproc[0], (addr)&intdt, & dtgint, gigigi }, { & xlatetabproc[0], (addr)&fltdt, & dtgflt, gfgfgf }, { NULLENTRY } }; dtxdes usrxtab[] = { { "**", 2, expontab, NNEN, false }, { "append", 2, appendtab, NNEN, false }, { "dist", 2, disttab, NNEN, false }, { "div", 2, divtab, NNEY, false }, { "ssd", 1, ssdtab, NYEN, true }, { "vacated", 1, vactab, NYEN, false }, { "xlate", 3, xlatetab, NYEN, false }, { (char *)0 } }; int usrzxtab = (sizeof (usrxtab) / sizeof (dtxdes)) - 1;