]> granicus.if.org Git - postgresql/commitdiff
Improve testlibpq3.c's example of PQexecParams() usage to include sending
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 20 Oct 2005 23:57:52 +0000 (23:57 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 20 Oct 2005 23:57:52 +0000 (23:57 +0000)
a parameter in binary format.  Also, add a TIP explaining how to use casts
in the query text to avoid needing to specify parameter types by OID.
Also fix bogus spacing --- apparently somebody expanded the tabs in the
example programs to 8 spaces instead of 4 when transposing them into SGML.

doc/src/sgml/libpq.sgml
src/test/examples/testlibpq3.c

index eafbaa777f50dae29e48080fcc53bfa950f26011..63fb3ab4192cc3ebc2bec496885aaf4a3dd3fb7d 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.195 2005/10/20 21:04:14 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.196 2005/10/20 23:57:51 tgl Exp $
 -->
 
  <chapter id="libpq">
@@ -1187,6 +1187,26 @@ than one nonempty command.)  This is a limitation of the underlying protocol,
 but has some usefulness as an extra defense against SQL-injection attacks.
 </para>
 
+<tip>
+<para>
+Specifying parameter types via OIDs is tedious, particularly if you prefer
+not to hard-wire particular OID values into your program.  However, you can
+avoid doing so even in cases where the server by itself cannot determine the
+type of the parameter, or chooses a different type than you want.  In the
+SQL command text, attach an explicit cast to the parameter symbol to show what
+data type you will send.  For example,
+<programlisting>
+select * from mytable where x = $1::bigint;
+</programlisting>
+This forces parameter <literal>$1</> to be treated as <type>bigint</>, whereas
+by default it would be assigned the same type as <literal>x</>.  Forcing the
+parameter type decision, either this way or by specifying a numeric type OID,
+is strongly recommended when sending parameter values in binary format, because
+binary format has less redundancy than text format and so there is less chance
+that the server will detect a type mismatch mistake for you.
+</para>
+</tip>
+
 <para>
 <variablelist>
 <varlistentry>
@@ -4226,7 +4246,7 @@ testlibpq.o(.text+0xa4): undefined reference to `PQerrorMessage'
 /*
  * testlibpq.c
  *
- *              Test the C version of LIBPQ, the POSTGRES frontend library.
+ *      Test the C version of libpq, the PostgreSQL frontend library.
  */
 #include &lt;stdio.h&gt;
 #include &lt;stdlib.h&gt;
@@ -4235,112 +4255,111 @@ testlibpq.o(.text+0xa4): undefined reference to `PQerrorMessage'
 static void
 exit_nicely(PGconn *conn)
 {
-        PQfinish(conn);
-        exit(1);
+    PQfinish(conn);
+    exit(1);
 }
 
 int
 main(int argc, char **argv)
 {
-        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=postgres
-         * and using environment variables or defaults for all other connection
-         * parameters.
-         */
-        if (argc &gt; 1)
-                conninfo = argv[1];
-        else
-                conninfo = "dbname = postgres";
-
-        /* 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 failed: %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
-         */
+    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=postgres and using
+     * environment variables or defaults for all other connection parameters.
+     */
+    if (argc &gt; 1)
+        conninfo = argv[1];
+    else
+        conninfo = "dbname = postgres";
+
+    /* 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 failed: %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);
-
-        /*
-         * 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);
-        }
+        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);
+    }
 
-        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");
 
-        /* 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");
+    }
 
-        /* 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);
+    PQclear(res);
 
-        /* close the portal ... we don't bother to check for errors ... */
-        res = PQexec(conn, "CLOSE myportal");
-        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);
+    /* end the transaction */
+    res = PQexec(conn, "END");
+    PQclear(res);
 
-        /* close the connection to the database and cleanup */
-        PQfinish(conn);
+    /* close the connection to the database and cleanup */
+    PQfinish(conn);
 
-        return 0;
+    return 0;
 }
 </programlisting>
   </example>
@@ -4351,7 +4370,7 @@ main(int argc, char **argv)
 <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;
