Application Development Guide


Writing OLE Automation UDFs

OLE (Object Linking and Embedding) automation is part of the OLE 2.0 architecture from Microsoft Corporation. With OLE automation, your applications, regardless of the language in which they are written, can expose their properties and methods in OLE automation objects. Other applications, such as Lotus Notes or Microsoft Exchange(R), can then integrate these objects by taking advantage of these properties and methods through OLE automation.

The applications exposing the properties and methods are called OLE automation servers or objects, and the applications that access those properties and methods are called OLE automation controllers. OLE automation servers are COM components (objects) that implement the OLE IDispatch interface. An OLE automation controller is a COM client that communicates with the automation server through its IDispatch interface. COM (Component Object Model) is the foundation of OLE. For OLE automation UDFs, DB2 acts as an OLE automation controller. Through this mechanism, DB2 can invoke methods of OLE automation objects as external UDFs.

Note that this section assumes that you are familiar with OLE automation terms and concepts. This book does not present any introductory OLE material. For an overview of OLE automation, refer to Microsoft Corporation: The Component Object Model Specification, October 1995. For details on OLE automation, refer to OLE Automation Programmer's Reference, Microsoft Press, 1996, ISBN 1-55615-851-3.

For a list of sample applications included with the DB2 Application Development Client that demonstrate OLE automation UDFs, see Table 49.

Creating and Registering OLE Automation UDFs

OLE automation UDFs are implemented as public methods of OLE automation objects. The OLE automation objects must be externally creatable by an OLE automation controller, in this case DB2, and support late binding (also called IDispatch-based binding). OLE automation objects must be registered in the Windows registration database (registry) with a class identifier (CLSID), and optionally, an OLE programmatic ID (progID) to identify the automation object. The progID can identify an in-process (.DLL) or local (.EXE) OLE automation server, or a remote server through DCOM (Distributed COM). OLE automation UDFs can be scalar functions or table functions.

After you code an OLE automation object, you need to register the methods of the object as UDFs using the SQL CREATE FUNCTION statement. Registering an OLE automation UDF is very similar to registering any external C or C++ UDF, but you must use the following options:

The external name consists of the OLE progID identifying the OLE automation object and the method name separated by ! (exclamation mark):

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

The calling conventions for OLE method implementations are identical to the conventions for functions written in C or C++. An implementation of the above method in the BASIC language looks like the following (notice that in BASIC the parameters are by default defined as call by reference):

     Public Sub increment(output As Long, _       
                          indicator As Integer, _
                          sqlstate As String, _
                          fname As String, _
                          fspecname As String, _
                          sqlmsg As String, _
                          scratchpad() As Byte, _
                          calltype As Long)

Object Instance and Scratchpad Considerations

OLE automation UDFs (methods of OLE automation objects) are applied on instances of OLE automation objects. DB2 creates an object instance for each UDF reference in an SQL statement. An object instance can be reused for subsequent method invocations of the UDF reference in an SQL statement, or the instance can be released after the method invocation and a new instance is created for each subsequent method invocation. The proper behavior can be specified with the SCRATCHPAD option in the SQL CREATE FUNCTION statement. For the LANGUAGE OLE clause, the SCRATCHPAD option has the additional semantic compared to C or C++, that a single object instance is created and reused for the entire query, whereas if NO SCRATCHPAD is specified, a new object instance may be created each time a method is invoked. Separate instances are created for each UDF reference in an SQL statement.

Using the scratchpad allows a method to maintain state information in instance variables of the object, across function invocations. It also increases performance as an object instance is only created once and then reused for subsequent invocations.

How the SQL Data Types are Passed to an OLE Automation UDF

DB2 handles the type conversions between SQL types and OLE automation types. The following table summarizes the supported data types and how they are mapped. The mapping of OLE automation types to data types of the implementing programming language, such as BASIC or C/C++, is described in Table 17.

