</itemizedlist>
</para>
<para>
The least error-prone way to define a related set of comparison operators
is to write the B-tree comparison support function first, and then write the
other functions as one-line wrappers around the support function. This
reduces the odds of getting inconsistent results for corner cases.
Following this approach, we first write:
<programlisting><![CDATA[
#define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y)
static int
complex_abs_cmp_internal(Complex *a, Complex *b)
{
double amag = Mag(a),
bmag = Mag(b);
if (amag < bmag)
return -1;
if (amag > bmag)
return 1;
return 0;
}
]]>
</programlisting>
Now the less-than function looks like:
<programlisting><![CDATA[
PG_FUNCTION_INFO_V1(complex_abs_lt);
Datum
complex_abs_lt(PG_FUNCTION_ARGS)
{
Complex *a = (Complex *) PG_GETARG_POINTER(0);
Complex *b = (Complex *) PG_GETARG_POINTER(1);
PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) < 0);
}
]]>
</programlisting>
The other four functions differ only in how they compare the internal
function's result to zero.
</para>
<para>
Next we declare the functions and the operators based on the functions
to SQL:
<programlisting>
CREATE FUNCTION complex_abs_lt(complex, complex) RETURNS bool
AS '<replaceable>filename</replaceable>', 'complex_abs_lt'
LANGUAGE C IMMUTABLE STRICT;
CREATE OPERATOR < (
leftarg = complex, rightarg = complex, procedure = complex_abs_lt,
commutator = > , negator = >= ,
restrict = scalarltsel, join = scalarltjoinsel
);
</programlisting>
It is important to specify the correct commutator and negator operators,
as well as suitable restriction and join selectivity
functions, otherwise the optimizer will be unable to make effective
use of the index.
</para>
<para>
Other things worth noting are happening here:
<itemizedlist>
<listitem>
<para>
There can only be one operator named, say, <literal>=</literal>
and taking type <type>complex</type> for both operands. In this
case we don't have any other operator <literal>=</literal> for
<type>complex</type>, but if we were building a practical data
type we'd probably want <literal>=</literal> to be the ordinary
equality operation for complex numbers (and not the equality of
the absolute values). In that case, we'd need to use some other
operator name for <function>complex_abs_eq</function>.
</para>
</listitem>
<listitem>
<para>
Although <productname>PostgreSQL</productname> can cope with
functions having the same SQL name as long as they have different
argument data types, C can only cope with one global function
having a given name. So we shouldn't name the C function
something simple like <filename>abs_eq</filename>. Usually it's
a good practice to include the data type name in the C function
name, so as not to conflict with functions for other data types.
</para>
</listitem>
<listitem>
<para>
We could have made the SQL name
of the function <filename>abs_eq</filename>, relying on
<productname>PostgreSQL</productname> to distinguish it by
argument data types from any other SQL function of the same name.
To keep the example simple, we make the function have the same
names at the C level and SQL level.
</para>
</listitem>
</itemizedlist>
</para>
<para>
The next step is the registration of the support routine required
by B-trees. The example C code that implements this is in the same
file that contains the operator functions. This is how we declare
the function:
<programlisting>
CREATE FUNCTION complex_abs_cmp(complex, complex)
RETURNS integer
AS '<replaceable>filename</replaceable>'
LANGUAGE C IMMUTABLE STRICT;
</programlisting>
</para>
<para>
Now that we have the required operators and support routine,
we can