Application Development Guide


Examples of Registering UDFs and Methods

The examples which follow illustrate a variety of typical situations where UDFs and methods can be registered.

Note that in these examples:

Example: Exponentiation

Suppose you have written an external UDF to perform exponentiation of floating point values, and wish to register it in the MATH schema. Assume that you have DBADM authority. As you have tested the function extensively, and know that it does not represent any integrity exposure, you define it as NOT FENCED. By virtue of having DBADM authority, you possess the database authority, CREATE_NOT_FENCED, which is required to define the function as NOT FENCED.

     CREATE FUNCTION MATH.EXPON (DOUBLE, DOUBLE) 
       RETURNS DOUBLE 
       EXTERNAL NAME '/common/math/exponent' 
       LANGUAGE C 
       PARAMETER STYLE DB2SQL 
       NO SQL 
       DETERMINISTIC
       NO EXTERNAL ACTION 
       NOT FENCED

In this example, the system uses the NOT NULL CALL default value. This is desirable since you want the result to be NULL if either argument is NULL. Since you do not require a scratchpad and no final call is necessary, the NO SCRATCHPAD and NO FINAL CALL default values are used. As there is no reason why EXPON cannot be parallel, the ALLOW PARALLELISM default value is used.

Example: String Search

Your associate, Willie, has written a UDF to look for the existence of a given short string, passed as an argument, within a given CLOB value, which is also passed as an argument. The UDF returns the position of the string within the CLOB if it finds the string, or zero if it does not. Because you are concerned with database integrity for this function as you suspect the UDF is not fully tested, you define the function as FENCED.

Additionally, Willie has written the function to return a FLOAT result. Suppose you know that when it is used in SQL, it should always return an INTEGER. You can create the following function:

     CREATE FUNCTION FINDSTRING (CLOB(500K), VARCHAR(200)) 
       RETURNS INTEGER 
       CAST FROM FLOAT 
       SPECIFIC "willie_find_feb95" 
       EXTERNAL NAME '/u/willie/testfunc/testmod!findstr' 
       LANGUAGE C 
       PARAMETER STYLE DB2SQL 
       NO SQL 
       DETERMINISTIC
       NO EXTERNAL ACTION 
       FENCED

Note that a CAST FROM clause is used to specify that the UDF body really returns a FLOAT value but you want to cast this to INTEGER before returning the value to the statement which used the UDF. As discussed in the SQL Reference, the INTEGER built-in function can perform this cast for you. Also, you wish to provide your own specific name for the function and later reference it in DDL (see Example: String Search over UDT). Because the UDF was not written to handle NULL values, you use the NOT NULL CALL default value. And because there is no scratchpad, you use the NO SCRATCHPAD and NO FINAL CALL default values. As there is no reason why FINDSTRING cannot be parallel, the ALLOW PARALLELISM default value is used.

Example: BLOB String Search

Because you want this function to work on BLOBs as well as on CLOBs, you define another FINDSTRING taking BLOB as the first parameter:

     CREATE FUNCTION FINDSTRING (BLOB(500K), VARCHAR(200)) 
       RETURNS INTEGER 
       CAST FROM FLOAT 
       SPECIFIC "willie_fblob_feb95" 
       EXTERNAL NAME '/u/willie/testfunc/testmod!findstr' 
       LANGUAGE C 
       PARAMETER STYLE DB2SQL 
       NO SQL 
       DETERMINISTIC 
       NO EXTERNAL ACTION 
       FENCED

This example illustrates overloading of the UDF name, and shows that multiple UDFs and methods can share the same body. Note that although a BLOB cannot be assigned to a CLOB, the same source code can be used. There is no programming problem in the above example as the programming interface for BLOB and CLOB between DB2 and UDF is the same; length followed by data. DB2 does not check if the UDF using a particular function body is in any way consistent with any other UDF using the same body.

Example: String Search over UDT

This example is a continuation of the previous example. Say you are satisfied with the FINDSTRING functions from Example: BLOB String Search, but now you have defined a distinct type BOAT with source type BLOB. You also want FINDSTRING to operate on values having data type BOAT, so you create another FINDSTRING function. This function is sourced on the FINDSTRING which operates on BLOB values in Example: BLOB String Search. Note the further overloading of FINDSTRING in this example:

     CREATE FUNCTION FINDSTRING (BOAT, VARCHAR(200)) 
       RETURNS INT 
       SPECIFIC "slick_fboat_mar95" 
       SOURCE SPECIFIC "willie_fblob_feb95"

Note that this FINDSTRING function has a different signature from the FINDSTRING functions in Example: BLOB String Search, so there is no problem overloading the name. You wish to provide our own specific name for possible later reference in DDL. Because you are using the SOURCE clause, you cannot use the EXTERNAL NAME clause or any of the related keywords specifying function attributes. These attributes are taken from the source function. Finally, observe that in identifying the source function you are using the specific function name explicitly provided in Example: BLOB String Search. Because this is an unqualified reference, the schema in which this source function resides must be in the function path, or the reference will not be resolved.

Example: External Function with UDT Parameter

You have written another UDF to take a BOAT and examine its design attributes and generate a cost for the boat in Canadian dollars. Even though internally, the labor cost may be priced in euros, or Japanese yen, or US dollars, this function needs to generate the cost to build the boat in the required currency, Canadian dollars. This means it has to get current exchange rate information from an exchange rate web page, and the answer depends on the contents of the web page. This makes the function NOT DETERMINISTIC (or VARIANT).

     CREATE FUNCTION BOAT_COST (BOAT) 
       RETURNS INTEGER 
       EXTERNAL NAME '/u/marine/funcdir/costs!boatcost' 
       LANGUAGE C 
       PARAMETER STYLE DB2SQL 
       NO SQL 
       NOT DETERMINISTIC
       NO EXTERNAL ACTION 
       FENCED

