From d1959f9ff6bcb68b081893bea85b937ae0588853 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 20 Oct 2005 23:57:52 +0000 Subject: [PATCH] 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. --- doc/src/sgml/libpq.sgml | 625 ++++++++++++++++++--------------- src/test/examples/testlibpq3.c | 168 ++++++--- 2 files changed, 464 insertions(+), 329 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index eafbaa777f..63fb3ab419 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,5 +1,5 @@ @@ -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. + + +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, + +select * from mytable where x = $1::bigint; + +This forces parameter $1 to be treated as bigint, whereas +by default it would be assigned the same type as 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. + + + @@ -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 <stdio.h> #include <stdlib.h> @@ -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; + 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 > 1) - conninfo = argv[1]; - else - conninfo = "dbname = postgres"; + /* + * 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 > 1) + conninfo = argv[1]; + else + conninfo = "dbname = postgres"; - /* Make a connection to the database */ - conn = PQconnectdb(conninfo); + /* 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); - } + /* 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. - */ + /* + * 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 - */ + /* 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); + } - /* - * 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); - } + /* + * 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 < nFields; i++) - printf("%-15s", PQfname(res, i)); - printf("\n\n"); - - /* next, print out the rows */ - for (i = 0; i < PQntuples(res); i++) - { - for (j = 0; j < nFields; j++) - printf("%-15s", PQgetvalue(res, i, j)); - printf("\n"); - } - + 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); + } - /* close the portal ... we don't bother to check for errors ... */ - res = PQexec(conn, "CLOSE myportal"); - PQclear(res); + /* first, print out the attribute names */ + nFields = PQnfields(res); + for (i = 0; i < nFields; i++) + printf("%-15s", PQfname(res, i)); + printf("\n\n"); - /* end the transaction */ - res = PQexec(conn, "END"); - PQclear(res); + /* next, print out the rows */ + for (i = 0; i < PQntuples(res); i++) + { + for (j = 0; j < nFields; j++) + printf("%-15s", PQgetvalue(res, i, j)); + printf("\n"); + } - /* close the connection to the database and cleanup */ - PQfinish(conn); + PQclear(res); - return 0; + /* 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; } @@ -4351,7 +4370,7 @@ main(int argc, char **argv) /* * 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 > 1) - conninfo = argv[1]; - else - conninfo = "dbname = postgres"; + /* + * 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 > 1) + conninfo = argv[1]; + else + conninfo = "dbname = postgres"; - /* Make a connection to the database */ - conn = PQconnectdb(conninfo); + /* 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); - } + /* 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 - */ + /* + * 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); + } - /* Quit after four notifies are received. */ - nnotifies = 0; - while (nnotifies < 4) + /* + * 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 < 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 < 0) + break; /* shouldn't happen */ + + FD_ZERO(&input_mask); + FD_SET(sock, &input_mask); + + if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0) { - /* - * 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' received from backend pid %d\n", - notify->relname, notify->be_pid); - PQfreemem(notify); - nnotifies++; - } + fprintf(stderr, "select() failed: %s\n", strerror(errno)); + exit_nicely(conn); } - fprintf(stderr, "Done.\n"); + /* Now check for input */ + PQconsumeInput(conn); + while ((notify = PQnotifies(conn)) != NULL) + { + fprintf(stderr, + "ASYNC NOTIFY of '%s' received from backend pid %d\n", + notify->relname, notify->be_pid); + PQfreemem(notify); + nnotifies++; + } + } - /* close the connection to the database and cleanup */ - PQfinish(conn); + fprintf(stderr, "Done.\n"); - return 0; + /* close the connection to the database and cleanup */ + PQfinish(conn); + + return 0; } @@ -4488,7 +4506,7 @@ main(int argc, char **argv) /* * 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 <stdio.h> #include <stdlib.h> @@ -4520,125 +4542,178 @@ main(int argc, char **argv) static void exit_nicely(PGconn *conn) { - PQfinish(conn); - exit(1); + PQfinish(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) { - const char *conninfo; - PGconn *conn; - PGresult *res; - const char *paramValues[1]; - int i, - j; - int i_fnum, - t_fnum, - b_fnum; + 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 > 1) - conninfo = argv[1]; - else - conninfo = "dbname = postgres"; + /* + * 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 > 1) + conninfo = argv[1]; + else + conninfo = "dbname = postgres"; - /* Make a connection to the database */ - conn = PQconnectdb(conninfo); + /* 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); - } + /* 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 - * 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 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"; + /* 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 < 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"); - } + 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); + } - /* close the connection to the database and cleanup */ - PQfinish(conn); + show_binary_results(res); - return 0; + 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 *) &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); + } + + show_binary_results(res); + + PQclear(res); + + /* close the connection to the database and cleanup */ + PQfinish(conn); + + return 0; } diff --git a/src/test/examples/testlibpq3.c b/src/test/examples/testlibpq3.c index 918d142c7b..c7e4e09732 100644 --- a/src/test/examples/testlibpq3.c +++ b/src/test/examples/testlibpq3.c @@ -17,6 +17,10 @@ * 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 #include @@ -36,67 +40,20 @@ exit_nicely(PGconn *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; - /* - * 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 > 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 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"); @@ -141,6 +98,109 @@ main(int argc, char **argv) printf("\\%03o", bptr[j]); printf("\n\n"); } +} + +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 > 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 *) &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); + } + + show_binary_results(res); PQclear(res);