</programlisting>
Although a structure is used in the <command>FETCH</command>
command, the attribute names in the <command>SELECT</command>
clause are specified one by one. This can be enhanced by using
a <literal>*</literal> to ask for all attributes of the composite
type value.
<programlisting>
...
EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).* FROM t4;
EXEC SQL OPEN cur1;
EXEC SQL WHENEVER NOT FOUND DO BREAK;
while (1)
{
/* Put all values in the SELECT list into one structure. */
EXEC SQL FETCH FROM cur1 INTO :compval;
printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr);
}
...
</programlisting>
This way, composite types can be mapped into structures almost
seamlessly, even though ECPG does not understand the composite
type itself.
</para>
<para>
Finally, it is also possible to store composite type values in
their external string representation in host variables of
type <type>char[]</type> or <type>VARCHAR[]</type>. But that
way, it is not easily possible to access the fields of the value
from the host program.
</para>
</sect3>
<sect3 id="ecpg-variables-nonprimitive-sql-user-defined-base-types">
<title>User-Defined Base Types</title>
<para>
New user-defined base types are not directly supported by ECPG.
You can use the external string representation and host variables
of type <type>char[]</type> or <type>VARCHAR[]</type>, and this
solution is indeed appropriate and sufficient for many types.
</para>
<para>
Here is an example using the data type <type>complex</type> from
the example in <xref linkend="xtypes"/>. The external string
representation of that type is <literal>(%f,%f)</literal>,
which is defined in the
functions <function>complex_in()</function>
and <function>complex_out()</function> functions
in <xref linkend="xtypes"/>. The following example inserts the
complex type values <literal>(1,1)</literal>
and <literal>(3,3)</literal> into the
columns <literal>a</literal> and <literal>b</literal>, and select
them from the table after that.
<programlisting>
EXEC SQL BEGIN DECLARE SECTION;
varchar a[64];
varchar b[64];
EXEC SQL END DECLARE SECTION;
EXEC SQL INSERT INTO test_complex VALUES ('(1,1)', '(3,3)');
EXEC SQL DECLARE cur1 CURSOR FOR SELECT a, b FROM test_complex;
EXEC SQL OPEN cur1;
EXEC SQL WHENEVER NOT FOUND DO BREAK;
while (1)
{
EXEC SQL FETCH FROM cur1 INTO :a, :b;
printf("a=%s, b=%s\n", a.arr, b.arr);
}
EXEC SQL CLOSE cur1;
</programlisting>
This example shows following result:
<screen>
a=(1,1), b=(3,3)
</screen>
</para>
<para>
Another workaround is avoiding the direct use of the user-defined
types in ECPG and instead create a function or cast that converts
between the user-defined type and a primitive type that ECPG can
handle. Note, however, that type casts, especially implicit
ones, should be introduced into the type system very carefully.
</para>
<para>
For example,
<programlisting>
CREATE FUNCTION create_complex(r double, i double) RETURNS complex
LANGUAGE SQL
IMMUTABLE
AS $$ SELECT $1 * complex '(1,0')' + $2 * complex '(0,1)' $$;
</programlisting>
After this definition, the following
<programlisting>
EXEC SQL BEGIN DECLARE SECTION;
double a, b, c, d;
EXEC SQL END DECLARE SECTION;
a = 1;
b = 2;
c = 3;
d = 4;
EXEC SQL INSERT INTO test_complex VALUES (create_complex(:a, :b), create_complex(:c, :d));
</programlisting>
has the same effect as
<programlisting>
EXEC SQL INSERT INTO test_complex VALUES ('(1,2)', '(3,4)');
</programlisting>
</para>
</sect3>
</sect2>
<sect2 id="ecpg-indicators">
<title>Indicators</title>
<para>
The examples above do not handle null values. In fact, the
retrieval examples will raise an error if they fetch a null value