Table 16. Mapping of SQL and OLE Automation Datatypes
SQL Type OLE Automation Type OLE Automation Type Description
SMALLINT short 16-bit signed integer
INTEGER long 32-bit signed integer
REAL float 32-bit IEEE floating-point number
FLOAT or DOUBLE double 64-bit IEEE floating-point number
DATE DATE 64-bit floating-point fractional number of days since December 30, 1899
TIME DATE
TIMESTAMP DATE
CHAR(n) BSTR Length-prefixed string as described in the OLE Automation Programmer's Reference.
VARCHAR(n) BSTR
LONG VARCHAR BSTR
CLOB(n) BSTR
GRAPHIC(n) BSTR Length-prefixed string as described in the OLE Automation Programmer's Reference.
VARGRAPHIC(n) BSTR
LONG GRAPHIC BSTR
DBCLOB(n) BSTR
CHAR(n)1 SAFEARRAY[unsigned char] 1-dim Byte() array of 8-bit unsigned data items. (SAFEARRAYs are described in the OLE Automation Programmer's Reference.)
VARCHAR(n)1 SAFEARRAY[unsigned char]
LONG VARCHAR1 SAFEARRAY[unsigned char]
BLOB(n) SAFEARRAY[unsigned char]
Note:
  1. With FOR BIT DATA specified

Data passed between DB2 and OLE automation UDFs is passed as call by reference. SQL types such as BIGINT, DECIMAL, or LOCATORS, or OLE automation types such as Boolean or CURRENCY that are not listed in the table are not supported. Character and graphic data mapped to BSTR is converted from the database code page to the UCS-2 (also known as Unicode, IBM code page 13488) scheme. Upon return, the data is converted back to the database code page. These conversions occur regardless of the database code page. If code page conversion tables to convert from the database code page to UCS-2 and from UCS-2 to the database code page are not installed, you receive an SQLCODE -332 (SQLSTATE 57017).

Implementing OLE Automation UDFs in BASIC and C++

You can implement OLE automation UDFs in any language. This section shows you how to implement OLE automation UDFs using BASIC or C++ as two sample languages.

Table 17 shows the mapping of the various SQL data types to the intermediate OLE automation data types, and the data types in the language of interest (BASIC or C++). OLE data types are language independent, (that is, Table 16 holds true for all languages).

Table 17. Mapping of SQL and OLE Data Types to BASIC and C++ Data Types
SQL Type OLE Automation Type UDF Language
BASIC Type C++ Type
SMALLINT short Integer short
INTEGER long Long long
REAL float Single float
FLOAT or DOUBLE double Double double
DATE, TIME, TIMESTAMP DATE Date DATE
CHAR(n), VARCHAR(n), LONG VARCHAR, CLOB(n) BSTR String BSTR
GRAPHIC(n), VARGRAPHIC(n), LONG GRAPHIC, DBCLOB(n) BSTR String BSTR
CHAR(n)1, VARCHAR(n)1, LONG VARCHAR1, BLOB(n) SAFEARRAY[unsigned char] Byte() SAFEARRAY
Note:
  1. With FOR BIT DATA specified

OLE Automation UDFs in BASIC

To implement OLE automation UDFs in BASIC you need to use the BASIC data types corresponding to the SQL data types mapped to OLE automation types.

The BASIC declaration of the bcounter OLE automation UDF in Creating and Registering OLE Automation UDFs looks like the following:

     Public Sub increment(output As Long, _       
                          indicator As Integer, _
                          sqlstate As String, _
                          fname As String, _
                          fspecname As String, _
                          sqlmsg As String, _
                          scratchpad() As Byte, _
                          calltype As Long)

You can find an example of an OLE table automation in Example: Mail OLE Automation Table Function in BASIC.

OLE Automation UDFs in C++

Table 17 shows the C++ data types that correspond to the SQL data types and how they map to OLE automation types.

The C++ declaration of the increment OLE automation UDF is as follows:

     STDMETHODIMP Ccounter::increment (long   *output,           
                                       short  *indicator,        
                                       BSTR   *sqlstate,         
                                       BSTR   *fname,            
                                       BSTR   *fspecname,        
                                       BSTR   *sqlmsg,           
                                       SAFEARRAY **scratchpad,   
                                       long   *calltype );       

OLE supports type libraries that describe the properties and methods of OLE automation objects. Exposed objects, properties, and methods are described in the Object Description Language (ODL). The ODL description of the above C++ method is as follows:

     HRESULT increment ([out]    long  *output,                    
                        [out]    short *indicator,                 
                        [out]    BSTR  *sqlstate,                  
                        [in]     BSTR  *fname,                     
                        [in]     BSTR  *fspecname,                 
                        [out]    BSTR  *sqlmsg, 
                        [in,out] SAFEARRAY (unsigned char) *scratchpad,
                        [in]     long *calltype);

The ODL description allows the specification whether a parameter is an input (in), output (out) or input/output (in,out) parameter. For an OLE automation UDF, the UDF input parameters and its input indicators are specified as [in] parameters, and UDF output parameters and its output indicators as [out] parameters. For the UDF trailing arguments, sqlstate is an [out] parameter, function name and function specific name are [in] parameters, scratchpad is an [in,out] parameter, and call type is an [in] parameter.

Scalar functions contain one output parameter and output indicator, whereas table functions contain multiple output parameters and output indicators corresponding to the RETURN columns of the CREATE FUNCTION statement.

OLE automation defines the BSTR data type to handle strings. BSTR is defined as a pointer to OLECHAR: typedef OLECHAR *BSTR. For allocating and freeing BSTRs, OLE imposes the rule, that the callee frees a BSTR passed in as a by-reference parameter before assigning the parameter a new value. This rule means the following for DB2 and OLE automation UDFs. The same rule applies for one-dimensional byte arrays which are received by the callee as SAFEARRAY**:

All other parameters are passed as pointers. DB2 allocates and manages the referenced memory.

OLE automation provides a set of data manipulation functions for dealing with BSTRs and SAFEARRAYs. The data manipulation functions are described in the OLE Automation Programmer's Reference.

The following C++ UDF returns the first 5 characters of a CLOB input parameter:

     // UDF DDL: CREATE FUNCTION crunch (clob(5k)) RETURNS char(5)
                                                                                
     STDMETHODIMP Cobj::crunch (BSTR *in,          // CLOB(5K)                  
                                BSTR *out,         // CHAR(5)                   
                                short *indicator1,  // input indicator           
                                short *indicator2,  // output indicator          
                                BSTR *sqlstate,    // pointer to NULL           
                                BSTR *fname,       // pointer to function name  
                                BSTR *fspecname,   // pointer to specific name  
                                BSTR *msgtext)     // pointer to NULL           
       {                                                                        
          // Allocate BSTR of 5 characters
          // and copy 5 characters of input parameter
                           
          // out is an [out] parameter of type BSTR, that is, 
          // it is a pointer to NULL and the memory does not have to be freed.
          // DB2 will free the allocated BSTR.
                                                                           
          *out = SysAllocStringLen (*in, 5);                                    
          return NOERROR;                                                       
       };                                                                       

An OLE automation server can be implemented as creatable single-use or creatable multi-use. With creatable single-use, each client (that is, a DB2 FENCED process) connecting with CoGetClassObject to an OLE automation object will use its own instance of a class factory, and run a new copy of the OLE automation server if necessary. With creatable multi-use, many clients connect to the same class factory. That is, each instantiation of a class factory is supplied by an already running copy of the OLE server, if any. If there are no copies of the OLE server running, a copy is automatically started to supply the class object. The choice between single-use and multi-use OLE automation servers is yours, when you implement your automation server. A single-use server is recommended for better performance.


[ Top of Page | Previous Page | Next Page ]