Observe that CAST FROM and SPECIFIC are not specified, but that NOT DETERMINISTIC is specified. Here again, FENCED is chosen for safety reasons.

Example: AVG over a UDT

This example implements the AVG column function over the CANADIAN_DOLLAR distinct type. See Example: Money for the definition of CANADIAN_DOLLAR. Strong typing prevents you from using the built-in AVG function on a distinct type. It turns out that the source type for CANADIAN_DOLLAR was DECIMAL, and so you implement the AVG by sourcing it on the AVG(DECIMAL) built-in function. The ability to do this depends on being able to cast from DECIMAL to CANADIAN_DOLLAR and vice versa, but since DECIMAL is the source type for CANADIAN_DOLLAR you know these casts will work.

     CREATE FUNCTION AVG (CANADIAN_DOLLAR) 
       RETURNS CANADIAN_DOLLAR 
       SOURCE "SYSIBM".AVG(DECIMAL(9,2))

Note that in the SOURCE clause you have qualified the function name, just in case there might be some other AVG function lurking in your function path.

Example: Counting

Your simple counting function returns a 1 the first time and increments the result by one each time it is called. This function takes no SQL arguments, and by definition it is a NOT DETERMINISTIC function since its answer varies from call to call. It uses the scratchpad to save the last value returned, and each time it is invoked it increments this value and returns it. You have rigorously tested this function, and possess DBADM authority on the database, so you will define it as NOT FENCED. (DBADM implies CREATE_NOT_FENCED.)

     CREATE FUNCTION COUNTER () 
       RETURNS INT 
       EXTERNAL NAME '/u/roberto/myfuncs/util!ctr' 
       LANGUAGE C 
       PARAMETER STYLE DB2SQL 
       NO SQL 
       NOT DETERMINISTIC
       NOT FENCED 
       SCRATCHPAD
       DISALLOW PARALLEL

Note that no parameter definitions are provided, just empty parentheses. The above function specifies SCRATCHPAD, and uses the default specification of NO FINAL CALL. In this case, as the default size of the scratchpad (100 bytes) is sufficient, no storage has to be freed by means of a final call, and so NO FINAL CALL is specified. Since the COUNTER function requires that a single scratchpad be used to operate properly, DISALLOW PARALLEL is added to prevent DB2 from operating it in parallel. To see an implementation of this COUNTER function, refer to Example: Counter.

Example: Counting with an OLE Automation Object

This example implements the previous counting example as an OLE (Object Linking and Embedding) automation object, counter, with an instance variable, nbrOfInvoke, to keep track of the number of invocations. Every time the UDF gets invoked, the increment method of the object increments the nbrOfInvoke instance variable and returns its current state. The automation object is registered in the Windows registry with the OLE programmatic identifier (progID) bert.bcounter.

     CREATE FUNCTION bcounter ()
       RETURNS integer
       EXTERNAL NAME 'bert.bcounter!increment'
       LANGUAGE OLE 
       PARAMETER STYLE DB2SQL
       SCRATCHPAD 
       NOT DETERMINISTIC 
       FENCED
       NULL CALL 
       NO SQL
       NO EXTERNAL ACTION
       DISALLOW PARALLEL;

The implementation of the class counter is shown in Example: Counter OLE Automation UDF in BASIC and in Example: Counter OLE Automation UDF in C++. For details of OLE support with DB2, see Writing OLE Automation UDFs.

Example: Table Function Returning Document IDs

You have written a table function which returns a row consisting of a single document identifier column for each known document in your text management system which matches a given subject area (the first parameter) and contains the given string (second parameter). This UDF uses the functions of the text management system to quickly identify the documents:

     CREATE FUNCTION DOCMATCH (VARCHAR(30), VARCHAR(255))
       RETURNS TABLE (DOC_ID CHAR(16))
       EXTERNAL NAME '/common/docfuncs/rajiv/udfmatch'
       LANGUAGE C
       PARAMETER STYLE DB2SQL
       NO SQL
       DETERMINISTIC
       NO EXTERNAL ACTION
       NOT FENCED
       SCRATCHPAD
       NO FINAL CALL
       DISALLOW PARALLEL
       CARDINALITY 20

Within the context of a single session it will always return the same table, and therefore it is defined as DETERMINISTIC. Note the RETURNS clause which defines the output from DOCMATCH, including the column name DOC_ID. FINAL CALL does not need to be specified for each table function. In addition, the DISALLOW PARALLEL keyword is added as table functions cannot operate in parallel. Although the size of the output from DOCMATCH is highly variable, CARDINALITY 20 is a representative value, and is specified to help the DB2 optimizer to make good decisions.

Typically this table function would be used in a join with the table containing the document text, as follows:

  SELECT T.AUTHOR, T.DOCTEXT                                           
  FROM DOCS as T, TABLE(DOCMATCH('MATHEMATICS', 'ZORN''S LEMMA')) as F 
  WHERE T.DOCID = F.DOC_ID                                             

Note the special syntax (TABLE keyword) for specifying a table function in a FROM clause. In this invocation, the docmatch() table function returns a row containing the single column DOC_ID for each mathematics document referencing Zorn's Lemma. These DOC_ID values are joined to the master document table, retrieving the author's name and document text.


[ Top of Page | Previous Page | Next Page ]