@@ -4382,102 +4401,101 @@ main(int argc, char **argv)
 static void
 exit_nicely(PGconn *conn)
 {
-        PQfinish(conn);
-        exit(1);
+    PQfinish(conn);
+    exit(1);
 }
 
 int
 main(int argc, char **argv)
 {
-        const char *conninfo;
-        PGconn     *conn;
-        PGresult   *res;
-        PGnotify   *notify;
-        int                     nnotifies;
-
+    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=postgres and using
+     * environment variables or defaults for all other connection parameters.
+     */
+    if (argc &gt; 1)
+        conninfo = argv[1];
+    else
+        conninfo = "dbname = postgres";
+
+    /* 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 failed: %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)
+    {
         /*
-         * If the user supplies a parameter on the command line, use it as
-         * the conninfo string; otherwise default to setting dbname=postgres
-         * and using environment variables or defaults for all other connection
-         * parameters.
+         * Sleep until something happens on the connection.  We use select(2)
+         * to wait for input, but you could also use poll() or similar
+         * facilities.
          */
-        if (argc &gt; 1)
-                conninfo = argv[1];
-        else
-                conninfo = "dbname = postgres";
+        int         sock;
+        fd_set      input_mask;
 
-        /* Make a connection to the database */
-        conn = PQconnectdb(conninfo);
+        sock = PQsocket(conn);
 
-        /* Check to see that the backend connection was successfully made */
-        if (PQstatus(conn) != CONNECTION_OK)
-        {
-                fprintf(stderr, "Connection to database failed: %s",
-                        PQerrorMessage(conn));
-                exit_nicely(conn);
-        }
+        if (sock &lt; 0)
+            break;              /* shouldn't happen */
 
-        /*
-         * Issue LISTEN command to enable notifications from the rule's NOTIFY.
-         */
-        res = PQexec(conn, "LISTEN TBL2");
-        if (PQresultStatus(res) != PGRES_COMMAND_OK)
+        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, "LISTEN command failed: %s", PQerrorMessage(conn));
-                PQclear(res);
-                exit_nicely(conn);
+            fprintf(stderr, "select() failed: %s\n", strerror(errno));
+            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)
+        /* Now check for input */
+        PQconsumeInput(conn);
+        while ((notify = PQnotifies(conn)) != NULL)
         {
-                /*
-                 * 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,
+                    "ASYNC NOTIFY of '%s' received from backend pid %d\n",
+                    notify-&gt;relname, notify-&gt;be_pid);
+            PQfreemem(notify);
+            nnotifies++;
         }
+    }
 
-        fprintf(stderr, "Done.\n");
+    fprintf(stderr, "Done.\n");
 
-        /* close the connection to the database and cleanup */
-        PQfinish(conn);
+    /* close the connection to the database and cleanup */
+    PQfinish(conn);
 
-        return 0;
+    return 0;
 }
 </programlisting>
   </example>
@@ -4488,7 +4506,7 @@ main(int argc, char **argv)
 <programlisting>
 /*
  * testlibpq3.c
- *              Test out-of-line parameters and binary I/O.
+ *      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):
@@ -4505,6 +4523,10 @@ main(int argc, char **argv)
  *  t = (11 bytes) 'joe's place'
  *  b = (5 bytes) \000\001\002\003\004
  *
+ * tuple 0: got
+ *  i = (4 bytes) 2
+ *  t = (8 bytes) 'ho there'
+ *  b = (5 bytes) \004\003\002\001\000
  */
 #include &lt;stdio.h&gt;
 #include &lt;stdlib.h&gt;
@@ -4520,125 +4542,178 @@ main(int argc, char **argv)
 static void
 exit_nicely(PGconn *conn)
 {
-        PQfinish(conn);
-        exit(1);
+    PQfinish(conn);
+    exit(1);
 }
 
-int
-main(int argc, char **argv)
+/*
+ * This function prints a query result that is a binary-format fetch from
+ * a table defined as in the comment above.  We split it out because the
+ * main() function uses it twice.
+ */
+static void
+show_binary_results(PGresult *res)
 {
-        const char *conninfo;
-        PGconn     *conn;
-        PGresult   *res;
-        const char *paramValues[1];
-        int                     i,
-                                j;
-        int                     i_fnum,
-                                t_fnum,
-                                b_fnum;
+    int         i,
+                j;
+    int         i_fnum,
+                t_fnum,
+                b_fnum;
+
+    /* 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);
 
         /*
-         * If the user supplies a parameter on the command line, use it as
-         * the conninfo string; otherwise default to setting dbname=postgres
-         * and using environment variables or defaults for all other connection
-         * parameters.
+         * The binary representation of INT4 is in network byte order, which
+         * we'd better coerce to the local byte order.
          */
-        if (argc &gt; 1)
-                conninfo = argv[1];
-        else
-                conninfo = "dbname = postgres";
-
-        /* 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 failed: %s",
-                        PQerrorMessage(conn));
-                exit_nicely(conn);
-        }
+        ival = ntohl(*((uint32_t *) iptr));
 
         /*
-         * 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.
+         * 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");
+    }
+}
 
-        /* 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");
+int
+main(int argc, char **argv)
+{
+    const char *conninfo;
+    PGconn     *conn;
+    PGresult   *res;
+    const char *paramValues[1];
+    int         paramLengths[1];
+    int         paramFormats[1];
+    uint32_t    binaryIntVal;
+
+    /*
+     * If the user supplies a parameter on the command line, use it as the
+     * conninfo string; otherwise default to setting dbname=postgres and using
+     * environment variables or defaults for all other connection parameters.
+     */
+    if (argc &gt; 1)
+        conninfo = argv[1];
+    else
+        conninfo = "dbname = postgres";
+
+    /* 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 failed: %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 data.
+     *
+     * This first example transmits the parameters as text, but receives the
+     * results in binary format.  By using out-of-line parameters we can
+     * avoid a lot of tedious mucking about with quoting and escaping, even
+     * though the data is text.  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);
+    }
+
+    show_binary_results(res);
+
+    PQclear(res);
+
+    /*
+     * In this second example we transmit an integer parameter in binary
+     * form, and again retrieve the results in binary form.
+     *
+     * Although we tell PQexecParams we are letting the backend deduce
+     * parameter type, we really force the decision by casting the parameter
+     * symbol in the query text.  This is a good safety measure when sending
+     * binary parameters.
+     */
+
+    /* Convert integer value "2" to network byte order */
+    binaryIntVal = htonl((uint32_t) 2);
+
+    /* Set up parameter arrays for PQexecParams */
+    paramValues[0] = (char *) &amp;binaryIntVal;
+    paramLengths[0] = sizeof(binaryIntVal);
+    paramFormats[0] = 1;        /* binary */
+
+    res = PQexecParams(conn,
+                       "SELECT * FROM test1 WHERE i = $1::int4",
+                       1,       /* one param */
+                       NULL,    /* let the backend deduce param type */
+                       paramValues,
+                       paramLengths,
+                       paramFormats,
+                       1);      /* ask for binary results */
+
+    if (PQresultStatus(res) != PGRES_TUPLES_OK)
+    {
+        fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn));
+        PQclear(res);
+        exit_nicely(conn);
+    }
 
