Examples of C# .NET CLR functions
Once you understand the basics of user-defined functions (UDFs), and the essentials of CLR routines, you can start exploiting CLR UDFs in your applications and database environment. This topic contains some examples of CLR UDFs to get you started.
Before you begin
- .NET common language runtime (CLR) routines
- Creating .NET CLR routines from the Db2 command window
- External scalar functions
- Building common language runtime (CLR) .NET routines
EMPLOYEE that is contained in
the SAMPLE database.About this task
Procedure
Example
- The C# external code file
- The following examples show a variety of C# UDF implementations.
The CREATE FUNCTION statement is provided for each UDF with the corresponding
C# source code from which the associated assembly can be built.
The C# source file that contains the functions declarations used in
the following examples is named gwenUDF.cs and
has the following format:
using System; using System.IO; using IBM.Data.DB2; namespace bizLogic { ... // Class definitions that contain UDF declarations // and any supporting class definitions ... }The function declarations must be contained in a class within a C# file. The use of namespaces is optional. If a namespace is used, the namespace must appear in the assembly path name provided in the EXTERNAL clause of the CREATE PROCEDURE statement. The
IBM.Data.DB2.inclusion is required if the function contains SQL. - Example 1: C# parameter style SQL table function
- This example shows the following:
- CREATE FUNCTION statement for a parameter style SQL table function
- C# code for a parameter style SQL table function
This table function returns a table containing rows of employee data that was created from a data array. There are two classes associated with this example. Class
personrepresents the employees, and the classempOpscontains the routine table UDF that uses classperson. The employee salary information is updated based on the value of an input parameter. The data array in this example is created within the table function itself on the first call of the table function. Such an array could have also been created by reading in data from a text file on the file system. The array data values are written to a scratchpad so that the data can be accessed in subsequent calls of the table function.On each call of the table function, one record is read from the array and one row is generated in the table that is returned by the function. The row is generated in the table, by setting the output parameters of the table function to the desired row values. After the final call of the table function occurs, the table of generated rows is returned.CREATE FUNCTION tableUDF(double) RETURNS TABLE (name varchar(20), job varchar(20), salary double) EXTERNAL NAME 'gwenUDF.dll:bizLogic.empOps!tableUDF' LANGUAGE CLR PARAMETER STYLE SQL NOT DETERMINISTIC FENCED THREADSAFE SCRATCHPAD 10 FINAL CALL EXECUTION CONTROL SAFE DISALLOW PARALLEL NO DBINFO// The class Person is a supporting class for // the table function UDF, tableUDF, below. class Person { private String name; private String position; private Int32 salary; public Person(String newName, String newPosition, Int32 newSalary) { this.name = newName; this.position = newPosition; this.salary = newSalary; } public String getName() { return this.name; } public String getPosition() { return this.position; } public Int32 getSalary() { return this.salary; } }class empOps { public static void TableUDF( Double factor, out String name, out String position, out Double salary, Int16 factorNullInd, out Int16 nameNullInd, out Int16 positionNullInd, out Int16 salaryNullInd, ref String sqlState, String funcName, String specName, ref String sqlMessageText, Byte[] scratchPad, Int32 callType) { Int16 intRow = 0; // Create an array of Person type information Person[] Staff = new Person[3]; Staff[0] = new Person("Gwen", "Developer", 10000); Staff[1] = new Person("Andrew", "Developer", 20000); Staff[2] = new Person("Liu", "Team Leader", 30000); salary = 0; name = position = ""; nameNullInd = positionNullInd = salaryNullInd = -1; switch(callType) { case (-2): // Case SQLUDF_TF_FIRST: break; case (-1): // Case SQLUDF_TF_OPEN: intRow = 1; scratchPad[0] = (Byte)intRow; // Write to scratchpad break; case (0): // Case SQLUDF_TF_FETCH: intRow = (Int16)scratchPad[0]; if (intRow > Staff.Length) { sqlState = "02000"; // Return an error SQLSTATE } else { // Generate a row in the output table // based on the Staff array data. name = Staff[intRow-1].getName(); position = Staff[intRow-1].getPosition(); salary = (Staff[intRow-1].getSalary()) * factor; nameNullInd = 0; positionNullInd = 0; salaryNullInd = 0; } intRow++; scratchPad[0] = (Byte)intRow; // Write scratchpad break; case (1): // Case SQLUDF_TF_CLOSE: break; case (2): // Case SQLUDF_TF_FINAL: break; } } } - Example 2: C# parameter style SQL scalar function
- This example shows the following:
- CREATE FUNCTION statement for a parameter style SQL scalar function
- C# code for a parameter style SQL scalar function
This scalar function returns a single count value for each input value that it operates on. For an input value in the nth position of the set of input values, the output scalar value is the value n. On each call of the scalar function, where one call is associated with each row or value in the input set of rows or values, the count is increased by one and the current value of the count is returned. The count is then saved in the scratchpad memory buffer to maintain the count value between each call of the scalar function.
This scalar function can be easily invoked if for example we have a table defined as follows:CREATE TABLE T (i1 INTEGER); INSERT INTO T VALUES 12, 45, 16, 99;A simple query such as the following can be used to invoke the scalar function:SELECT countUp(i1) as count, i1 FROM T;The output of such a query would be:
This scalar UDF is quite simple. Instead of returning just the count of the rows, you could use a scalar function to format data in an existing column. For example you might append a string to each value in an address column or you might build up a complex string from a series of input strings or you might do a complex mathematical evaluation over a set of data where you must store an intermediate result.COUNT I1 ----------- ---------- 1 12 2 45 3 16 4 99CREATE FUNCTION countUp(INTEGER) RETURNS INTEGER LANGUAGE CLR PARAMETER STYLE SQL SCRATCHPAD 10 FINAL CALL NO SQL FENCED THREADSAFE NOT DETERMINISTIC EXECUTION CONTROL SAFE EXTERNAL NAME 'gwenUDF.dll:bizLogic.empOps!CountUp' ;class empOps { public static void CountUp( Int32 input, out Int32 outCounter, Int16 inputNullInd, out Int16 outCounterNullInd, ref String sqlState, String funcName, String specName, ref String sqlMessageText, Byte[] scratchPad, Int32 callType) { Int32 counter = 1; switch(callType) { case -1: // case SQLUDF_FIRST_CALL scratchPad[0] = (Byte)counter; outCounter = counter; outCounterNullInd = 0; break; case 0: // case SQLUDF_NORMAL_CALL: counter = (Int32)scratchPad[0]; counter = counter + 1; outCounter = counter; outCounterNullInd = 0; scratchPad[0] = (Byte)counter; break; case 1: // case SQLUDF_FINAL_CALL: counter = (Int32)scratchPad[0]; outCounter = counter; outCounterNullInd = 0; break; default: // Should never enter here // * Required so that at compile time // out parameter outCounter is always set * outCounter = (Int32)(0); outCounterNullInd = -1; sqlState="ABCDE"; sqlMessageText = "Should not get here: Default case!"; break; } } }