aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-06-22 00:29:29 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-06-22 00:29:29 +0000
commite92dc1e19934cf11d9ab551979700ac64a4165d8 (patch)
treef0dede2756772f046c73eee859f0d992282760c3
parent21e0b7b8f2bc6933ed3e75c36dbafcfa9b9b30e3 (diff)
downloadpostgresql-e92dc1e19934cf11d9ab551979700ac64a4165d8.tar.gz
postgresql-e92dc1e19934cf11d9ab551979700ac64a4165d8.zip
Bring the libpq example programs into the 21st century.
-rw-r--r--doc/src/sgml/libpq.sgml714
-rw-r--r--src/test/examples/Makefile3
-rw-r--r--src/test/examples/testlibpq.c84
-rw-r--r--src/test/examples/testlibpq2.c131
-rw-r--r--src/test/examples/testlibpq2.sql3
-rw-r--r--src/test/examples/testlibpq3.c229
-rw-r--r--src/test/examples/testlibpq3.sql8
-rw-r--r--src/test/examples/testlibpq4.c4
8 files changed, 574 insertions, 602 deletions
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 65201ccf55a..b41ba1e172c 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1,5 +1,5 @@
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.125 2003/06/21 21:51:30 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.126 2003/06/22 00:29:29 tgl Exp $
-->
<chapter id="libpq">
@@ -3431,6 +3431,12 @@ testlibpq.o(.text+0xa4): undefined reference to `PQerrorMessage'
<sect1 id="libpq-example">
<title>Example Programs</title>
+ <para>
+ These examples and others can be found in the
+ directory <filename>src/test/examples</filename> in the source code
+ distribution.
+ </para>
+
<example id="libpq-example-1">
<title><application>libpq</application> Example Program 1</title>
@@ -3438,128 +3444,121 @@ testlibpq.o(.text+0xa4): undefined reference to `PQerrorMessage'
/*
* testlibpq.c
*
- * Test the C version of libpq, the <productname>PostgreSQL</> frontend
- * library.
+ * Test the C version of LIBPQ, the POSTGRES frontend library.
*/
#include &lt;stdio.h&gt;
-#include &lt;libpq-fe.h&gt;
+#include &lt;stdlib.h&gt;
+#include "libpq-fe.h"
-void
+static void
exit_nicely(PGconn *conn)
{
- PQfinish(conn);
- exit(1);
+ PQfinish(conn);
+ exit(1);
}
-main()
+int
+main(int argc, char **argv)
{
- char *pghost,
- *pgport,
- *pgoptions,
- *pgtty;
- char *dbName;
- int nFields;
- int i,
- j;
-
- /* FILE *debug; */
-
- PGconn *conn;
- PGresult *res;
-
- /*
- * begin, by setting the parameters for a backend connection if the
- * parameters are null, then the system will try to use reasonable
- * defaults by looking up environment variables or, failing that,
- * using hardwired constants
- */
- pghost = NULL; /* host name of the backend server */
- pgport = NULL; /* port of the backend server */
- pgoptions = NULL; /* special options to start up the backend
- * server */
- pgtty = NULL; /* unused */
- dbName = "template1";
-
- /* make a connection to the database */
- conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
-
- /*
- * check to see that the backend connection was successfully made
- */
- if (PQstatus(conn) == CONNECTION_BAD)
- {
- fprintf(stderr, "Connection to database '%s' failed.\n", dbName);
- fprintf(stderr, "%s", PQerrorMessage(conn));
- exit_nicely(conn);
- }
-
- /* debug = fopen("/tmp/trace.out","w"); */
- /* PQtrace(conn, debug); */
-
- /* start a transaction block */
- res = PQexec(conn, "BEGIN");
- if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- fprintf(stderr, "BEGIN command failed\n");
- PQclear(res);
- exit_nicely(conn);
- }
-
- /*
- * should PQclear PGresult whenever it is no longer needed to avoid
- * memory leaks
- */
- PQclear(res);
-
- /*
- * fetch rows from the pg_database, the system catalog of
- * databases
- */
- res = PQexec(conn, "DECLARE mycursor CURSOR FOR SELECT * FROM pg_database");
- if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- fprintf(stderr, "DECLARE CURSOR command failed\n");
- PQclear(res);
- exit_nicely(conn);
- }
- PQclear(res);
- res = PQexec(conn, "FETCH ALL in mycursor");
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- fprintf(stderr, "FETCH ALL command didn't return tuples properly\n");
- PQclear(res);
- exit_nicely(conn);
- }
-
- /* first, print out the attribute names */
- nFields = PQnfields(res);
- for (i = 0; i &lt; nFields; i++)
- printf("%-15s", PQfname(res, i));
- printf("\n\n");
-
- /* next, print out the rows */
- for (i = 0; i &lt; PQntuples(res); i++)
- {
- for (j = 0; j &lt; nFields; j++)
- printf("%-15s", PQgetvalue(res, i, j));
- printf("\n");
- }
- PQclear(res);
-
- /* close the cursor */
- res = PQexec(conn, "CLOSE mycursor");
- PQclear(res);
-
- /* commit the transaction */
- res = PQexec(conn, "COMMIT");
- PQclear(res);
-
- /* close the connection to the database and cleanup */
- PQfinish(conn);
-
- /* fclose(debug); */
- return 0;
-
+ const char *conninfo;
+ PGconn *conn;
+ PGresult *res;
+ int nFields;
+ int i,
+ j;
+
+ /*
+ * If the user supplies a parameter on the command line, use it as
+ * the conninfo string; otherwise default to setting dbname=template1
+ * and using environment variables or defaults for all other connection
+ * parameters.
+ */
+ if (argc &gt; 1)
+ conninfo = argv[1];
+ else
+ conninfo = "dbname = template1";
+
+ /* Make a connection to the database */
+ conn = PQconnectdb(conninfo);
+
+ /* Check to see that the backend connection was successfully made */
+ if (PQstatus(conn) != CONNECTION_OK)
+ {
+ fprintf(stderr, "Connection to database '%s' failed.\n", PQdb(conn));
+ fprintf(stderr, "%s", PQerrorMessage(conn));
+ exit_nicely(conn);
+ }
+
+ /*
+ * Our test case here involves using a cursor, for which we must be
+ * inside a transaction block. We could do the whole thing with a
+ * single PQexec() of "select * from pg_database", but that's too
+ * trivial to make a good example.
+ */
+
+ /* Start a transaction block */
+ res = PQexec(conn, "BEGIN");
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn));
+ PQclear(res);
+ exit_nicely(conn);
+ }
+
+ /*
+ * Should PQclear PGresult whenever it is no longer needed to avoid
+ * memory leaks
+ */
+ PQclear(res);
+
+ /*
+ * Fetch rows from pg_database, the system catalog of databases
+ */
+ res = PQexec(conn, "DECLARE myportal CURSOR FOR select * from pg_database");
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ fprintf(stderr, "DECLARE CURSOR failed: %s", PQerrorMessage(conn));
+ PQclear(res);
+ exit_nicely(conn);
+ }
+ PQclear(res);
+
+ res = PQexec(conn, "FETCH ALL in myportal");
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ fprintf(stderr, "FETCH ALL failed: %s", PQerrorMessage(conn));
+ PQclear(res);
+ exit_nicely(conn);
+ }
+
+ /* first, print out the attribute names */
+ nFields = PQnfields(res);
+ for (i = 0; i &lt; nFields; i++)
+ printf("%-15s", PQfname(res, i));
+ printf("\n\n");
+
+ /* next, print out the rows */
+ for (i = 0; i &lt; PQntuples(res); i++)
+ {
+ for (j = 0; j &lt; nFields; j++)
+ printf("%-15s", PQgetvalue(res, i, j));
+ printf("\n");
+ }
+
+ PQclear(res);
+
+ /* close the portal ... we don't bother to check for errors ... */
+ res = PQexec(conn, "CLOSE myportal");
+ PQclear(res);
+
+ /* end the transaction */
+ res = PQexec(conn, "END");
+ PQclear(res);
+
+ /* close the connection to the database and cleanup */
+ PQfinish(conn);
+
+ return 0;
}
</programlisting>
</example>
@@ -3570,116 +3569,133 @@ main()
<programlisting>
/*
* testlibpq2.c
- * Test of the asynchronous notification interface
+ * Test of the asynchronous notification interface
*
* Start this program, then from psql in another window do
* NOTIFY TBL2;
+ * Repeat four times to get this program to exit.
*
* Or, if you want to get fancy, try this:
- * Populate a database with the following:
+ * populate a database with the following commands
+ * (provided in src/test/examples/testlibpq2.sql):
*
* CREATE TABLE TBL1 (i int4);
*
* CREATE TABLE TBL2 (i int4);
*
* CREATE RULE r1 AS ON INSERT TO TBL1 DO
- * (INSERT INTO TBL2 values (new.i); NOTIFY TBL2);
- *
- * and do
+ * (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
*
- * INSERT INTO TBL1 values (10);
+ * and do this four times:
*
+ * INSERT INTO TBL1 VALUES (10);
*/
#include &lt;stdio.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+#include &lt;errno.h&gt;
+#include &lt;sys/time.h&gt;
#include "libpq-fe.h"
-void
+static void
exit_nicely(PGconn *conn)
{
- PQfinish(conn);
- exit(1);
+ PQfinish(conn);
+ exit(1);
}
-main()
+int
+main(int argc, char **argv)
{
- char *pghost,
- *pgport,
- *pgoptions,
- *pgtty;
- char *dbName;
- int nFields;
- int i,
- j;
-
- PGconn *conn;
- PGresult *res;
- PGnotify *notify;
-
- /*
- * begin, by setting the parameters for a backend connection if the
- * parameters are null, then the system will try to use reasonable
- * defaults by looking up environment variables or, failing that,
- * using hardwired constants
- */
- pghost = NULL; /* host name of the backend server */
- pgport = NULL; /* port of the backend server */
- pgoptions = NULL; /* special options to start up the backend
- * server */
- pgtty = NULL; /* unused */
- dbName = getenv("USER"); /* change this to the name of your test
- * database */
-
- /* make a connection to the database */
- conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
-
- /*
- * check to see that the backend connection was successfully made
- */
- if (PQstatus(conn) == CONNECTION_BAD)
- {
- fprintf(stderr, "Connection to database '%s' failed.\n", dbName);
- fprintf(stderr, "%s", PQerrorMessage(conn));
- exit_nicely(conn);
- }
-
- res = PQexec(conn, "LISTEN TBL2");
- if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- fprintf(stderr, "LISTEN command failed\n");
- PQclear(res);
- exit_nicely(conn);
- }
-
- /*
- * should PQclear PGresult whenever it is no longer needed to avoid
- * memory leaks
- */
- PQclear(res);
-
- while (1)
- {
-
- /*
- * wait a little bit between checks; waiting with select()
- * would be more efficient.
- */
- sleep(1);
- /* collect any asynchronous backend messages */
- PQconsumeInput(conn);
- /* check for asynchronous notify messages */
- while ((notify = PQnotifies(conn)) != NULL)
- {
- fprintf(stderr,
- "ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
- notify-&gt;relname, notify-&gt;be_pid);
- PQfreemem(notify);
- }
- }
-
- /* close the connection to the database and cleanup */
- PQfinish(conn);
-
- return 0;
+ const char *conninfo;
+ PGconn *conn;
+ PGresult *res;
+ PGnotify *notify;
+ int nnotifies;
+
+ /*
+ * If the user supplies a parameter on the command line, use it as
+ * the conninfo string; otherwise default to setting dbname=template1
+ * and using environment variables or defaults for all other connection
+ * parameters.
+ */
+ if (argc &gt; 1)
+ conninfo = argv[1];
+ else
+ conninfo = "dbname = template1";
+
+ /* Make a connection to the database */
+ conn = PQconnectdb(conninfo);
+
+ /* Check to see that the backend connection was successfully made */
+ if (PQstatus(conn) != CONNECTION_OK)
+ {
+ fprintf(stderr, "Connection to database '%s' failed.\n", PQdb(conn));
+ fprintf(stderr, "%s", PQerrorMessage(conn));
+ exit_nicely(conn);
+ }
+
+ /*
+ * Issue LISTEN command to enable notifications from the rule's NOTIFY.
+ */
+ res = PQexec(conn, "LISTEN TBL2");
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ fprintf(stderr, "LISTEN command failed: %s", PQerrorMessage(conn));
+ PQclear(res);
+ exit_nicely(conn);
+ }
+
+ /*
+ * should PQclear PGresult whenever it is no longer needed to avoid
+ * memory leaks
+ */
+ PQclear(res);
+
+ /* Quit after four notifies are received. */
+ nnotifies = 0;
+ while (nnotifies &lt; 4)
+ {
+ /*
+ * Sleep until something happens on the connection. We use select(2)
+ * to wait for input, but you could also use poll() or similar
+ * facilities.
+ */
+ int sock;
+ fd_set input_mask;
+
+ sock = PQsocket(conn);
+
+ if (sock &lt; 0)
+ break; /* shouldn't happen */
+
+ FD_ZERO(&amp;input_mask);
+ FD_SET(sock, &amp;input_mask);
+
+ if (select(sock + 1, &amp;input_mask, NULL, NULL, NULL) &lt; 0)
+ {
+ fprintf(stderr, "select() failed: %s\n", strerror(errno));
+ exit_nicely(conn);
+ }
+
+ /* Now check for input */
+ PQconsumeInput(conn);
+ while ((notify = PQnotifies(conn)) != NULL)
+ {
+ fprintf(stderr,
+ "ASYNC NOTIFY of '%s' received from backend pid %d\n",
+ notify-&gt;relname, notify-&gt;be_pid);
+ PQfreemem(notify);
+ nnotifies++;
+ }
+ }
+
+ fprintf(stderr, "Done.\n");
+
+ /* close the connection to the database and cleanup */
+ PQfinish(conn);
+
+ return 0;
}
</programlisting>
</example>
@@ -3689,176 +3705,158 @@ main()
<programlisting>
/*
- * testlibpq3.c Test the C version of Libpq, the <productname>PostgreSQL</> frontend
- * library. tests the binary cursor interface
- *
- *
- *
- * populate a database by doing the following:
+ * testlibpq3.c
+ * Test out-of-line parameters and binary I/O.
*
- * CREATE TABLE test1 (i int4, d real, p polygon);
+ * Before running this, populate a database with the following commands
+ * (provided in src/test/examples/testlibpq3.sql):
*
- * INSERT INTO test1 values (1, 3.567, polygon '(3.0, 4.0, 1.0, 2.0)');
+ * CREATE TABLE test1 (i int4, t text, b bytea);
*
- * INSERT INTO test1 values (2, 89.05, polygon '(4.0, 3.0, 2.0, 1.0)');
+ * INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
+ * INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
*
- * the expected output is:
- *
- * tuple 0: got i = (4 bytes) 1, d = (4 bytes) 3.567000, p = (4
- * bytes) 2 points boundbox = (hi=3.000000/4.000000, lo =
- * 1.000000,2.000000) tuple 1: got i = (4 bytes) 2, d = (4 bytes)
- * 89.050003, p = (4 bytes) 2 points boundbox =
- * (hi=4.000000/3.000000, lo = 2.000000,1.000000)
+ * The expected output is:
*
+ * tuple 0: got
+ * i = (4 bytes) 1
+ * t = (11 bytes) 'joe's place'
+ * b = (5 bytes) \000\001\002\003\004
*
*/
#include &lt;stdio.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+#include &lt;sys/types.h&gt;
#include "libpq-fe.h"
-#include "utils/geo_decls.h" /* for the POLYGON type */
-void
+/* for ntohl/htonl */
+#include &lt;netinet/in.h&gt;
+#include &lt;arpa/inet.h&gt;
+
+
+static void
exit_nicely(PGconn *conn)
{
- PQfinish(conn);
- exit(1);
+ PQfinish(conn);
+ exit(1);
}
-main()
+int
+main(int argc, char **argv)
{
- char *pghost,
- *pgport,
- *pgoptions,
- *pgtty;
- char *dbName;
- int nFields;
- int i,
- j;
- int i_fnum,
- d_fnum,
- p_fnum;
- PGconn *conn;
- PGresult *res;
-
- /*
- * begin, by setting the parameters for a backend connection if the
- * parameters are null, then the system will try to use reasonable
- * defaults by looking up environment variables or, failing that,
- * using hardwired constants
- */
- pghost = NULL; /* host name of the backend server */
- pgport = NULL; /* port of the backend server */
- pgoptions = NULL; /* special options to start up the backend
- * server */
- pgtty = NULL; /* unused */
-
- dbName = getenv("USER"); /* change this to the name of your test
- * database */
-
- /* make a connection to the database */
- conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
-
- /*
- * check to see that the backend connection was successfully made
- */
- if (PQstatus(conn) == CONNECTION_BAD)
- {
- fprintf(stderr, "Connection to database '%s' failed.\n", dbName);
- fprintf(stderr, "%s", PQerrorMessage(conn));
- exit_nicely(conn);
- }
-
- /* start a transaction block */
- res = PQexec(conn, "BEGIN");
- if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- fprintf(stderr, "BEGIN command failed\n");
- PQclear(res);
- exit_nicely(conn);
- }
-
- /*
- * should PQclear PGresult whenever it is no longer needed to avoid
- * memory leaks
- */
- PQclear(res);
-
- /*
- * fetch rows from the pg_database, the system catalog of
- * databases
- */
- res = PQexec(conn, "DECLARE mycursor BINARY CURSOR FOR SELECT * FROM test1");
- if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- fprintf(stderr, "DECLARE CURSOR command failed\n");
- PQclear(res);
- exit_nicely(conn);
- }
- PQclear(res);
-
- res = PQexec(conn, "FETCH ALL in mycursor");
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- fprintf(stderr, "FETCH ALL command didn't return tuples properly\n");
- PQclear(res);
- exit_nicely(conn);
- }
-
- i_fnum = PQfnumber(res, "i");
- d_fnum = PQfnumber(res, "d");
- p_fnum = PQfnumber(res, "p");
-
- for (i = 0; i &lt; 3; i++)
- {
- printf("type[%d] = %d, size[%d] = %d\n",
- i, PQftype(res, i),
- i, PQfsize(res, i));
- }
- for (i = 0; i &lt; PQntuples(res); i++)
- {
- int *ival;
- float *dval;
- int plen;
- POLYGON *pval;
-
- /* we hard-wire this to the 3 fields we know about */
- ival = (int *) PQgetvalue(res, i, i_fnum);
- dval = (float *) PQgetvalue(res, i, d_fnum);
- plen = PQgetlength(res, i, p_fnum);
-
- /*
- * plen doesn't include the length field so need to
- * increment by VARHDSZ
- */
- pval = (POLYGON *) malloc(plen + VARHDRSZ);
- pval-&gt;size = plen;
- memmove((char *) &amp;pval-&gt;npts, PQgetvalue(res, i, p_fnum), plen);
- printf("tuple %d: got\n", i);
- printf(" i = (%d bytes) %d,\n",
- PQgetlength(res, i, i_fnum), *ival);
- printf(" d = (%d bytes) %f,\n",
- PQgetlength(res, i, d_fnum), *dval);
- printf(" p = (%d bytes) %d points \tboundbox = (hi=%f/%f, lo = %f,%f)\n",
- PQgetlength(res, i, d_fnum),
- pval-&gt;npts,
- pval-&gt;boundbox.xh,
- pval-&gt;boundbox.yh,
- pval-&gt;boundbox.xl,
- pval-&gt;boundbox.yl);
- }
- PQclear(res);
-
- /* close the cursor */
- res = PQexec(conn, "CLOSE mycursor");
- PQclear(res);
-
- /* commit the transaction */
- res = PQexec(conn, "COMMIT");
- PQclear(res);
-
- /* close the connection to the database and cleanup */
- PQfinish(conn);
-
- return 0;
+ const char *conninfo;
+ PGconn *conn;
+ PGresult *res;
+ const char *paramValues[1];
+ int i,
+ j;
+ int i_fnum,
+ t_fnum,
+ b_fnum;
+
+ /*
+ * If the user supplies a parameter on the command line, use it as
+ * the conninfo string; otherwise default to setting dbname=template1
+ * and using environment variables or defaults for all other connection
+ * parameters.
+ */
+ if (argc &gt; 1)
+ conninfo = argv[1];
+ else
+ conninfo = "dbname = template1";
+
+ /* Make a connection to the database */
+ conn = PQconnectdb(conninfo);
+
+ /* Check to see that the backend connection was successfully made */
+ if (PQstatus(conn) != CONNECTION_OK)
+ {
+ fprintf(stderr, "Connection to database '%s' failed.\n", PQdb(conn));
+ fprintf(stderr, "%s", PQerrorMessage(conn));
+ exit_nicely(conn);
+ }
+
+ /*
+ * The point of this program is to illustrate use of PQexecParams()
+ * with out-of-line parameters, as well as binary transmission of
+ * results. By using out-of-line parameters we can avoid a lot of
+ * tedious mucking about with quoting and escaping. Notice how we
+ * don't have to do anything special with the quote mark in the
+ * parameter value.
+ */
+
+ /* Here is our out-of-line parameter value */
+ paramValues[0] = "joe's place";
+
+ res = PQexecParams(conn,
+ "SELECT * FROM test1 WHERE t = $1",
+ 1, /* one param */
+ NULL, /* let the backend deduce param type */
+ paramValues,
+ NULL, /* don't need param lengths since text */
+ NULL, /* default to all text params */
+ 1); /* ask for binary results */
+
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn));
+ PQclear(res);
+ exit_nicely(conn);
+ }
+
+ /* Use PQfnumber to avoid assumptions about field order in result */
+ i_fnum = PQfnumber(res, "i");
+ t_fnum = PQfnumber(res, "t");
+ b_fnum = PQfnumber(res, "b");
+
+ for (i = 0; i &lt; PQntuples(res); i++)
+ {
+ char *iptr;
+ char *tptr;
+ char *bptr;
+ int blen;
+ int ival;
+
+ /* Get the field values (we ignore possibility they are null!) */
+ iptr = PQgetvalue(res, i, i_fnum);
+ tptr = PQgetvalue(res, i, t_fnum);
+ bptr = PQgetvalue(res, i, b_fnum);
+
+ /*
+ * The binary representation of INT4 is in network byte order,
+ * which we'd better coerce to the local byte order.
+ */
+ ival = ntohl(*((uint32_t *) iptr));
+
+ /*
+ * The binary representation of TEXT is, well, text, and since
+ * libpq was nice enough to append a zero byte to it, it'll work
+ * just fine as a C string.
+ *
+ * The binary representation of BYTEA is a bunch of bytes, which
+ * could include embedded nulls so we have to pay attention to
+ * field length.
+ */
+ blen = PQgetlength(res, i, b_fnum);
+
+ printf("tuple %d: got\n", i);
+ printf(" i = (%d bytes) %d\n",
+ PQgetlength(res, i, i_fnum), ival);
+ printf(" t = (%d bytes) '%s'\n",
+ PQgetlength(res, i, t_fnum), tptr);
+ printf(" b = (%d bytes) ", blen);
+ for (j = 0; j &lt; blen; j++)
+ printf("\\%03o", bptr[j]);
+ printf("\n\n");
+ }
+
+ PQclear(res);
+
+ /* close the connection to the database and cleanup */
+ PQfinish(conn);
+
+ return 0;
}
</programlisting>
</example>
diff --git a/src/test/examples/Makefile b/src/test/examples/Makefile
index c5ee425f3b5..8589b0a95fe 100644
--- a/src/test/examples/Makefile
+++ b/src/test/examples/Makefile
@@ -7,10 +7,9 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
-LIBS += $(libpq)
+LDFLAGS += $(libpq)
-# PROGS= testlibpq0 testlibpq1 testlibpq2 testlibpq3 testlibpq4 testlo
PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo
all: $(PROGS)
diff --git a/src/test/examples/testlibpq.c b/src/test/examples/testlibpq.c
index ca5ebbafba6..20dfb7220d9 100644
--- a/src/test/examples/testlibpq.c
+++ b/src/test/examples/testlibpq.c
@@ -1,10 +1,10 @@
/*
* testlibpq.c
- * Test the C version of LIBPQ, the POSTGRES frontend library.
- *
*
+ * Test the C version of LIBPQ, the POSTGRES frontend library.
*/
#include <stdio.h>
+#include <stdlib.h>
#include "libpq-fe.h"
static void
@@ -15,76 +15,66 @@ exit_nicely(PGconn *conn)
}
int
-main()
+main(int argc, char **argv)
{
- char *pghost,
- *pgport,
- *pgoptions,
- *pgtty;
- char *dbName;
+ const char *conninfo;
+ PGconn *conn;
+ PGresult *res;
int nFields;
int i,
j;
-#ifdef DEBUG
- FILE *debug;
-#endif /* DEBUG */
-
- PGconn *conn;
- PGresult *res;
-
/*
- * begin, by setting the parameters for a backend connection if the
- * parameters are null, then the system will try to use reasonable
- * defaults by looking up environment variables or, failing that,
- * using hardwired constants
+ * If the user supplies a parameter on the command line, use it as
+ * the conninfo string; otherwise default to setting dbname=template1
+ * and using environment variables or defaults for all other connection
+ * parameters.
*/
- pghost = NULL; /* host name of the backend server */
- pgport = NULL; /* port of the backend server */
- pgoptions = NULL; /* special options to start up the backend
- * server */
- pgtty = NULL; /* debugging tty for the backend server */
- dbName = "template1";
-
- /* make a connection to the database */
- conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
-
- /* check to see that the backend connection was successfully made */
- if (PQstatus(conn) == CONNECTION_BAD)
+ if (argc > 1)
+ conninfo = argv[1];
+ else
+ conninfo = "dbname = template1";
+
+ /* Make a connection to the database */
+ conn = PQconnectdb(conninfo);
+
+ /* Check to see that the backend connection was successfully made */
+ if (PQstatus(conn) != CONNECTION_OK)
{
- fprintf(stderr, "Connection to database '%s' failed.\n", dbName);
+ fprintf(stderr, "Connection to database '%s' failed.\n", PQdb(conn));
fprintf(stderr, "%s", PQerrorMessage(conn));
exit_nicely(conn);
}
-#ifdef DEBUG
- debug = fopen("/tmp/trace.out", "w");
- PQtrace(conn, debug);
-#endif /* DEBUG */
+ /*
+ * Our test case here involves using a cursor, for which we must be
+ * inside a transaction block. We could do the whole thing with a
+ * single PQexec() of "select * from pg_database", but that's too
+ * trivial to make a good example.
+ */
- /* start a transaction block */
+ /* Start a transaction block */
res = PQexec(conn, "BEGIN");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- fprintf(stderr, "BEGIN command failed\n");
+ fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/*
- * should PQclear PGresult whenever it is no longer needed to avoid
+ * Should PQclear PGresult whenever it is no longer needed to avoid
* memory leaks
*/
PQclear(res);
/*
- * fetch instances from the pg_database, the system catalog of
- * databases
+ * Fetch rows from pg_database, the system catalog of databases
*/
res = PQexec(conn, "DECLARE myportal CURSOR FOR select * from pg_database");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- fprintf(stderr, "DECLARE CURSOR command failed\n");
+ fprintf(stderr, "DECLARE CURSOR failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
@@ -93,7 +83,7 @@ main()
res = PQexec(conn, "FETCH ALL in myportal");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- fprintf(stderr, "FETCH ALL command didn't return tuples properly\n");
+ fprintf(stderr, "FETCH ALL failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
@@ -104,7 +94,7 @@ main()
printf("%-15s", PQfname(res, i));
printf("\n\n");
- /* next, print out the instances */
+ /* next, print out the rows */
for (i = 0; i < PQntuples(res); i++)
{
for (j = 0; j < nFields; j++)
@@ -114,7 +104,7 @@ main()
PQclear(res);
- /* close the portal */
+ /* close the portal ... we don't bother to check for errors ... */
res = PQexec(conn, "CLOSE myportal");
PQclear(res);
@@ -125,9 +115,5 @@ main()
/* close the connection to the database and cleanup */
PQfinish(conn);
-#ifdef DEBUG
- fclose(debug);
-#endif /* DEBUG */
-
return 0;
}
diff --git a/src/test/examples/testlibpq2.c b/src/test/examples/testlibpq2.c
index 8f5a1ec3abb..51c9929df3d 100644
--- a/src/test/examples/testlibpq2.c
+++ b/src/test/examples/testlibpq2.c
@@ -2,24 +2,30 @@
* testlibpq2.c
* Test of the asynchronous notification interface
*
- populate a database with the following:
-
-CREATE TABLE TBL1 (i int4);
-
-CREATE TABLE TBL2 (i int4);
-
-CREATE RULE r1 AS ON INSERT TO TBL1 DO [INSERT INTO TBL2 values (new.i); NOTIFY TBL2];
-
- * Then start up this program
- * After the program has begun, do
-
-INSERT INTO TBL1 values (10);
-
+ * Start this program, then from psql in another window do
+ * NOTIFY TBL2;
+ * Repeat four times to get this program to exit.
+ *
+ * Or, if you want to get fancy, try this:
+ * populate a database with the following commands
+ * (provided in src/test/examples/testlibpq2.sql):
*
+ * CREATE TABLE TBL1 (i int4);
*
+ * CREATE TABLE TBL2 (i int4);
+ *
+ * CREATE RULE r1 AS ON INSERT TO TBL1 DO
+ * (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
+ *
+ * and do this four times:
+ *
+ * INSERT INTO TBL1 VALUES (10);
*/
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
#include "libpq-fe.h"
static void
@@ -30,51 +36,43 @@ exit_nicely(PGconn *conn)
}
int
-main()
+main(int argc, char **argv)
{
- char *pghost,
- *pgport,
- *pgoptions,
- *pgtty;
- char *dbName;
-
- /*
- * int nFields; int i, j;
- */
-
+ const char *conninfo;
PGconn *conn;
PGresult *res;
PGnotify *notify;
+ int nnotifies;
/*
- * begin, by setting the parameters for a backend connection if the
- * parameters are null, then the system will try to use reasonable
- * defaults by looking up environment variables or, failing that,
- * using hardwired constants
+ * If the user supplies a parameter on the command line, use it as
+ * the conninfo string; otherwise default to setting dbname=template1
+ * and using environment variables or defaults for all other connection
+ * parameters.
*/
- pghost = NULL; /* host name of the backend server */
- pgport = NULL; /* port of the backend server */
- pgoptions = NULL; /* special options to start up the backend
- * server */
- pgtty = NULL; /* debugging tty for the backend server */
- dbName = getenv("USER"); /* change this to the name of your test
- * database */
-
- /* make a connection to the database */
- conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
-
- /* check to see that the backend connection was successfully made */
- if (PQstatus(conn) == CONNECTION_BAD)
+ if (argc > 1)
+ conninfo = argv[1];
+ else
+ conninfo = "dbname = template1";
+
+ /* Make a connection to the database */
+ conn = PQconnectdb(conninfo);
+
+ /* Check to see that the backend connection was successfully made */
+ if (PQstatus(conn) != CONNECTION_OK)
{
- fprintf(stderr, "Connection to database '%s' failed.\n", dbName);
+ fprintf(stderr, "Connection to database '%s' failed.\n", PQdb(conn));
fprintf(stderr, "%s", PQerrorMessage(conn));
exit_nicely(conn);
}
+ /*
+ * Issue LISTEN command to enable notifications from the rule's NOTIFY.
+ */
res = PQexec(conn, "LISTEN TBL2");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- fprintf(stderr, "LISTEN command failed\n");
+ fprintf(stderr, "LISTEN command failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
@@ -85,27 +83,48 @@ main()
*/
PQclear(res);
- while (1)
+ /* Quit after four notifies are received. */
+ nnotifies = 0;
+ while (nnotifies < 4)
{
- /* async notification only come back as a result of a query */
- /* we can send empty queries */
- res = PQexec(conn, " ");
-/* printf("res->status = %s\n", PQresStatus(PQresultStatus(res))); */
- /* check for asynchronous returns */
- notify = PQnotifies(conn);
- if (notify)
+ /*
+ * Sleep until something happens on the connection. We use select(2)
+ * to wait for input, but you could also use poll() or similar
+ * facilities.
+ */
+ int sock;
+ fd_set input_mask;
+
+ sock = PQsocket(conn);
+
+ if (sock < 0)
+ break; /* shouldn't happen */
+
+ FD_ZERO(&input_mask);
+ FD_SET(sock, &input_mask);
+
+ if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0)
+ {
+ fprintf(stderr, "select() failed: %s\n", strerror(errno));
+ exit_nicely(conn);
+ }
+
+ /* Now check for input */
+ PQconsumeInput(conn);
+ while ((notify = PQnotifies(conn)) != NULL)
{
fprintf(stderr,
- "ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
+ "ASYNC NOTIFY of '%s' received from backend pid %d\n",
notify->relname, notify->be_pid);
PQfreemem(notify);
- break;
+ nnotifies++;
}
- PQclear(res);
}
+ fprintf(stderr, "Done.\n");
+
/* close the connection to the database and cleanup */
PQfinish(conn);
- return 0; /* Though PQfinish(conn1) has called
- * exit(1) */
+
+ return 0;
}
diff --git a/src/test/examples/testlibpq2.sql b/src/test/examples/testlibpq2.sql
index f9c74109328..fb7d3535073 100644
--- a/src/test/examples/testlibpq2.sql
+++ b/src/test/examples/testlibpq2.sql
@@ -2,4 +2,5 @@ CREATE TABLE TBL1 (i int4);
CREATE TABLE TBL2 (i int4);
-CREATE RULE r1 AS ON INSERT TO TBL1 DO [INSERT INTO TBL2 values (new.i); NOTIFY TBL2];
+CREATE RULE r1 AS ON INSERT TO TBL1 DO
+(INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
diff --git a/src/test/examples/testlibpq3.c b/src/test/examples/testlibpq3.c
index da19fa49aae..4a13ceafc28 100644
--- a/src/test/examples/testlibpq3.c
+++ b/src/test/examples/testlibpq3.c
@@ -1,34 +1,33 @@
/*
* testlibpq3.c
- * Test the C version of LIBPQ, the POSTGRES frontend library.
- * tests the binary cursor interface
+ * Test out-of-line parameters and binary I/O.
*
+ * Before running this, populate a database with the following commands
+ * (provided in src/test/examples/testlibpq3.sql):
*
+ * CREATE TABLE test1 (i int4, t text, b bytea);
*
- populate a database by doing the following:
-
-CREATE TABLE test1 (i int4, d float4, p polygon);
-
-INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0, 2.0)'::polygon);
-
-INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0, 1.0)'::polygon);
-
- the expected output is:
-
-tuple 0: got
- i = (4 bytes) 1,
- d = (4 bytes) 3.567000,
- p = (4 bytes) 2 points boundbox = (hi=3.000000/4.000000, lo = 1.000000,2.000000)
-tuple 1: got
- i = (4 bytes) 2,
- d = (4 bytes) 89.050003,
- p = (4 bytes) 2 points boundbox = (hi=4.000000/3.000000, lo = 2.000000,1.000000)
-
+ * INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
+ * INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
+ *
+ * The expected output is:
+ *
+ * tuple 0: got
+ * i = (4 bytes) 1
+ * t = (11 bytes) 'joe's place'
+ * b = (5 bytes) \000\001\002\003\004
*
*/
-#include "postgres.h" /* -> "c.h" -> int16, in access/attnum.h */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
#include "libpq-fe.h"
-#include "utils/geo_decls.h" /* for the POLYGON type */
+
+/* for ntohl/htonl */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
static void
exit_nicely(PGconn *conn)
@@ -38,146 +37,118 @@ exit_nicely(PGconn *conn)
}
int
-main()
+main(int argc, char **argv)
{
- char *pghost,
- *pgport,
- *pgoptions,
- *pgtty;
- char *dbName;
-
- /*
- * int nFields; int i, j;
- */
- int i;
- int i_fnum,
- d_fnum,
- p_fnum;
-
+ const char *conninfo;
PGconn *conn;
PGresult *res;
+ const char *paramValues[1];
+ int i,
+ j;
+ int i_fnum,
+ t_fnum,
+ b_fnum;
/*
- * begin, by setting the parameters for a backend connection if the
- * parameters are null, then the system will try to use reasonable
- * defaults by looking up environment variables or, failing that,
- * using hardwired constants
+ * If the user supplies a parameter on the command line, use it as
+ * the conninfo string; otherwise default to setting dbname=template1
+ * and using environment variables or defaults for all other connection
+ * parameters.
*/
- pghost = NULL; /* host name of the backend server */
- pgport = NULL; /* port of the backend server */
- pgoptions = NULL; /* special options to start up the backend
- * server */
- pgtty = NULL; /* debugging tty for the backend server */
+ if (argc > 1)
+ conninfo = argv[1];
+ else
+ conninfo = "dbname = template1";
- dbName = getenv("USER"); /* change this to the name of your test
- * database */
+ /* Make a connection to the database */
+ conn = PQconnectdb(conninfo);
- /* make a connection to the database */
- conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
-
- /* check to see that the backend connection was successfully made */
- if (PQstatus(conn) == CONNECTION_BAD)
+ /* Check to see that the backend connection was successfully made */
+ if (PQstatus(conn) != CONNECTION_OK)
{
- fprintf(stderr, "Connection to database '%s' failed.\n", dbName);
+ fprintf(stderr, "Connection to database '%s' failed.\n", PQdb(conn));
fprintf(stderr, "%s", PQerrorMessage(conn));
exit_nicely(conn);
}
- /* start a transaction block */
- res = PQexec(conn, "BEGIN");
- if (PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- fprintf(stderr, "BEGIN command failed\n");
- PQclear(res);
- exit_nicely(conn);
- }
-
/*
- * should PQclear PGresult whenever it is no longer needed to avoid
- * memory leaks
+ * The point of this program is to illustrate use of PQexecParams()
+ * with out-of-line parameters, as well as binary transmission of
+ * results. By using out-of-line parameters we can avoid a lot of
+ * tedious mucking about with quoting and escaping. Notice how we
+ * don't have to do anything special with the quote mark in the
+ * parameter value.
*/
- PQclear(res);
- /*
- * fetch instances from the pg_database, the system catalog of
- * databases
- */
- res = PQexec(conn, "DECLARE mycursor BINARY CURSOR FOR select * from test1");
- if (res == NULL ||
- PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- fprintf(stderr, "DECLARE CURSOR command failed\n");
- if (res)
- PQclear(res);
- exit_nicely(conn);
- }
- PQclear(res);
+ /* Here is our out-of-line parameter value */
+ paramValues[0] = "joe's place";
+
+ res = PQexecParams(conn,
+ "SELECT * FROM test1 WHERE t = $1",
+ 1, /* one param */
+ NULL, /* let the backend deduce param type */
+ paramValues,
+ NULL, /* don't need param lengths since text */
+ NULL, /* default to all text params */
+ 1); /* ask for binary results */
- res = PQexec(conn, "FETCH ALL in mycursor");
- if (res == NULL ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- fprintf(stderr, "FETCH ALL command didn't return tuples properly\n");
- if (res)
- PQclear(res);
+ fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn));
+ PQclear(res);
exit_nicely(conn);
}
+ /* Use PQfnumber to avoid assumptions about field order in result */
i_fnum = PQfnumber(res, "i");
- d_fnum = PQfnumber(res, "d");
- p_fnum = PQfnumber(res, "p");
+ t_fnum = PQfnumber(res, "t");
+ b_fnum = PQfnumber(res, "b");
- for (i = 0; i < 3; i++)
- {
- printf("type[%d] = %d, size[%d] = %d\n",
- i, PQftype(res, i),
- i, PQfsize(res, i));
- }
for (i = 0; i < PQntuples(res); i++)
{
- int *ival;
- float *dval;
- int plen;
- POLYGON *pval;
+ char *iptr;
+ char *tptr;
+ char *bptr;
+ int blen;
+ int ival;
- /* we hard-wire this to the 3 fields we know about */
- ival = (int *) PQgetvalue(res, i, i_fnum);
- dval = (float *) PQgetvalue(res, i, d_fnum);
- plen = PQgetlength(res, i, p_fnum);
+ /* Get the field values (we ignore possibility they are null!) */
+ iptr = PQgetvalue(res, i, i_fnum);
+ tptr = PQgetvalue(res, i, t_fnum);
+ bptr = PQgetvalue(res, i, b_fnum);
/*
- * plen doesn't include the length field so need to increment by
- * VARHDSZ
+ * The binary representation of INT4 is in network byte order,
+ * which we'd better coerce to the local byte order.
*/
- pval = (POLYGON *) malloc(plen + VARHDRSZ);
- pval->size = plen;
- memmove((char *) &pval->npts, PQgetvalue(res, i, p_fnum), plen);
- printf("tuple %d: got\n", i);
- printf(" i = (%d bytes) %d,\n",
- PQgetlength(res, i, i_fnum), *ival);
- printf(" d = (%d bytes) %f,\n",
- PQgetlength(res, i, d_fnum), *dval);
- printf(" p = (%d bytes) %d points \tboundbox = (hi=%f/%f, lo = %f,%f)\n",
- PQgetlength(res, i, d_fnum),
- pval->npts,
- pval->boundbox.high.x,
- pval->boundbox.high.y,
- pval->boundbox.low.x,
- pval->boundbox.low.y);
- }
+ ival = ntohl(*((uint32_t *) iptr));
- PQclear(res);
+ /*
+ * The binary representation of TEXT is, well, text, and since
+ * libpq was nice enough to append a zero byte to it, it'll work
+ * just fine as a C string.
+ *
+ * The binary representation of BYTEA is a bunch of bytes, which
+ * could include embedded nulls so we have to pay attention to
+ * field length.
+ */
+ blen = PQgetlength(res, i, b_fnum);
- /* close the portal */
- res = PQexec(conn, "CLOSE mycursor");
- PQclear(res);
+ printf("tuple %d: got\n", i);
+ printf(" i = (%d bytes) %d\n",
+ PQgetlength(res, i, i_fnum), ival);
+ printf(" t = (%d bytes) '%s'\n",
+ PQgetlength(res, i, t_fnum), tptr);
+ printf(" b = (%d bytes) ", blen);
+ for (j = 0; j < blen; j++)
+ printf("\\%03o", bptr[j]);
+ printf("\n\n");
+ }
- /* end the transaction */
- res = PQexec(conn, "END");
PQclear(res);
/* close the connection to the database and cleanup */
PQfinish(conn);
- return 0; /* Though PQfinish(conn1) has called
- * exit(1) */
+
+ return 0;
}
diff --git a/src/test/examples/testlibpq3.sql b/src/test/examples/testlibpq3.sql
index f024c0b071b..9d9e217e5dd 100644
--- a/src/test/examples/testlibpq3.sql
+++ b/src/test/examples/testlibpq3.sql
@@ -1,6 +1,4 @@
-CREATE TABLE test1 (i int4, d float4, p polygon);
-
-INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0, 2.0)'::polygon);
-
-INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0, 1.0)'::polygon);
+CREATE TABLE test1 (i int4, t text, b bytea);
+INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
+INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
diff --git a/src/test/examples/testlibpq4.c b/src/test/examples/testlibpq4.c
index e497e7a8fb9..6fa167e3b1c 100644
--- a/src/test/examples/testlibpq4.c
+++ b/src/test/examples/testlibpq4.c
@@ -1,11 +1,11 @@
/*
* testlibpq4.c
- * this test programs shows to use LIBPQ to make multiple backend
+ * this test program shows to use LIBPQ to make multiple backend
* connections
*
- *
*/
#include <stdio.h>
+#include <stdlib.h>
#include "libpq-fe.h"
static void