SGML-escaping.
-<!-- $PostgreSQL: pgsql/doc/src/sgml/auto-explain.sgml,v 1.1 2008/11/19 02:59:28 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/auto-explain.sgml,v 1.2 2008/12/07 23:46:39 alvherre Exp $ -->
<sect1 id="auto-explain">
<title>auto_explain</title>
This might produce log output such as:
</para>
- <programlisting>
+ <programlisting><![CDATA[
LOG: duration: 0.986 ms plan:
Aggregate (cost=14.90..14.91 rows=1 width=0)
- -> Hash Join (cost=3.91..14.70 rows=81 width=0)
+ -> Hash Join (cost=3.91..14.70 rows=81 width=0)
Hash Cond: (pg_class.oid = pg_index.indrelid)
- -> Seq Scan on pg_class (cost=0.00..8.27 rows=227 width=4)
- -> Hash (cost=2.90..2.90 rows=81 width=4)
- -> Seq Scan on pg_index (cost=0.00..2.90 rows=81 width=4)
+ -> Seq Scan on pg_class (cost=0.00..8.27 rows=227 width=4)
+ -> Hash (cost=2.90..2.90 rows=81 width=4)
+ -> Seq Scan on pg_index (cost=0.00..2.90 rows=81 width=4)
Filter: indisunique
STATEMENT: SELECT count(*)
FROM pg_class, pg_index
WHERE oid = indrelid AND indisunique;
+]]>
</programlisting>
</sect2>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/ecpg.sgml,v 1.86 2008/06/12 19:15:40 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/ecpg.sgml,v 1.87 2008/12/07 23:46:39 alvherre Exp $ -->
<chapter id="ecpg">
<title><application>ECPG</application> - Embedded <acronym>SQL</acronym> in C</title>
</programlisting>
If the statement you are executing returns values, then add an
<literal>INTO</literal> clause:
-<programlisting>
+<programlisting><![CDATA[
EXEC SQL BEGIN DECLARE SECTION;
-const char *stmt = "SELECT a, b, c FROM test1 WHERE a > ?";
+const char *stmt = "SELECT a, b, c FROM test1 WHERE a > ?";
int v1, v2;
VARCHAR v3;
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE mystmt FROM :stmt;
...
EXEC SQL EXECUTE mystmt INTO v1, v2, v3 USING 37;
+]]>
</programlisting>
An <command>EXECUTE</command> command can have an
<literal>INTO</literal> clause, a <literal>USING</literal> clause,
functions to do basic calculations with those types within C, i.e. without
the help of the <productname>PostgreSQL</productname> server. See the
following example:
-<programlisting>
+<programlisting><![CDATA[
EXEC SQL BEGIN DECLARE SECTION;
date date1;
timestamp ts1, tsout;
char *out;
EXEC SQL END DECLARE SECTION;
-PGTYPESdate_today(&date1);
+PGTYPESdate_today(&date1);
EXEC SQL SELECT started, duration INTO :ts1, :iv1 FROM datetbl WHERE d=:date1;
-PGTYPEStimestamp_add_interval(&ts1, &iv1, &tsout);
-out = PGTYPEStimestamp_to_asc(&tsout);
+PGTYPEStimestamp_add_interval(&ts1, &iv1, &tsout);
+out = PGTYPEStimestamp_to_asc(&tsout);
printf("Started + duration: %s\n", out);
free(out);
+]]>
</programlisting>
</para>
<para>
Here is an example of a call to this function:
-<programlisting>
+<programlisting><![CDATA[
$char c[] = "abc ";
$short s = 17;
$int i = -74874;
rsetnull(CCHARTYPE, (char *) c);
-rsetnull(CSHORTTYPE, (char *) &s);
-rsetnull(CINTTYPE, (char *) &i);
+rsetnull(CSHORTTYPE, (char *) &s);
+rsetnull(CINTTYPE, (char *) &i);
+]]>
</programlisting>
</para>
</listitem>
</para>
<para>
Here is an example of how to use this function:
-<programlisting>
+<programlisting><![CDATA[
$char c[] = "abc ";
$short s = 17;
$int i = -74874;
risnull(CCHARTYPE, (char *) c);
-risnull(CSHORTTYPE, (char *) &s);
-risnull(CINTTYPE, (char *) &i);
+risnull(CSHORTTYPE, (char *) &s);
+risnull(CINTTYPE, (char *) &i);
+]]>
</programlisting>
</para>
</listitem>
EXEC SQL SELECT res INTO :result FROM mytable WHERE index = :index;
</programlisting>
is translated into:
-<programlisting>
+<programlisting><![CDATA[
/* Processed by ecpg (2.6.0) */
/* These two include files are added by the preprocessor */
-#include <ecpgtype.h>;
-#include <ecpglib.h>;
+#include <ecpgtype.h>;
+#include <ecpglib.h>;
/* exec sql begin declare section */
/* exec sql end declare section */
...
ECPGdo(__LINE__, NULL, "SELECT res FROM mytable WHERE index = ? ",
- ECPGt_int,&(index),1L,1L,sizeof(int),
+ ECPGt_int,&(index),1L,1L,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT,
- ECPGt_int,&(result),1L,1L,sizeof(int),
+ ECPGt_int,&(result),1L,1L,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
#line 147 "foo.pgc"
+]]>
</programlisting>
(The indentation here is added for readability and not
something the preprocessor does.)
-<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.272 2008/12/02 12:42:11 mha Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.273 2008/12/07 23:46:39 alvherre Exp $ -->
<chapter id="libpq">
<title><application>libpq</application> - C Library</title>
</para>
<programlisting>
+<![CDATA[
/* required header for libpq events (note: includes libpq-fe.h) */
-#include <libpq-events.h>
+#include <libpq-events.h>
/* The instanceData */
typedef struct
case PGEVT_REGISTER:
{
PGEventRegister *e = (PGEventRegister *)evtInfo;
- mydata *data = get_mydata(e->conn);
+ mydata *data = get_mydata(e->conn);
/* associate app specific data with connection */
- PQsetInstanceData(e->conn, myEventProc, data);
+ PQsetInstanceData(e->conn, myEventProc, data);
break;
}
case PGEVT_CONNRESET:
{
PGEventConnReset *e = (PGEventConnReset *)evtInfo;
- mydata *data = PQinstanceData(e->conn, myEventProc);
+ mydata *data = PQinstanceData(e->conn, myEventProc);
if (data)
memset(data, 0, sizeof(mydata));
case PGEVT_CONNDESTROY:
{
PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo;
- mydata *data = PQinstanceData(e->conn, myEventProc);
+ mydata *data = PQinstanceData(e->conn, myEventProc);
/* free instance data because the conn is being destroyed */
if (data)
case PGEVT_RESULTCREATE:
{
PGEventResultCreate *e = (PGEventResultCreate *)evtInfo;
- mydata *conn_data = PQinstanceData(e->conn, myEventProc);
+ mydata *conn_data = PQinstanceData(e->conn, myEventProc);
mydata *res_data = dup_mydata(conn_data);
/* associate app specific data with result (copy it from conn) */
- PQsetResultInstanceData(e->result, myEventProc, res_data);
+ PQsetResultInstanceData(e->result, myEventProc, res_data);
break;
}
case PGEVT_RESULTCOPY:
{
PGEventResultCopy *e = (PGEventResultCopy *)evtInfo;
- mydata *src_data = PQresultInstanceData(e->src, myEventProc);
+ mydata *src_data = PQresultInstanceData(e->src, myEventProc);
mydata *dest_data = dup_mydata(src_data);
/* associate app specific data with result (copy it from a result) */
- PQsetResultInstanceData(e->dest, myEventProc, dest_data);
+ PQsetResultInstanceData(e->dest, myEventProc, dest_data);
break;
}
case PGEVT_RESULTDESTROY:
{
PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo;
- mydata *data = PQresultInstanceData(e->result, myEventProc);
+ mydata *data = PQresultInstanceData(e->result, myEventProc);
/* free instance data because the result is being destroyed */
if (data)
return TRUE; /* event processing succeeded */
}
+]]>
</programlisting>
</sect2>
</sect1>
<title><application>libpq</application> Example Program 1</title>
<programlisting>
+<![CDATA[
/*
* testlibpq.c
*
* Test the C version of libpq, the PostgreSQL frontend library.
*/
-#include <stdio.h>
-#include <stdlib.h>
+#include <stdio.h>
+#include <stdlib.h>
#include "libpq-fe.h"
static void
* conninfo string; otherwise default to setting dbname=postgres and using
* environment variables or defaults for all other connection parameters.
*/
- if (argc > 1)
+ if (argc > 1)
conninfo = argv[1];
else
conninfo = "dbname = postgres";
/* first, print out the attribute names */
nFields = PQnfields(res);
- for (i = 0; i < nFields; i++)
+ for (i = 0; i < nFields; i++)
printf("%-15s", PQfname(res, i));
printf("\n\n");
/* next, print out the rows */
- for (i = 0; i < PQntuples(res); i++)
+ for (i = 0; i < PQntuples(res); i++)
{
- for (j = 0; j < nFields; j++)
+ for (j = 0; j < nFields; j++)
printf("%-15s", PQgetvalue(res, i, j));
printf("\n");
}
return 0;
}
+]]>
</programlisting>
</example>
<title><application>libpq</application> Example Program 2</title>
<programlisting>
+<![CDATA[
/*
* testlibpq2.c
* Test of the asynchronous notification interface
*
* INSERT INTO TBL1 VALUES (10);
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
#include "libpq-fe.h"
static void
* conninfo string; otherwise default to setting dbname=postgres and using
* environment variables or defaults for all other connection parameters.
*/
- if (argc > 1)
+ if (argc > 1)
conninfo = argv[1];
else
conninfo = "dbname = postgres";
/* Quit after four notifies are received. */
nnotifies = 0;
- while (nnotifies < 4)
+ while (nnotifies < 4)
{
/*
* Sleep until something happens on the connection. We use select(2)
sock = PQsocket(conn);
- if (sock < 0)
+ if (sock < 0)
break; /* shouldn't happen */
- FD_ZERO(&input_mask);
- FD_SET(sock, &input_mask);
+ FD_ZERO(&input_mask);
+ FD_SET(sock, &input_mask);
- if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0)
+ if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0)
{
fprintf(stderr, "select() failed: %s\n", strerror(errno));
exit_nicely(conn);
{
fprintf(stderr,
"ASYNC NOTIFY of '%s' received from backend pid %d\n",
- notify->relname, notify->be_pid);
+ notify->relname, notify->be_pid);
PQfreemem(notify);
nnotifies++;
}
return 0;
}
+]]>
</programlisting>
</example>
<title><application>libpq</application> Example Program 3</>
<programlisting>
+<![CDATA[
/*
* testlibpq3.c
* Test out-of-line parameters and binary I/O.
* t = (8 bytes) 'ho there'
* b = (5 bytes) \004\003\002\001\000
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
#include "libpq-fe.h"
/* for ntohl/htonl */
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
static void
t_fnum = PQfnumber(res, "t");
b_fnum = PQfnumber(res, "b");
- for (i = 0; i < PQntuples(res); i++)
+ for (i = 0; i < PQntuples(res); i++)
{
char *iptr;
char *tptr;
printf(" t = (%d bytes) '%s'\n",
PQgetlength(res, i, t_fnum), tptr);
printf(" b = (%d bytes) ", blen);
- for (j = 0; j < blen; j++)
+ for (j = 0; j < blen; j++)
printf("\\%03o", bptr[j]);
printf("\n\n");
}
* conninfo string; otherwise default to setting dbname=postgres and using
* environment variables or defaults for all other connection parameters.
*/
- if (argc > 1)
+ if (argc > 1)
conninfo = argv[1];
else
conninfo = "dbname = postgres";
binaryIntVal = htonl((uint32_t) 2);
/* Set up parameter arrays for PQexecParams */
- paramValues[0] = (char *) &binaryIntVal;
+ paramValues[0] = (char *) &binaryIntVal;
paramLengths[0] = sizeof(binaryIntVal);
paramFormats[0] = 1; /* binary */
return 0;
}
+]]>
</programlisting>
</example>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.48 2008/03/22 01:55:14 ishii Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.49 2008/12/07 23:46:39 alvherre Exp $ -->
<chapter id="largeObjects">
<title id="largeObjects-title">Large Objects</title>
<example id="lo-example">
<title>Large Objects with <application>libpq</application> Example Program</title>
-<programlisting>
+<programlisting><![CDATA[
/*--------------------------------------------------------------
*
* testlo.c--
*
*--------------------------------------------------------------
*/
-#include <stdio.h>
-#include "libpq-fe.h"
-#include "libpq/libpq-fs.h"
+#include <stdio.h>
+#include "libpq-fe.h"
+#include "libpq/libpq-fs.h"
#define BUFSIZE 1024
/*
* importFile
- * import file "in_filename" into database as large object "lobjOid"
+ * import file "in_filename" into database as large object "lobjOid"
*
*/
Oid
* open the file to be read in
*/
fd = open(filename, O_RDONLY, 0666);
- if (fd < 0)
+ if (fd < 0)
{ /* error */
- fprintf(stderr, "cannot open unix file %s\n", filename);
+ fprintf(stderr, "cannot open unix file %s\n", filename);
}
/*
*/
lobjId = lo_creat(conn, INV_READ | INV_WRITE);
if (lobjId == 0)
- fprintf(stderr, "cannot create large object\n");
+ fprintf(stderr, "cannot create large object\n");
lobj_fd = lo_open(conn, lobjId, INV_WRITE);
/*
* read in from the Unix file and write to the inversion file
*/
- while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
+ while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
{
tmp = lo_write(conn, lobj_fd, buf, nbytes);
- if (tmp < nbytes)
- fprintf(stderr, "error while reading large object\n");
+ if (tmp < nbytes)
+ fprintf(stderr, "error while reading large object\n");
}
(void) close(fd);
int nread;
lobj_fd = lo_open(conn, lobjId, INV_READ);
- if (lobj_fd < 0)
+ if (lobj_fd < 0)
{
- fprintf(stderr, "cannot open large object %d\n",
+ fprintf(stderr, "cannot open large object %d\n",
lobjId);
}
buf = malloc(len + 1);
nread = 0;
- while (len - nread > 0)
+ while (len - nread > 0)
{
nbytes = lo_read(conn, lobj_fd, buf, len - nread);
buf[nbytes] = ' ';
- fprintf(stderr, ">>> %s", buf);
+ fprintf(stderr, ">>> %s", buf);
nread += nbytes;
}
free(buf);
- fprintf(stderr, "\n");
+ fprintf(stderr, "\n");
lo_close(conn, lobj_fd);
}
int i;
lobj_fd = lo_open(conn, lobjId, INV_WRITE);
- if (lobj_fd < 0)
+ if (lobj_fd < 0)
{
- fprintf(stderr, "cannot open large object %d\n",
+ fprintf(stderr, "cannot open large object %d\n",
lobjId);
}
lo_lseek(conn, lobj_fd, start, SEEK_SET);
buf = malloc(len + 1);
- for (i = 0; i < len; i++)
+ for (i = 0; i < len; i++)
buf[i] = 'X';
buf[i] = ' ';
nwritten = 0;
- while (len - nwritten > 0)
+ while (len - nwritten > 0)
{
nbytes = lo_write(conn, lobj_fd, buf + nwritten, len - nwritten);
nwritten += nbytes;
}
free(buf);
- fprintf(stderr, "\n");
+ fprintf(stderr, "\n");
lo_close(conn, lobj_fd);
}
/*
* exportFile
- * export large object "lobjOid" to file "out_filename"
+ * export large object "lobjOid" to file "out_filename"
*
*/
void
* open the large object
*/
lobj_fd = lo_open(conn, lobjId, INV_READ);
- if (lobj_fd < 0)
+ if (lobj_fd < 0)
{
- fprintf(stderr, "cannot open large object %d\n",
+ fprintf(stderr, "cannot open large object %d\n",
lobjId);
}
* open the file to be written to
*/
fd = open(filename, O_CREAT | O_WRONLY, 0666);
- if (fd < 0)
+ if (fd < 0)
{ /* error */
- fprintf(stderr, "cannot open unix file %s\n",
+ fprintf(stderr, "cannot open unix file %s\n",
filename);
}
/*
* read in from the inversion file and write to the Unix file
*/
- while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0)
+ while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0)
{
tmp = write(fd, buf, nbytes);
- if (tmp < nbytes)
+ if (tmp < nbytes)
{
- fprintf(stderr, "error while writing %s\n",
+ fprintf(stderr, "error while writing %s\n",
filename);
}
}
if (argc != 4)
{
- fprintf(stderr, "Usage: %s database_name in_filename out_filename\n",
+ fprintf(stderr, "Usage: %s database_name in_filename out_filename\n",
argv[0]);
exit(1);
}
/* check to see that the backend connection was successfully made */
if (PQstatus(conn) == CONNECTION_BAD)
{
- fprintf(stderr, "Connection to database '%s' failed.\n", database);
- fprintf(stderr, "%s", PQerrorMessage(conn));
+ fprintf(stderr, "Connection to database '%s' failed.\n", database);
+ fprintf(stderr, "%s", PQerrorMessage(conn));
exit_nicely(conn);
}
- res = PQexec(conn, "begin");
+ res = PQexec(conn, "begin");
PQclear(res);
- printf("importing file %s\n", in_filename);
+ printf("importing file %s\n", in_filename);
/* lobjOid = importFile(conn, in_filename); */
lobjOid = lo_import(conn, in_filename);
/*
- printf("as large object %d.\n", lobjOid);
+ printf("as large object %d.\n", lobjOid);
- printf("picking out bytes 1000-2000 of the large object\n");
+ printf("picking out bytes 1000-2000 of the large object\n");
pickout(conn, lobjOid, 1000, 1000);
- printf("overwriting bytes 1000-2000 of the large object with X's\n");
+ printf("overwriting bytes 1000-2000 of the large object with X's\n");
overwrite(conn, lobjOid, 1000, 1000);
*/
- printf("exporting large object to file %s\n", out_filename);
+ printf("exporting large object to file %s\n", out_filename);
/* exportFile(conn, lobjOid, out_filename); */
lo_export(conn, lobjOid, out_filename);
- res = PQexec(conn, "end");
+ res = PQexec(conn, "end");
PQclear(res);
PQfinish(conn);
exit(0);
}
+]]>
</programlisting>
</example>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.52 2008/03/28 00:21:55 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.53 2008/12/07 23:46:39 alvherre Exp $ -->
<chapter id="triggers">
<title>Triggers</title>
<para>
This is the source code of the trigger function:
-<programlisting>
+<programlisting><![CDATA[
#include "postgres.h"
#include "executor/spi.h" /* this is what you need to work with SPI */
#include "commands/trigger.h" /* ... and triggers */
Datum
trigf(PG_FUNCTION_ARGS)
{
- TriggerData *trigdata = (TriggerData *) fcinfo->context;
+ TriggerData *trigdata = (TriggerData *) fcinfo->context;
TupleDesc tupdesc;
HeapTuple rettuple;
char *when;
elog(ERROR, "trigf: not called by trigger manager");
/* tuple to return to executor */
- if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- rettuple = trigdata->tg_newtuple;
+ if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
+ rettuple = trigdata->tg_newtuple;
else
- rettuple = trigdata->tg_trigtuple;
+ rettuple = trigdata->tg_trigtuple;
/* check for null values */
- if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)
- && TRIGGER_FIRED_BEFORE(trigdata->tg_event))
+ if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)
+ && TRIGGER_FIRED_BEFORE(trigdata->tg_event))
checknull = true;
- if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
+ if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
when = "before";
else
when = "after ";
- tupdesc = trigdata->tg_relation->rd_att;
+ tupdesc = trigdata->tg_relation->rd_att;
/* connect to SPI manager */
- if ((ret = SPI_connect()) < 0)
+ if ((ret = SPI_connect()) < 0)
elog(INFO, "trigf (fired %s): SPI_connect returned %d", when, ret);
/* get number of rows in table */
ret = SPI_exec("SELECT count(*) FROM ttest", 0);
- if (ret < 0)
+ if (ret < 0)
elog(NOTICE, "trigf (fired %s): SPI_exec returned %d", when, ret);
/* count(*) returns int8, so be careful to convert */
- i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
- SPI_tuptable->tupdesc,
+ i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
+ SPI_tuptable->tupdesc,
1,
- &isnull));
+ &isnull));
elog (INFO, "trigf (fired %s): there are %d rows in ttest", when, i);
if (checknull)
{
- SPI_getbinval(rettuple, tupdesc, 1, &isnull);
+ SPI_getbinval(rettuple, tupdesc, 1, &isnull);
if (isnull)
rettuple = NULL;
}
return PointerGetDatum(rettuple);
}
+]]>
</programlisting>
</para>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.134 2008/12/04 17:51:26 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.135 2008/12/07 23:46:39 alvherre Exp $ -->
<sect1 id="xfunc">
<title>User-Defined Functions</title>
For example, if we wanted to store 40 bytes in a <structname>text</>
structure, we might use a code fragment like this:
-<programlisting>
+<programlisting><![CDATA[
#include "postgres.h"
...
char buffer[40]; /* our source data */
...
text *destination = (text *) palloc(VARHDRSZ + 40);
-destination->length = VARHDRSZ + 40;
-memcpy(destination->data, buffer, 40);
+destination->length = VARHDRSZ + 40;
+memcpy(destination->data, buffer, 40);
...
+]]>
</programlisting>
<literal>VARHDRSZ</> is the same as <literal>sizeof(int4)</>, but
<para>
Here are some examples:
-<programlisting>
+<programlisting><![CDATA[
#include "postgres.h"
-#include <string.h>
+#include <string.h>
/* by value */
{
Point *new_point = (Point *) palloc(sizeof(Point));
- new_point->x = pointx->x;
- new_point->y = pointy->y;
+ new_point->x = pointx->x;
+ new_point->y = pointy->y;
return new_point;
}
VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
return new_text;
}
+]]>
</programlisting>
</para>
<para>
Here we show the same functions as above, coded in version-1 style:
-<programlisting>
+<programlisting><![CDATA[
#include "postgres.h"
-#include <string.h>
+#include <string.h>
#include "fmgr.h"
/* by value */
Point *pointy = PG_GETARG_POINT_P(1);
Point *new_point = (Point *) palloc(sizeof(Point));
- new_point->x = pointx->x;
- new_point->y = pointy->y;
+ new_point->x = pointx->x;
+ new_point->y = pointy->y;
PG_RETURN_POINT_P(new_point);
}
VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
PG_RETURN_TEXT_P(new_text);
}
+]]>
</programlisting>
</para>
Using call conventions version 0, we can define
<function>c_overpaid</> as:
-<programlisting>
+<programlisting><![CDATA[
#include "postgres.h"
#include "executor/executor.h" /* for GetAttributeByName() */
bool isnull;
int32 salary;
- salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull));
+ salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull));
if (isnull)
return false;
- return salary > limit;
+ return salary > limit;
}
+]]>
</programlisting>
In version-1 coding, the above would look like this:
-<programlisting>
+<programlisting><![CDATA[
#include "postgres.h"
#include "executor/executor.h" /* for GetAttributeByName() */
bool isnull;
Datum salary;
- salary = GetAttributeByName(t, "salary", &isnull);
+ salary = GetAttributeByName(t, "salary", &isnull);
if (isnull)
PG_RETURN_BOOL(false);
/* Alternatively, we might prefer to do PG_RETURN_NULL() for null salary. */
- PG_RETURN_BOOL(DatumGetInt32(salary) > limit);
+ PG_RETURN_BOOL(DatumGetInt32(salary) > limit);
}
+]]>
</programlisting>
</para>
<para>
A complete example of a simple <acronym>SRF</> returning a composite type
looks like:
-<programlisting>
+<programlisting><![CDATA[
PG_FUNCTION_INFO_V1(retcomposite);
Datum
funcctx = SRF_FIRSTCALL_INIT();
/* switch to memory context appropriate for multiple function calls */
- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* total number of tuples to be returned */
- funcctx->max_calls = PG_GETARG_UINT32(0);
+ funcctx->max_calls = PG_GETARG_UINT32(0);
/* Build a tuple descriptor for our result type */
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning record called in context "
* C strings
*/
attinmeta = TupleDescGetAttInMetadata(tupdesc);
- funcctx->attinmeta = attinmeta;
+ funcctx->attinmeta = attinmeta;
MemoryContextSwitchTo(oldcontext);
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
- call_cntr = funcctx->call_cntr;
- max_calls = funcctx->max_calls;
- attinmeta = funcctx->attinmeta;
+ call_cntr = funcctx->call_cntr;
+ max_calls = funcctx->max_calls;
+ attinmeta = funcctx->attinmeta;
- if (call_cntr < max_calls) /* do when there is more left to send */
+ if (call_cntr < max_calls) /* do when there is more left to send */
{
char **values;
HeapTuple tuple;
SRF_RETURN_DONE(funcctx);
}
}
+]]>
</programlisting>
One way to declare this function in SQL is:
-<!-- $PostgreSQL: pgsql/doc/src/sgml/xindex.sgml,v 1.63 2008/05/16 16:31:01 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/xindex.sgml,v 1.64 2008/12/07 23:46:39 alvherre Exp $ -->
<sect1 id="xindex">
<title>Interfacing Extensions To Indexes</title>
reduces the odds of getting inconsistent results for corner cases.
Following this approach, we first write:
-<programlisting>
-#define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y)
+<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)
+ if (amag < bmag)
return -1;
- if (amag > bmag)
+ if (amag > bmag)
return 1;
return 0;
}
+]]>
</programlisting>
Now the less-than function looks like:
-<programlisting>
+<programlisting><![CDATA[
PG_FUNCTION_INFO_V1(complex_abs_lt);
Datum
Complex *a = (Complex *) PG_GETARG_POINTER(0);
Complex *b = (Complex *) PG_GETARG_POINTER(1);
- PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) < 0);
+ PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) < 0);
}
+]]>
</programlisting>
The other four functions differ only in how they compare the internal
Now that we have the required operators and support routine,
we can finally create the operator class:
-<programlisting>
+<programlisting><![CDATA[
CREATE OPERATOR CLASS complex_abs_ops
DEFAULT FOR TYPE complex USING btree AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
FUNCTION 1 complex_abs_cmp(complex, complex);
+]]>
</programlisting>
</para>
on one of these types can be searched using a comparison value of another
type. The family could be duplicated by these definitions:
-<programlisting>
+<programlisting><![CDATA[
CREATE OPERATOR FAMILY integer_ops USING btree;
CREATE OPERATOR CLASS int8_ops
DEFAULT FOR TYPE int8 USING btree FAMILY integer_ops AS
-- standard int8 comparisons
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
FUNCTION 1 btint8cmp(int8, int8) ;
CREATE OPERATOR CLASS int4_ops
DEFAULT FOR TYPE int4 USING btree FAMILY integer_ops AS
-- standard int4 comparisons
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
FUNCTION 1 btint4cmp(int4, int4) ;
CREATE OPERATOR CLASS int2_ops
DEFAULT FOR TYPE int2 USING btree FAMILY integer_ops AS
-- standard int2 comparisons
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
FUNCTION 1 btint2cmp(int2, int2) ;
ALTER OPERATOR FAMILY integer_ops USING btree ADD
-- cross-type comparisons int8 vs int2
- OPERATOR 1 < (int8, int2) ,
- OPERATOR 2 <= (int8, int2) ,
+ OPERATOR 1 < (int8, int2) ,
+ OPERATOR 2 <= (int8, int2) ,
OPERATOR 3 = (int8, int2) ,
- OPERATOR 4 >= (int8, int2) ,
- OPERATOR 5 > (int8, int2) ,
+ OPERATOR 4 >= (int8, int2) ,
+ OPERATOR 5 > (int8, int2) ,
FUNCTION 1 btint82cmp(int8, int2) ,
-- cross-type comparisons int8 vs int4
- OPERATOR 1 < (int8, int4) ,
- OPERATOR 2 <= (int8, int4) ,
+ OPERATOR 1 < (int8, int4) ,
+ OPERATOR 2 <= (int8, int4) ,
OPERATOR 3 = (int8, int4) ,
- OPERATOR 4 >= (int8, int4) ,
- OPERATOR 5 > (int8, int4) ,
+ OPERATOR 4 >= (int8, int4) ,
+ OPERATOR 5 > (int8, int4) ,
FUNCTION 1 btint84cmp(int8, int4) ,
-- cross-type comparisons int4 vs int2
- OPERATOR 1 < (int4, int2) ,
- OPERATOR 2 <= (int4, int2) ,
+ OPERATOR 1 < (int4, int2) ,
+ OPERATOR 2 <= (int4, int2) ,
OPERATOR 3 = (int4, int2) ,
- OPERATOR 4 >= (int4, int2) ,
- OPERATOR 5 > (int4, int2) ,
+ OPERATOR 4 >= (int4, int2) ,
+ OPERATOR 5 > (int4, int2) ,
FUNCTION 1 btint42cmp(int4, int2) ,
-- cross-type comparisons int4 vs int8
- OPERATOR 1 < (int4, int8) ,
- OPERATOR 2 <= (int4, int8) ,
+ OPERATOR 1 < (int4, int8) ,
+ OPERATOR 2 <= (int4, int8) ,
OPERATOR 3 = (int4, int8) ,
- OPERATOR 4 >= (int4, int8) ,
- OPERATOR 5 > (int4, int8) ,
+ OPERATOR 4 >= (int4, int8) ,
+ OPERATOR 5 > (int4, int8) ,
FUNCTION 1 btint48cmp(int4, int8) ,
-- cross-type comparisons int2 vs int8
- OPERATOR 1 < (int2, int8) ,
- OPERATOR 2 <= (int2, int8) ,
+ OPERATOR 1 < (int2, int8) ,
+ OPERATOR 2 <= (int2, int8) ,
OPERATOR 3 = (int2, int8) ,
- OPERATOR 4 >= (int2, int8) ,
- OPERATOR 5 > (int2, int8) ,
+ OPERATOR 4 >= (int2, int8) ,
+ OPERATOR 5 > (int2, int8) ,
FUNCTION 1 btint28cmp(int2, int8) ,
-- cross-type comparisons int2 vs int4
- OPERATOR 1 < (int2, int4) ,
- OPERATOR 2 <= (int2, int4) ,
+ OPERATOR 1 < (int2, int4) ,
+ OPERATOR 2 <= (int2, int4) ,
OPERATOR 3 = (int2, int4) ,
- OPERATOR 4 >= (int2, int4) ,
- OPERATOR 5 > (int2, int4) ,
+ OPERATOR 4 >= (int2, int4) ,
+ OPERATOR 5 > (int2, int4) ,
FUNCTION 1 btint24cmp(int2, int4) ;
+]]>
</programlisting>
Notice that this definition <quote>overloads</> the operator strategy and
-<!-- $PostgreSQL: pgsql/doc/src/sgml/xtypes.sgml,v 1.30 2008/02/23 19:11:45 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/xtypes.sgml,v 1.31 2008/12/07 23:46:39 alvherre Exp $ -->
<sect1 id="xtypes">
<title>User-Defined Types</title>
write a complete and robust parser for that representation as your
input function. For instance:
-<programlisting>
+<programlisting><![CDATA[
PG_FUNCTION_INFO_V1(complex_in);
Datum
y;
Complex *result;
- if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2)
+ if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for complex: \"%s\"",
str)));
result = (Complex *) palloc(sizeof(Complex));
- result->x = x;
- result->y = y;
+ result->x = x;
+ result->y = y;
PG_RETURN_POINTER(result);
}
+]]>
</programlisting>
The output function can simply be:
-<programlisting>
+<programlisting><![CDATA[
PG_FUNCTION_INFO_V1(complex_out);
Datum
char *result;
result = (char *) palloc(100);
- snprintf(result, 100, "(%g,%g)", complex->x, complex->y);
+ snprintf(result, 100, "(%g,%g)", complex->x, complex->y);
PG_RETURN_CSTRING(result);
}
+]]>
</programlisting>
</para>
<type>complex</type>, we will piggy-back on the binary I/O converters
for type <type>float8</>:
-<programlisting>
+<programlisting><![CDATA[
PG_FUNCTION_INFO_V1(complex_recv);
Datum
Complex *result;
result = (Complex *) palloc(sizeof(Complex));
- result->x = pq_getmsgfloat8(buf);
- result->y = pq_getmsgfloat8(buf);
+ result->x = pq_getmsgfloat8(buf);
+ result->y = pq_getmsgfloat8(buf);
PG_RETURN_POINTER(result);
}
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));
+ pq_begintypsend(&buf);
+ pq_sendfloat8(&buf, complex->x);
+ pq_sendfloat8(&buf, complex->y);
+ PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
+]]>
</programlisting>
</para>