-        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");
-        }
+    show_binary_results(res);
 
-        PQclear(res);
+    PQclear(res);
 
-        /* close the connection to the database and cleanup */
-        PQfinish(conn);
+    /* close the connection to the database and cleanup */
+    PQfinish(conn);
 
-        return 0;
+    return 0;
 }
 </programlisting>
   </example>
index 918d142c7b6a677d71a15a0ba77edb4051750d73..c7e4e097322659d8ffcbd84bd2f335f1f87cb6ba 100644 (file)
  *     t = (11 bytes) 'joe's place'
  *     b = (5 bytes) \000\001\002\003\004
  *
+ * tuple 0: got
+ *  i = (4 bytes) 2
+ *  t = (8 bytes) 'ho there'
+ *  b = (5 bytes) \004\003\002\001\000
  */
 #include <stdio.h>
 #include <stdlib.h>
@@ -36,6 +40,66 @@ exit_nicely(PGconn *conn)
        exit(1);
 }
 
+/*
+ * This function prints a query result that is a binary-format fetch from
+ * a table defined as in the comment above.  We split it out because the
+ * main() function uses it twice.
+ */
+static void
+show_binary_results(PGresult *res)
+{
+       int                     i,
+                               j;
+       int                     i_fnum,
+                               t_fnum,
+                               b_fnum;
+
+       /* 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 < 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 < blen; j++)
+                       printf("\\%03o", bptr[j]);
+               printf("\n\n");
+       }
+}
+
 int
 main(int argc, char **argv)
 {
@@ -43,11 +107,9 @@ main(int argc, char **argv)
        PGconn     *conn;
        PGresult   *res;
        const char *paramValues[1];
-       int                     i,
-                               j;
-       int                     i_fnum,
-                               t_fnum,
-                               b_fnum;
+       int                     paramLengths[1];
+       int                     paramFormats[1];
+       uint32_t        binaryIntVal;
 
        /*
         * If the user supplies a parameter on the command line, use it as the
@@ -72,10 +134,13 @@ main(int argc, char **argv)
 
        /*
         * 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.
+        * out-of-line parameters, as well as binary transmission of data.
+        *
+        * This first example transmits the parameters as text, but receives the
+        * results in binary format.  By using out-of-line parameters we can
+        * avoid a lot of tedious mucking about with quoting and escaping, even
+        * though the data is text.  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 */
@@ -97,51 +162,46 @@ main(int argc, char **argv)
                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");
+       show_binary_results(res);
 
-       for (i = 0; i < PQntuples(res); i++)
-       {
-               char       *iptr;
-               char       *tptr;
-               char       *bptr;
-               int                     blen;
-               int                     ival;
+       PQclear(res);
 
-               /* 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);
+       /*
+        * In this second example we transmit an integer parameter in binary
+        * form, and again retrieve the results in binary form.
+        *
+        * Although we tell PQexecParams we are letting the backend deduce
+        * parameter type, we really force the decision by casting the parameter
+        * symbol in the query text.  This is a good safety measure when sending
+        * binary parameters.
+        */
 
-               /*
-                * 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));
+       /* Convert integer value "2" to network byte order */
+       binaryIntVal = htonl((uint32_t) 2);
 
-               /*
-                * 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);
+       /* Set up parameter arrays for PQexecParams */
+       paramValues[0] = (char *) &binaryIntVal;
+       paramLengths[0] = sizeof(binaryIntVal);
+       paramFormats[0] = 1;            /* binary */
 
-               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");
+       res = PQexecParams(conn,
+                                          "SELECT * FROM test1 WHERE i = $1::int4",
+                                          1,           /* one param */
+                                          NULL,        /* let the backend deduce param type */
+                                          paramValues,
+                                          paramLengths,
+                                          paramFormats,
+                                          1);          /* ask for binary results */
+
+       if (PQresultStatus(res) != PGRES_TUPLES_OK)
+       {
+               fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn));
+               PQclear(res);
+               exit_nicely(conn);
        }
 
+       show_binary_results(res);
+
        PQclear(res);
 
        /* close the connection to the database and cleanup */