If your data model takes advantage of subtypes, a value
in a column could be one of many different subtypes. You can dynamically
choose the correct transform functions based on the actual input type.
About this task
Suppose you want to issue the following SELECT statement:
SELECT Address
FROM Person
INTO :hvaddr;
The application has no way of knowing
whether an instance of Address_t
, US_addr_t
,
or so on, will be returned. To keep the example from being too complex, let
it be assumed that only Address_t
or US_addr_t
can
be returned. The structures of these types are different, so the transforms
that decompose the attributes must be different.
Procedure
To ensure that the proper transforms are invoked:
- Create a FROM SQL function transform
for each variation of address:
CREATE FUNCTION addresstofunc(A address_t)
RETURNS ROW
(Street VARCHAR(30), Number CHAR(15), City
VARCHAR(30), STATE VARCHAR (10))
LANGUAGE SQL
RETURN VALUES
(A..Street, A..Number, A..City, A..State)
CREATE FUNCTION US_addresstofunc(A US_addr_t)
RETURNS ROW
(Street VARCHAR(30), Number CHAR(15), City
VARCHAR(30), STATE VARCHAR (10), Zip
CHAR(10))
LANGUAGE SQL
RETURN VALUES
(A..Street, A..Number, A..City, A..State, A..Zip)
- Create transform groups, one for each
type variation:
CREATE TRANSFORM FOR Address_t
funcgroup1 (FROM SQL WITH FUNCTION addresstofunc)
CREATE TRANSFORM FOR US_addr_t
funcgroup2 (FROM SQL WITH FUNCTION US_addresstofunc)
- Create external UDFs, one for each type
variation.
Register the external UDF for the Address_t
type:
CREATE FUNCTION address_to_client (A Address_t)
RETURNS VARCHAR(150)
LANGUAGE C
EXTERNAL NAME 'addressudf!address_to_client'
...
TRANSFORM GROUP funcgroup1
Write
the address_to_client
UDF:
void SQL_API_FN address_to_client(
SQLUDF_VARCHAR *street,
SQLUDF_CHAR *number,
SQLUDF_VARCHAR *city,
SQLUDF_VARCHAR *state,
SQLUDF_VARCHAR *output,
/* Null indicators for attributes */
SQLUDF_NULLIND *street_ind,
SQLUDF_NULLIND *number_ind,
SQLUDF_NULLIND *city_ind,
SQLUDF_NULLIND *state_ind,
/* Null indicator for instance */
SQLUDF_NULLIND *address_ind,
/* Null indicator for output */
SQLUDF_NULLIND *output_ind,
SQLUDF_TRAIL_ARGS)
{
sprintf (output, "[address_t] [Street:%s] [number:%s]
[city:%s] [state:%s]",
street, number, city, state);
*output_ind = 0;
}
Register the external UDF for
the US_addr_t
type:
CREATE FUNCTION address_to_client (A US_addr_t)
RETURNS VARCHAR(150)
LANGUAGE C
EXTERNAL NAME 'addressudf!US_addr_to_client'
...
TRANSFORM GROUP funcgroup2
Write
the US_addr_to_client
UDF:
void SQL_API_FN US_address_to_client(
SQLUDF_VARCHAR *street,
SQLUDF_CHAR *number,
SQLUDF_VARCHAR *city,
SQLUDF_VARCHAR *state,
SQLUDF_CHAR *zip,
SQLUDF_VARCHAR *output,
/* Null indicators */
SQLUDF_NULLIND *street_ind,
SQLUDF_NULLIND *number_ind,
SQLUDF_NULLIND *city_ind,
SQLUDF_NULLIND *state_ind,
SQLUDF_NULLIND *zip_ind,
SQLUDF_NULLIND *us_address_ind,
SQLUDF_NULLIND *output_ind,
SQLUDF_TRAIL_ARGS)
{
sprintf (output, "[US_addr_t] [Street:%s] [number:%s]
[city:%s] [state:%s] [zip:%s]",
street, number, city, state, zip);
*output_ind = 0;
}
- Create a SQL-bodied UDF that chooses the
correct external UDF to process the instance.
The following
UDF uses the TREAT specification in SELECT statements combined by
a UNION ALL clause to invoke the correct FROM SQL client transform:
CREATE FUNCTION addr_stream (ab Address_t)
RETURNS VARCHAR(150)
LANGUAGE SQL
RETURN
WITH temp(addr) AS
(SELECT address_to_client(ta.a)
FROM TABLE (VALUES (ab)) AS ta(a)
WHERE ta.a IS OF (ONLY Address_t)
UNION ALL
SELECT address_to_client(TREAT (tb.a AS US_addr_t))
FROM TABLE (VALUES (ab)) AS tb(a)
WHERE tb.a IS OF (ONLY US_addr_t))
SELECT addr FROM temp;
At this point,
applications can invoke the appropriate external UDF by invoking the Addr_stream
function:
SELECT Addr_stream(Address)
FROM Employee;
- Add the
Addr_stream
external
UDF as a FROM SQL client transform for Address_t
:
CREATE TRANSFORM GROUP FOR Address_t
client_group (FROM SQL
WITH FUNCTION Addr_stream)
Note: If
your application might use a type predicate to specify particular
address types in the query, add Addr_stream
as a
FROM SQL to client transform for US_addr_t
. This
ensures that Addr_stream
can be invoked when a query
specifically requests instances of US_addr_t
.
- Bind the application with the TRANSFORM
GROUP option set to client_group.
PREP myprogram TRANSFORM GROUP client_group
What to do next
When Db2
binds the application that contains the SELECT Address FROM Person INTO :hvar
statement, Db2
looks for a FROM SQL client transform. Db2 recognizes that a
structured type is being bound out, and looks in the transform group client_group
because that is the TRANSFORM GROUP specified at bind time in Step 6.
The
transform group contains the transform function Addr_stream
associated
with the root type Address_t
in Step 5. Addr_stream
is
a SQL-bodied function, defined in Step 4,
so it has no dependency on any other transform function. The Addr_stream
function
returns VARCHAR(150), the data type required by the :hvaddr
host
variable.
The Addr_stream
function takes an
input value of type Address_t
, which can be substituted
with US_addr_t
in this example, and determines the
dynamic type of the input value. When Addr_stream
determines
the dynamic type, it invokes the corresponding external UDF on the
value: address_to_client
if the dynamic type is Address_t
;
or USaddr_to_client
if the dynamic type is US_addr_t
.
These two UDFs are defined in Step 3.
Each UDF decomposes their corresponding structured type to VARCHAR(150),
the type required by the Addr_stream
transform function.
To
accept the structured types as input, each UDF needs a FROM SQL transform
function to decompose the input structured type instance into individual
attribute parameters. The CREATE FUNCTION statements in Step 3 name the TRANSFORM GROUP that
contains these transforms.
The CREATE FUNCTION statements for
the transform functions are issued in Step 1. The CREATE TRANSFORM statements
that associate the transform functions with their transform groups
are issued in Step 2.