Commit d1959f9f authored by Tom Lane's avatar Tom Lane

Improve testlibpq3.c's example of PQexecParams() usage to include sending

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.
parent 61abd9a3
<!-- <!--
$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"> <chapter id="libpq">
...@@ -1187,6 +1187,26 @@ than one nonempty command.) This is a limitation of the underlying protocol, ...@@ -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. but has some usefulness as an extra defense against SQL-injection attacks.
</para> </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> <para>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
...@@ -4226,7 +4246,7 @@ testlibpq.o(.text+0xa4): undefined reference to `PQerrorMessage' ...@@ -4226,7 +4246,7 @@ testlibpq.o(.text+0xa4): undefined reference to `PQerrorMessage'
/* /*
* testlibpq.c * 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;stdio.h&gt;
#include &lt;stdlib.h&gt; #include &lt;stdlib.h&gt;
...@@ -4235,112 +4255,111 @@ testlibpq.o(.text+0xa4): undefined reference to `PQerrorMessage' ...@@ -4235,112 +4255,111 @@ testlibpq.o(.text+0xa4): undefined reference to `PQerrorMessage'
static void static void
exit_nicely(PGconn *conn) exit_nicely(PGconn *conn)
{ {
PQfinish(conn); PQfinish(conn);
exit(1); exit(1);
} }
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
const char *conninfo; const char *conninfo;
PGconn *conn; PGconn *conn;
PGresult *res; PGresult *res;
int nFields; int nFields;
int i, int i,
j; j;
/* /*
* If the user supplies a parameter on the command line, use it as * If the user supplies a parameter on the command line, use it as the
* the conninfo string; otherwise default to setting dbname=postgres * conninfo string; otherwise default to setting dbname=postgres and using
* and using environment variables or defaults for all other connection * environment variables or defaults for all other connection parameters.
* parameters. */
*/ if (argc &gt; 1)
if (argc &gt; 1) conninfo = argv[1];
conninfo = argv[1]; else
else conninfo = "dbname = postgres";
conninfo = "dbname = postgres";
/* Make a connection to the database */
/* Make a connection to the database */ conn = PQconnectdb(conninfo);
conn = PQconnectdb(conninfo);
/* Check to see that the backend connection was successfully made */
/* Check to see that the backend connection was successfully made */ if (PQstatus(conn) != CONNECTION_OK)
if (PQstatus(conn) != CONNECTION_OK) {
{ fprintf(stderr, "Connection to database failed: %s",
fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn));
PQerrorMessage(conn)); exit_nicely(conn);
exit_nicely(conn); }
}
/*
/* * Our test case here involves using a cursor, for which we must be inside
* Our test case here involves using a cursor, for which we must be * a transaction block. We could do the whole thing with a single
* inside a transaction block. We could do the whole thing with a * PQexec() of "select * from pg_database", but that's too trivial to make
* single PQexec() of "select * from pg_database", but that's too * a good example.
* trivial to make a good example. */
*/
/* Start a transaction block */
/* Start a transaction block */ res = PQexec(conn, "BEGIN");
res = PQexec(conn, "BEGIN"); if (PQresultStatus(res) != PGRES_COMMAND_OK)
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
{ fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn));
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); PQclear(res);
exit_nicely(conn);
/* }
* Fetch rows from pg_database, the system catalog of databases
*/ /*
res = PQexec(conn, "DECLARE myportal CURSOR FOR select * from pg_database"); * Should PQclear PGresult whenever it is no longer needed to avoid memory
if (PQresultStatus(res) != PGRES_COMMAND_OK) * leaks
{ */
fprintf(stderr, "DECLARE CURSOR failed: %s", PQerrorMessage(conn)); PQclear(res);
PQclear(res);
exit_nicely(conn); /*
} * 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); 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"); /* first, print out the attribute names */
if (PQresultStatus(res) != PGRES_TUPLES_OK) nFields = PQnfields(res);
{ for (i = 0; i &lt; nFields; i++)
fprintf(stderr, "FETCH ALL failed: %s", PQerrorMessage(conn)); printf("%-15s", PQfname(res, i));
PQclear(res); printf("\n\n");
exit_nicely(conn);
}
/* first, print out the attribute names */ /* next, print out the rows */
nFields = PQnfields(res); for (i = 0; i &lt; PQntuples(res); i++)
for (i = 0; i &lt; nFields; i++) {
printf("%-15s", PQfname(res, i)); for (j = 0; j &lt; nFields; j++)
printf("\n\n"); printf("%-15s", PQgetvalue(res, i, j));
printf("\n");
}
/* next, print out the rows */ PQclear(res);
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 ... */ /* close the portal ... we don't bother to check for errors ... */
res = PQexec(conn, "CLOSE myportal"); res = PQexec(conn, "CLOSE myportal");
PQclear(res); PQclear(res);
/* end the transaction */ /* end the transaction */
res = PQexec(conn, "END"); res = PQexec(conn, "END");
PQclear(res); PQclear(res);
/* close the connection to the database and cleanup */ /* close the connection to the database and cleanup */
PQfinish(conn); PQfinish(conn);
return 0; return 0;
} }
</programlisting> </programlisting>
</example> </example>
...@@ -4351,7 +4370,7 @@ main(int argc, char **argv) ...@@ -4351,7 +4370,7 @@ main(int argc, char **argv)
<programlisting> <programlisting>
/* /*
* testlibpq2.c * testlibpq2.c
* Test of the asynchronous notification interface * Test of the asynchronous notification interface
* *
* Start this program, then from psql in another window do * Start this program, then from psql in another window do
* NOTIFY TBL2; * NOTIFY TBL2;
...@@ -4382,102 +4401,101 @@ main(int argc, char **argv) ...@@ -4382,102 +4401,101 @@ main(int argc, char **argv)
static void static void
exit_nicely(PGconn *conn) exit_nicely(PGconn *conn)
{ {
PQfinish(conn); PQfinish(conn);
exit(1); exit(1);
} }
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
const char *conninfo; const char *conninfo;
PGconn *conn; PGconn *conn;
PGresult *res; PGresult *res;
PGnotify *notify; PGnotify *notify;
int nnotifies; 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 * Sleep until something happens on the connection. We use select(2)
* the conninfo string; otherwise default to setting dbname=postgres * to wait for input, but you could also use poll() or similar
* and using environment variables or defaults for all other connection * facilities.
* parameters.
*/ */
if (argc &gt; 1) int sock;
conninfo = argv[1]; fd_set input_mask;
else
conninfo = "dbname = postgres";
/* Make a connection to the database */ sock = PQsocket(conn);
conn = PQconnectdb(conninfo);
/* Check to see that the backend connection was successfully made */ if (sock &lt; 0)
if (PQstatus(conn) != CONNECTION_OK) break; /* shouldn't happen */
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
exit_nicely(conn);
}
/* FD_ZERO(&amp;input_mask);
* Issue LISTEN command to enable notifications from the rule's NOTIFY. FD_SET(sock, &amp;input_mask);
*/
res = PQexec(conn, "LISTEN TBL2"); if (select(sock + 1, &amp;input_mask, NULL, NULL, NULL) &lt; 0)
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{ {
fprintf(stderr, "LISTEN command failed: %s", PQerrorMessage(conn)); fprintf(stderr, "select() failed: %s\n", strerror(errno));
PQclear(res); exit_nicely(conn);
exit_nicely(conn);
} }
/* /* Now check for input */
* should PQclear PGresult whenever it is no longer needed to avoid PQconsumeInput(conn);
* memory leaks while ((notify = PQnotifies(conn)) != NULL)
*/
PQclear(res);
/* Quit after four notifies are received. */
nnotifies = 0;
while (nnotifies &lt; 4)
{ {
/* fprintf(stderr,
* Sleep until something happens on the connection. We use select(2) "ASYNC NOTIFY of '%s' received from backend pid %d\n",
* to wait for input, but you could also use poll() or similar notify-&gt;relname, notify-&gt;be_pid);
* facilities. PQfreemem(notify);
*/ nnotifies++;
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"); fprintf(stderr, "Done.\n");
/* close the connection to the database and cleanup */ /* close the connection to the database and cleanup */
PQfinish(conn); PQfinish(conn);
return 0; return 0;
} }
</programlisting> </programlisting>
</example> </example>
...@@ -4488,7 +4506,7 @@ main(int argc, char **argv) ...@@ -4488,7 +4506,7 @@ main(int argc, char **argv)
<programlisting> <programlisting>
/* /*
* testlibpq3.c * 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 * Before running this, populate a database with the following commands
* (provided in src/test/examples/testlibpq3.sql): * (provided in src/test/examples/testlibpq3.sql):
...@@ -4505,6 +4523,10 @@ main(int argc, char **argv) ...@@ -4505,6 +4523,10 @@ main(int argc, char **argv)
* t = (11 bytes) 'joe's place' * t = (11 bytes) 'joe's place'
* b = (5 bytes) \000\001\002\003\004 * 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;stdio.h&gt;
#include &lt;stdlib.h&gt; #include &lt;stdlib.h&gt;
...@@ -4520,125 +4542,178 @@ main(int argc, char **argv) ...@@ -4520,125 +4542,178 @@ main(int argc, char **argv)
static void static void
exit_nicely(PGconn *conn) exit_nicely(PGconn *conn)
{ {
PQfinish(conn); PQfinish(conn);
exit(1); 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; int i,
PGconn *conn; j;
PGresult *res; int i_fnum,
const char *paramValues[1]; t_fnum,
int i, b_fnum;
j;
int i_fnum, /* Use PQfnumber to avoid assumptions about field order in result */
t_fnum, i_fnum = PQfnumber(res, "i");
b_fnum; 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 binary representation of INT4 is in network byte order, which
* the conninfo string; otherwise default to setting dbname=postgres * we'd better coerce to the local byte order.
* and using environment variables or defaults for all other connection
* parameters.
*/ */
if (argc &gt; 1) ival = ntohl(*((uint32_t *) iptr));
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() * The binary representation of TEXT is, well, text, and since libpq
* with out-of-line parameters, as well as binary transmission of * was nice enough to append a zero byte to it, it'll work just fine
* results. By using out-of-line parameters we can avoid a lot of * as a C string.
* tedious mucking about with quoting and escaping. Notice how we *
* don't have to do anything special with the quote mark in the * The binary representation of BYTEA is a bunch of bytes, which could
* parameter value. * 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 */ int
paramValues[0] = "joe's place"; main(int argc, char **argv)
{
res = PQexecParams(conn, const char *conninfo;
"SELECT * FROM test1 WHERE t = $1", PGconn *conn;
1, /* one param */ PGresult *res;
NULL, /* let the backend deduce param type */ const char *paramValues[1];
paramValues, int paramLengths[1];
NULL, /* don't need param lengths since text */ int paramFormats[1];
NULL, /* default to all text params */ uint32_t binaryIntVal;
1); /* ask for binary results */
/*
if (PQresultStatus(res) != PGRES_TUPLES_OK) * If the user supplies a parameter on the command line, use it as the
{ * conninfo string; otherwise default to setting dbname=postgres and using
fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn)); * environment variables or defaults for all other connection parameters.
PQclear(res); */
exit_nicely(conn); if (argc &gt; 1)
} conninfo = argv[1];
else
/* Use PQfnumber to avoid assumptions about field order in result */ conninfo = "dbname = postgres";
i_fnum = PQfnumber(res, "i");
t_fnum = PQfnumber(res, "t"); /* Make a connection to the database */
b_fnum = PQfnumber(res, "b"); 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++) show_binary_results(res);
{
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); PQclear(res);
/* close the connection to the database and cleanup */ /* close the connection to the database and cleanup */
PQfinish(conn); PQfinish(conn);
return 0; return 0;
} }
</programlisting> </programlisting>
</example> </example>
......
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
* t = (11 bytes) 'joe's place' * t = (11 bytes) 'joe's place'
* b = (5 bytes) \000\001\002\003\004 * 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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -36,6 +40,66 @@ exit_nicely(PGconn *conn) ...@@ -36,6 +40,66 @@ exit_nicely(PGconn *conn)
exit(1); 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 int
main(int argc, char **argv) main(int argc, char **argv)
{ {
...@@ -43,11 +107,9 @@ main(int argc, char **argv) ...@@ -43,11 +107,9 @@ main(int argc, char **argv)
PGconn *conn; PGconn *conn;
PGresult *res; PGresult *res;
const char *paramValues[1]; const char *paramValues[1];
int i, int paramLengths[1];
j; int paramFormats[1];
int i_fnum, uint32_t binaryIntVal;
t_fnum,
b_fnum;
/* /*
* If the user supplies a parameter on the command line, use it as the * If the user supplies a parameter on the command line, use it as the
...@@ -72,10 +134,13 @@ main(int argc, char **argv) ...@@ -72,10 +134,13 @@ main(int argc, char **argv)
/* /*
* The point of this program is to illustrate use of PQexecParams() with * The point of this program is to illustrate use of PQexecParams() with
* out-of-line parameters, as well as binary transmission of results. By * out-of-line parameters, as well as binary transmission of data.
* 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 * This first example transmits the parameters as text, but receives the
* anything special with the quote mark in the parameter value. * 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 */ /* Here is our out-of-line parameter value */
...@@ -97,51 +162,46 @@ main(int argc, char **argv) ...@@ -97,51 +162,46 @@ main(int argc, char **argv)
exit_nicely(conn); exit_nicely(conn);
} }
/* Use PQfnumber to avoid assumptions about field order in result */ show_binary_results(res);
i_fnum = PQfnumber(res, "i");
t_fnum = PQfnumber(res, "t");
b_fnum = PQfnumber(res, "b");
for (i = 0; i < PQntuples(res); i++) PQclear(res);
{
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); * In this second example we transmit an integer parameter in binary
tptr = PQgetvalue(res, i, t_fnum); * form, and again retrieve the results in binary form.
bptr = PQgetvalue(res, i, b_fnum); *
* 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 */
* The binary representation of INT4 is in network byte order, which binaryIntVal = htonl((uint32_t) 2);
* we'd better coerce to the local byte order.
*/
ival = ntohl(*((uint32_t *) iptr));
/* /* Set up parameter arrays for PQexecParams */
* The binary representation of TEXT is, well, text, and since libpq paramValues[0] = (char *) &binaryIntVal;
* was nice enough to append a zero byte to it, it'll work just fine paramLengths[0] = sizeof(binaryIntVal);
* as a C string. paramFormats[0] = 1; /* binary */
*
* 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); res = PQexecParams(conn,
printf(" i = (%d bytes) %d\n", "SELECT * FROM test1 WHERE i = $1::int4",
PQgetlength(res, i, i_fnum), ival); 1, /* one param */
printf(" t = (%d bytes) '%s'\n", NULL, /* let the backend deduce param type */
PQgetlength(res, i, t_fnum), tptr); paramValues,
printf(" b = (%d bytes) ", blen); paramLengths,
for (j = 0; j < blen; j++) paramFormats,
printf("\\%03o", bptr[j]); 1); /* ask for binary results */
printf("\n\n");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
} }
show_binary_results(res);
PQclear(res); PQclear(res);
/* close the connection to the database and cleanup */ /* close the connection to the database and cleanup */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment