diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2003-10-21 22:51:14 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2003-10-21 22:51:14 +0000 |
commit | 6fbb14a17432681f80a98f64cc810a7871a0a757 (patch) | |
tree | 17a2e4b1214aa1fae976b71515cc0c2b37c86cf8 /src | |
parent | 6c7c493a093ed8f2060015f91b3d1ffe881f1339 (diff) | |
download | postgresql-6fbb14a17432681f80a98f64cc810a7871a0a757.tar.gz postgresql-6fbb14a17432681f80a98f64cc810a7871a0a757.zip |
Update the complex-datatype example to V1 function calling conventions,
and add binary send/receive functions. Fix some other grottiness such
as failure to mark the C functions STRICT.
Diffstat (limited to 'src')
-rw-r--r-- | src/tutorial/complex.c | 217 | ||||
-rw-r--r-- | src/tutorial/complex.source | 63 |
2 files changed, 181 insertions, 99 deletions
diff --git a/src/tutorial/complex.c b/src/tutorial/complex.c index 5e8f75ae32e..38aafd9ff56 100644 --- a/src/tutorial/complex.c +++ b/src/tutorial/complex.c @@ -6,33 +6,44 @@ #include "postgres.h" +#include "fmgr.h" +#include "libpq/pqformat.h" /* needed for send/recv functions */ + + typedef struct Complex { double x; double y; } Complex; -/* These prototypes declare the requirements that Postgres places on these - user written functions. -*/ -Complex *complex_in(char *str); -char *complex_out(Complex * complex); -Complex *complex_add(Complex * a, Complex * b); -bool complex_abs_lt(Complex * a, Complex * b); -bool complex_abs_le(Complex * a, Complex * b); -bool complex_abs_eq(Complex * a, Complex * b); -bool complex_abs_ge(Complex * a, Complex * b); -bool complex_abs_gt(Complex * a, Complex * b); -int4 complex_abs_cmp(Complex * a, Complex * b); +/* + * Since we use V1 function calling convention, all these functions have + * the same signature as far as C is concerned. We provide these prototypes + * just to forestall warnings when compiled with gcc -Wmissing-prototypes. + */ +Datum complex_in(PG_FUNCTION_ARGS); +Datum complex_out(PG_FUNCTION_ARGS); +Datum complex_recv(PG_FUNCTION_ARGS); +Datum complex_send(PG_FUNCTION_ARGS); +Datum complex_add(PG_FUNCTION_ARGS); +Datum complex_abs_lt(PG_FUNCTION_ARGS); +Datum complex_abs_le(PG_FUNCTION_ARGS); +Datum complex_abs_eq(PG_FUNCTION_ARGS); +Datum complex_abs_ge(PG_FUNCTION_ARGS); +Datum complex_abs_gt(PG_FUNCTION_ARGS); +Datum complex_abs_cmp(PG_FUNCTION_ARGS); /***************************************************************************** * Input/Output functions *****************************************************************************/ -Complex * -complex_in(char *str) +PG_FUNCTION_INFO_V1(complex_in); + +Datum +complex_in(PG_FUNCTION_ARGS) { + char *str = PG_GETARG_CSTRING(0); double x, y; Complex *result; @@ -40,133 +51,173 @@ complex_in(char *str) if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("invalid input syntax for complex: \"%s\"", str))); + errmsg("invalid input syntax for complex: \"%s\"", + str))); result = (Complex *) palloc(sizeof(Complex)); result->x = x; result->y = y; - return result; + PG_RETURN_POINTER(result); } -char * -complex_out(Complex * complex) +PG_FUNCTION_INFO_V1(complex_out); + +Datum +complex_out(PG_FUNCTION_ARGS) { + Complex *complex = (Complex *) PG_GETARG_POINTER(0); char *result; - if (complex == NULL) - return NULL; - result = (char *) palloc(100); snprintf(result, 100, "(%g,%g)", complex->x, complex->y); - return result; + PG_RETURN_CSTRING(result); +} + +/***************************************************************************** + * Binary Input/Output functions + * + * These are optional. + *****************************************************************************/ + +PG_FUNCTION_INFO_V1(complex_recv); + +Datum +complex_recv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + Complex *result; + + result = (Complex *) palloc(sizeof(Complex)); + result->x = pq_getmsgfloat8(buf); + result->y = pq_getmsgfloat8(buf); + PG_RETURN_POINTER(result); +} + +PG_FUNCTION_INFO_V1(complex_send); + +Datum +complex_send(PG_FUNCTION_ARGS) +{ + Complex *complex = (Complex *) PG_GETARG_POINTER(0); + StringInfoData buf; + + pq_begintypsend(&buf); + pq_sendfloat8(&buf, complex->x); + pq_sendfloat8(&buf, complex->y); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } /***************************************************************************** * New Operators + * + * A practical Complex datatype would provide much more than this, of course. *****************************************************************************/ -Complex * -complex_add(Complex * a, Complex * b) +PG_FUNCTION_INFO_V1(complex_add); + +Datum +complex_add(PG_FUNCTION_ARGS) { + Complex *a = (Complex *) PG_GETARG_POINTER(0); + Complex *b = (Complex *) PG_GETARG_POINTER(1); Complex *result; result = (Complex *) palloc(sizeof(Complex)); result->x = a->x + b->x; result->y = a->y + b->y; - return result; + PG_RETURN_POINTER(result); } /***************************************************************************** * Operator class for defining B-tree index + * + * It's essential that the comparison operators and support function for a + * B-tree index opclass always agree on the relative ordering of any two + * data values. Experience has shown that it's depressingly easy to write + * unintentionally inconsistent functions. One way to reduce the odds of + * making a mistake is to make all the functions simple wrappers around + * an internal three-way-comparison function, as we do here. *****************************************************************************/ #define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y) -bool -complex_abs_lt(Complex * a, Complex * b) +static int +complex_abs_cmp_internal(Complex *a, Complex *b) { double amag = Mag(a), bmag = Mag(b); - return amag < bmag; + if (amag < bmag) + return -1; + if (amag > bmag) + return 1; + return 0; } -bool -complex_abs_le(Complex * a, Complex * b) + +PG_FUNCTION_INFO_V1(complex_abs_lt); + +Datum +complex_abs_lt(PG_FUNCTION_ARGS) { - double amag = Mag(a), - bmag = Mag(b); + Complex *a = (Complex *) PG_GETARG_POINTER(0); + Complex *b = (Complex *) PG_GETARG_POINTER(1); - return amag <= bmag; + PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) < 0); } -bool -complex_abs_eq(Complex * a, Complex * b) +PG_FUNCTION_INFO_V1(complex_abs_le); + +Datum +complex_abs_le(PG_FUNCTION_ARGS) { - double amag = Mag(a), - bmag = Mag(b); + Complex *a = (Complex *) PG_GETARG_POINTER(0); + Complex *b = (Complex *) PG_GETARG_POINTER(1); - return amag == bmag; + PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) <= 0); } -bool -complex_abs_ge(Complex * a, Complex * b) +PG_FUNCTION_INFO_V1(complex_abs_eq); + +Datum +complex_abs_eq(PG_FUNCTION_ARGS) { - double amag = Mag(a), - bmag = Mag(b); + Complex *a = (Complex *) PG_GETARG_POINTER(0); + Complex *b = (Complex *) PG_GETARG_POINTER(1); - return amag >= bmag; + PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) == 0); } -bool -complex_abs_gt(Complex * a, Complex * b) +PG_FUNCTION_INFO_V1(complex_abs_ge); + +Datum +complex_abs_ge(PG_FUNCTION_ARGS) { - double amag = Mag(a), - bmag = Mag(b); + Complex *a = (Complex *) PG_GETARG_POINTER(0); + Complex *b = (Complex *) PG_GETARG_POINTER(1); - return amag > bmag; + PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) >= 0); } -int4 -complex_abs_cmp(Complex * a, Complex * b) +PG_FUNCTION_INFO_V1(complex_abs_gt); + +Datum +complex_abs_gt(PG_FUNCTION_ARGS) { - double amag = Mag(a), - bmag = Mag(b); + Complex *a = (Complex *) PG_GETARG_POINTER(0); + Complex *b = (Complex *) PG_GETARG_POINTER(1); - if (amag < bmag) - return -1; - else if (amag > bmag) - return 1; - else - return 0; + PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) > 0); } -/***************************************************************************** - * test code - *****************************************************************************/ +PG_FUNCTION_INFO_V1(complex_abs_cmp); -/* - * You should always test your code separately. Trust me, using POSTGRES to - * debug your C function will be very painful and unproductive. In case of - * POSTGRES crashing, it is impossible to tell whether the bug is in your - * code or POSTGRES's. - */ -void test_main(void); -void -test_main() +Datum +complex_abs_cmp(PG_FUNCTION_ARGS) { - Complex *a; - Complex *b; - - a = complex_in("(4.01, 3.77 )"); - printf("a = %s\n", complex_out(a)); - b = complex_in("(1.0,2.0)"); - printf("b = %s\n", complex_out(b)); - printf("a + b = %s\n", complex_out(complex_add(a, b))); - printf("a < b = %d\n", complex_abs_lt(a, b)); - printf("a <= b = %d\n", complex_abs_le(a, b)); - printf("a = b = %d\n", complex_abs_eq(a, b)); - printf("a >= b = %d\n", complex_abs_ge(a, b)); - printf("a > b = %d\n", complex_abs_gt(a, b)); + Complex *a = (Complex *) PG_GETARG_POINTER(0); + Complex *b = (Complex *) PG_GETARG_POINTER(1); + + PG_RETURN_INT32(complex_abs_cmp_internal(a, b)); } diff --git a/src/tutorial/complex.source b/src/tutorial/complex.source index db33d9100e7..9050e82a594 100644 --- a/src/tutorial/complex.source +++ b/src/tutorial/complex.source @@ -8,20 +8,25 @@ -- Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group -- Portions Copyright (c) 1994, Regents of the University of California -- --- $Header: /cvsroot/pgsql/src/tutorial/complex.source,v 1.15 2003/08/04 23:59:41 tgl Exp $ +-- $Header: /cvsroot/pgsql/src/tutorial/complex.source,v 1.16 2003/10/21 22:51:14 tgl Exp $ -- --------------------------------------------------------------------------- ----------------------------- -- Creating a new type: --- a user-defined type must have an input and an output function. They --- are user-defined C functions. We are going to create a new type --- called 'complex' which represents complex numbers. +-- We are going to create a new type called 'complex' which represents +-- complex numbers. +-- A user-defined type must have an input and an output function, and +-- optionally can have binary input and output functions. All of these +-- are usually user-defined C functions. ----------------------------- -- Assume the user defined functions are in _OBJWD_/complex$DLSUFFIX --- (we do not want to assume this is in the dynamic loader search path) --- Look at $PWD/complex.c for the source. +-- (we do not want to assume this is in the dynamic loader search path). +-- Look at $PWD/complex.c for the source. Note that we declare all of +-- them as STRICT, so we do not need to cope with NULL inputs in the +-- C code. We also mark them IMMUTABLE, since they always return the +-- same outputs given the same inputs. -- the input function 'complex_in' takes a null-terminated string (the -- textual representation of the type) and turns it into the internal @@ -31,7 +36,7 @@ CREATE FUNCTION complex_in(cstring) RETURNS complex AS '_OBJWD_/complex' - LANGUAGE 'c'; + LANGUAGE C IMMUTABLE STRICT; -- the output function 'complex_out' takes the internal representation and -- converts it into the textual representation. @@ -39,7 +44,24 @@ CREATE FUNCTION complex_in(cstring) CREATE FUNCTION complex_out(complex) RETURNS cstring AS '_OBJWD_/complex' - LANGUAGE 'c'; + LANGUAGE C IMMUTABLE STRICT; + +-- the binary input function 'complex_recv' takes a StringInfo buffer +-- and turns its contents into the internal representation. + +CREATE FUNCTION complex_recv(internal) + RETURNS complex + AS '_OBJWD_/complex' + LANGUAGE C IMMUTABLE STRICT; + +-- the binary output function 'complex_send' takes the internal representation +-- and converts it into a (hopefully) platform-independent bytea string. + +CREATE FUNCTION complex_send(complex) + RETURNS bytea + AS '_OBJWD_/complex' + LANGUAGE C IMMUTABLE STRICT; + -- now, we can create the type. The internallength specifies the size of the -- memory block required to hold the type (we need two 8-byte doubles). @@ -48,6 +70,8 @@ CREATE TYPE complex ( internallength = 16, input = complex_in, output = complex_out, + receive = complex_recv, + send = complex_send, alignment = double ); @@ -57,7 +81,7 @@ CREATE TYPE complex ( -- user-defined types can be used like ordinary built-in types. ----------------------------- --- eg. we can use it in a schema +-- eg. we can use it in a table CREATE TABLE test_complex ( a complex, @@ -84,7 +108,7 @@ SELECT * FROM test_complex; CREATE FUNCTION complex_add(complex, complex) RETURNS complex AS '_OBJWD_/complex' - LANGUAGE 'c'; + LANGUAGE C IMMUTABLE STRICT; -- we can now define the operator. We show a binary operator here but you -- can also define unary operators by omitting either of leftarg or rightarg. @@ -132,40 +156,47 @@ SELECT complex_sum(a) FROM test_complex; -- first, define the required operators CREATE FUNCTION complex_abs_lt(complex, complex) RETURNS bool - AS '_OBJWD_/complex' LANGUAGE 'c'; + AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT; CREATE FUNCTION complex_abs_le(complex, complex) RETURNS bool - AS '_OBJWD_/complex' LANGUAGE 'c'; + AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT; CREATE FUNCTION complex_abs_eq(complex, complex) RETURNS bool - AS '_OBJWD_/complex' LANGUAGE 'c'; + AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT; CREATE FUNCTION complex_abs_ge(complex, complex) RETURNS bool - AS '_OBJWD_/complex' LANGUAGE 'c'; + AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT; CREATE FUNCTION complex_abs_gt(complex, complex) RETURNS bool - AS '_OBJWD_/complex' LANGUAGE 'c'; + AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR < ( leftarg = complex, rightarg = complex, procedure = complex_abs_lt, + commutator = > , negator = >= , restrict = scalarltsel, join = scalarltjoinsel ); CREATE OPERATOR <= ( leftarg = complex, rightarg = complex, procedure = complex_abs_le, + commutator = >= , negator = > , restrict = scalarltsel, join = scalarltjoinsel ); CREATE OPERATOR = ( leftarg = complex, rightarg = complex, procedure = complex_abs_eq, + commutator = = , + -- leave out negator since we didn't create <> operator + -- negator = <> , restrict = eqsel, join = eqjoinsel ); CREATE OPERATOR >= ( leftarg = complex, rightarg = complex, procedure = complex_abs_ge, + commutator = <= , negator = < , restrict = scalargtsel, join = scalargtjoinsel ); CREATE OPERATOR > ( leftarg = complex, rightarg = complex, procedure = complex_abs_gt, + commutator = < , negator = <= , restrict = scalargtsel, join = scalargtjoinsel ); -- create the support function too CREATE FUNCTION complex_abs_cmp(complex, complex) RETURNS int4 - AS '_OBJWD_/complex' LANGUAGE 'c'; + AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT; -- now we can make the operator class CREATE OPERATOR CLASS complex_abs_ops |