Final libpq patch? - Mailing list pgsql-hackers

From Bruce Momjian
Subject Final libpq patch?
Date
Msg-id 200410181816.i9IIGnq16187@candle.pha.pa.us
Whole thread Raw
In response to Re: Nearing final release?  (Abhijit Menon-Sen <ams@oryx.com>)
Responses Re: Final libpq patch?
List pgsql-hackers
Here is a mega-patch that merges in your four previous patches.  Is this
ready to be applied?  Making libpq changes during beta is risky so we
had better be careful.

I have also attached your pgtest.c file.

---------------------------------------------------------------------------

Abhijit Menon-Sen wrote:
> At 2004-10-16 18:41:05 -0400, tgl@sss.pgh.pa.us wrote:
> >
> > I think the cleanest solution is probably to add a state flag indicating
> > whether ParseComplete should generate a PGresult or not.
>
> Like the appended (incremental) patch?
>
> (I didn't think this would work, because I thought libpq would allow you
> to send multiple queries before trying to read results. But you said it
> didn't support that, so...)
>
> -- ams
>
> --- libpq-int.h.1~    2004-10-18 17:42:13.175759981 +0530
> +++ libpq-int.h    2004-10-18 17:43:40.105986570 +0530
> @@ -269,6 +269,8 @@
>                                   * nonblock sending semantics */
>      bool        ext_query;        /* was our last query sent with extended
>                                   * query protocol? */
> +    bool        sent_prepare;    /* was our last Parse message sent with
> +                                 * PQprepare? */
>      char        copy_is_binary; /* 1 = copy binary, 0 = copy text */
>      int            copy_already_done;        /* # bytes already returned in
>                                           * COPY OUT */
>
> --- fe-exec.c.2~    2004-10-18 17:47:55.540189274 +0530
> +++ fe-exec.c    2004-10-18 17:48:30.119038902 +0530
> @@ -686,6 +686,7 @@
>          goto sendFailed;
>
>      conn->ext_query = true;
> +    conn->sent_prepare = true;
>      if (pqFlush(conn) < 0)
>          goto sendFailed;
>      conn->asyncStatus = PGASYNC_BUSY;
>
> --- fe-protocol3.c.2~    2004-10-18 17:44:06.616198123 +0530
> +++ fe-protocol3.c    2004-10-18 17:46:34.431656569 +0530
> @@ -220,10 +220,13 @@
>                      conn->asyncStatus = PGASYNC_READY;
>                      break;
>                  case '1':        /* Parse Complete */
> -                    if (conn->result == NULL)
> -                        conn->result = PQmakeEmptyPGresult(conn,
> -                                                           PGRES_COMMAND_OK);
> -                    conn->asyncStatus = PGASYNC_READY;
> +                    if (conn->sent_prepare) {
> +                        if (!conn->result)
> +                            conn->result = PQmakeEmptyPGresult(conn,
> +                                                               PGRES_COMMAND_OK);
> +                        conn->asyncStatus = PGASYNC_READY;
> +                        conn->sent_prepare = false;
> +                    }
>                      break;
>                  case '2':        /* Bind Complete */
>                  case '3':        /* Close Complete */
>
> --- libpq-fe.h.2~    2004-10-18 17:55:40.632064120 +0530
> +++ libpq-fe.h    2004-10-18 17:56:26.501634328 +0530
> @@ -312,9 +312,9 @@
>                 int resultFormat);
>
>  /* Interface for multiple-result or asynchronous queries */
> -extern PGresult *PQsendPrepare(PGconn *conn, const char *stmtName,
> -                               const char *query, int nParams,
> -                               const Oid *paramTypes);
> +extern int PQsendPrepare(PGconn *conn, const char *stmtName,
> +                         const char *query, int nParams,
> +                         const Oid *paramTypes);
>  extern int    PQsendQuery(PGconn *conn, const char *query);
>  extern int PQsendQueryParams(PGconn *conn,
>                    const char *command,
>
> ---------------------------(end of broadcast)---------------------------
> TIP 5: Have you checked our extensive FAQ?
>
>                http://www.postgresql.org/docs/faqs/FAQ.html
>

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.165
diff -c -c -r1.165 libpq.sgml
*** doc/src/sgml/libpq.sgml    1 Oct 2004 17:34:17 -0000    1.165
--- doc/src/sgml/libpq.sgml    18 Oct 2004 18:13:09 -0000
***************
*** 1183,1194 ****
  </varlistentry>
  </variablelist>

! Presently, prepared statements for use with <function>PQexecPrepared</>
! must be set up by executing an SQL <command>PREPARE</> command,
! which is typically sent with <function>PQexec</> (though any of
! <application>libpq</>'s query-submission functions may be used).
! A lower-level interface for preparing statements may be offered in a
! future release.
  </para>

  <para>
--- 1183,1238 ----
  </varlistentry>
  </variablelist>

! Prepared statements for use with <function>PQexecPrepared</> can be
! created by executing an SQL <command>PREPARE</> statement (which is
! sent with <function>PQexec</>, or one of the other query-submission
! functions), or with <function>PQprepare</>. The latter does not
! require parameter types to be pre-specified.
! </para>
!
! <para>
! <variablelist>
! <varlistentry>
! <term><function>PQprepare</function><indexterm><primary>PQprepare</></></term>
! <listitem>
! <para>
!           Submits a request to create a prepared statement with the
!           given parameters, and waits for completion.
! <synopsis>
! PGresult *PQprepare(PGconn *conn,
!                     const char *stmtName,
!                     const char *query,
!                     int nParams,
!                     const Oid *paramTypes);
! </synopsis>
! </para>
!
! <para>
! <function>PQprepare</> creates a prepared statement for execution with
! <function>PQexecPrepared</>. Unlike <command>PREPARE</>, it allows the
! client to prepare a statement without pre-specifying the types of each
! parameter. This function is supported only in protocol 3.0 and later
! connections; it will fail when using protocol 2.0.
! </para>
!
! <para>
! The function creates a prepared statement named <parameter>stmtName</>
! (which may be <literal>""</>, to refer to the unnamed statement) from
! the <parameter>query</>. If any parameters are used, they are referred
! to in the query as <literal>$1</>, <literal>$2</>, etc.
!
! <parameter>nParams</> is the number of parameters for which types are
! pre-specified in the array <parameter>paramTypes[]</>. It may be zero,
! or up to the number of parameters used in the query. Each entry in the
! <parameter>paramTypes[]</> array should contain the OID of the type of
! the corresponding parameter. If <parameter>nParams</> is 0, the server
! assigns a data type to each parameter, as it would for untyped literal
! strings. Likewise, if any element in the type array is zero, its type
! is inferred.
! </para>
! </listitem>
! </varlistentry>
! </variablelist>
  </para>

  <para>
***************
*** 2353,2358 ****
--- 2397,2423 ----
  </varlistentry>

  <varlistentry>
+ <term><function>PQsendPrepare</><indexterm><primary>PQsendPrepare</></></term>
+ <listitem>
+ <para>
+         Sends a request to create a prepared statement with the given
+         parameters, without waiting for completion.
+ <synopsis>
+ int PQsendPrepare(PGconn *conn,
+                   const char *stmtName,
+                   const char *query,
+                   int nParams,
+                   const Oid *paramTypes);
+ </synopsis>
+
+         This is an asynchronous version of <function>PQprepare</>, and
+         its parameters are handled identically. It will not work on 2.0
+         protocol connections.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
  <term><function>PQgetResult</function><indexterm><primary>PQgetResult</></></term>
  <listitem>
  <para>
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.163
diff -c -c -r1.163 fe-exec.c
*** src/interfaces/libpq/fe-exec.c    16 Oct 2004 22:52:53 -0000    1.163
--- src/interfaces/libpq/fe-exec.c    18 Oct 2004 18:13:23 -0000
***************
*** 635,640 ****
--- 635,704 ----


  /*
+  * PQsendPrepare
+  *   Submit a Parse message, but don't wait for it to finish.
+  *
+  * Returns: 1 if successfully submitted
+  *          0 if error (conn->errorMessage is set)
+  */
+ int
+ PQsendPrepare(PGconn *conn,
+               const char *stmtName, const char *query,
+               int nParams, const Oid *paramTypes)
+ {
+     if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+     {
+         printfPQExpBuffer(&conn->errorMessage,
+                           libpq_gettext("function requires at least protocol version 3.0\n"));
+         return 0;
+     }
+
+     if (!PQsendQueryStart(conn))
+         return 0;
+
+     if (pqPutMsgStart('P', false, conn) < 0 ||
+         pqPuts(stmtName, conn) < 0 ||
+         pqPuts(query, conn) < 0)
+         goto sendFailed;
+
+     if (nParams > 0 && paramTypes)
+     {
+         int i;
+
+         if (pqPutInt(nParams, 2, conn) < 0)
+             goto sendFailed;
+         for (i = 0; i < nParams; i++)
+         {
+             if (pqPutInt(paramTypes[i], 4, conn) < 0)
+                 goto sendFailed;
+         }
+     }
+     else
+     {
+         if (pqPutInt(0, 2, conn) < 0)
+             goto sendFailed;
+     }
+     if (pqPutMsgEnd(conn) < 0)
+         goto sendFailed;
+
+     if (pqPutMsgStart('S', false, conn) < 0 ||
+         pqPutMsgEnd(conn) < 0)
+         goto sendFailed;
+
+     conn->ext_query = true;
+     conn->sent_prepare = true;
+     if (pqFlush(conn) < 0)
+         goto sendFailed;
+     conn->asyncStatus = PGASYNC_BUSY;
+     return 1;
+
+ sendFailed:
+     pqHandleSendFailure(conn);
+     return 0;
+ }
+
+
+ /*
   * PQsendQuery
   *     Submit a query, but don't wait for it to finish
   *
***************
*** 1145,1150 ****
--- 1209,1236 ----
      return PQexecFinish(conn);
  }

+
+ /*
+  * PQprepare
+  *    Creates a prepared statement by issuing a v3.0 parse message.
+  *
+  * Returns NULL if the query failed, and a new PGresult otherwise. The
+  * user is responsible for calling PQclient() on the result.
+  */
+
+ PGresult *
+ PQprepare(PGconn *conn,
+           const char *stmtName, const char *query,
+           int nParams, const Oid *paramTypes)
+ {
+     if (!PQexecStart(conn))
+         return NULL;
+     if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes))
+         return NULL;
+     return PQexecFinish(conn);
+ }
+
+
  /*
   * PQexecParams
   *        Like PQexec, but use protocol 3.0 so we can pass parameters
Index: src/interfaces/libpq/fe-protocol3.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v
retrieving revision 1.18
diff -c -c -r1.18 fe-protocol3.c
*** src/interfaces/libpq/fe-protocol3.c    16 Oct 2004 22:52:54 -0000    1.18
--- src/interfaces/libpq/fe-protocol3.c    18 Oct 2004 18:13:26 -0000
***************
*** 220,225 ****
--- 220,233 ----
                      conn->asyncStatus = PGASYNC_READY;
                      break;
                  case '1':        /* Parse Complete */
+                     if (conn->sent_prepare) {
+                         if (!conn->result)
+                             conn->result = PQmakeEmptyPGresult(conn,
+                                                                PGRES_COMMAND_OK);
+                         conn->asyncStatus = PGASYNC_READY;
+                         conn->sent_prepare = false;
+                     }
+                     break;
                  case '2':        /* Bind Complete */
                  case '3':        /* Close Complete */
                      /* Nothing to do for these message types */
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.111
diff -c -c -r1.111 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h    16 Oct 2004 22:52:55 -0000    1.111
--- src/interfaces/libpq/libpq-fe.h    18 Oct 2004 18:13:28 -0000
***************
*** 296,301 ****
--- 296,304 ----

  /* Simple synchronous query */
  extern PGresult *PQexec(PGconn *conn, const char *query);
+ extern PGresult *PQprepare(PGconn *conn, const char *stmtName,
+                            const char *query, int nParams,
+                            const Oid *paramTypes);
  extern PGresult *PQexecParams(PGconn *conn,
               const char *command,
               int nParams,
***************
*** 313,318 ****
--- 316,324 ----
                 int resultFormat);

  /* Interface for multiple-result or asynchronous queries */
+ extern int PQsendPrepare(PGconn *conn, const char *stmtName,
+                          const char *query, int nParams,
+                          const Oid *paramTypes);
  extern int    PQsendQuery(PGconn *conn, const char *query);
  extern int PQsendQueryParams(PGconn *conn,
                    const char *command,
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.94
diff -c -c -r1.94 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    16 Oct 2004 22:52:55 -0000    1.94
--- src/interfaces/libpq/libpq-int.h    18 Oct 2004 18:13:29 -0000
***************
*** 268,273 ****
--- 268,275 ----
                                   * nonblock sending semantics */
      bool        ext_query;        /* was our last query sent with extended
                                   * query protocol? */
+     bool        sent_prepare;    /* was our last Parse message sent with
+                                  * PQprepare? */
      char        copy_is_binary; /* 1 = copy binary, 0 = copy text */
      int            copy_already_done;        /* # bytes already returned in
                                           * COPY OUT */
/*
 * Test program for PQprepare/PQdescribe.
 * Abhijit Menon-Sen <ams@oryx.com>
 *
 * create table pqtest (a int, b text, c text);
 * insert into pqtest (a,b,c) values (1,'foo','bar');
 * insert into pqtest (a,b,c) values (1,'foo','baz');
 * insert into pqtest (a,b,c) values (2,'foo','barf');
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libpq-fe.h"


int main(int argc, char *argv[])
{
    int i, a, b, c;
    PGconn *conn;
    PGresult *res;

    char *query = "select * from pqtest where a=$1 and b=$2";
    const char *values[2];

    conn = PQconnectdb("");
    if (PQstatus(conn) != CONNECTION_OK) {
        fprintf(stderr, "Couldn't connect to the database: %s",
                PQerrorMessage(conn));
        PQfinish(conn);
        exit(-1);
    }

    res = PQprepare(conn, "prep", query, 0, NULL);
    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        fprintf(stderr, "PREPARE failed: %s", PQerrorMessage(conn));
        PQclear(res);
        PQfinish(conn);
        exit(-1);
    }

    PQclear(res);
    values[0] = "1";
    values[1] = "foo";
    res = PQexecPrepared(conn, "prep", 2, values, NULL, NULL, 0);
    if (PQresultStatus(res) != PGRES_TUPLES_OK) {
        fprintf(stderr, "EXECUTE failed: %s", PQerrorMessage(conn));
        PQclear(res);
        PQfinish(conn);
        exit(-1);
    }

    a = PQfnumber(res, "a");
    b = PQfnumber(res, "b");
    c = PQfnumber(res, "c");

    printf("Results:\n");
    for(i = 0; i < PQntuples(res); i++) {
        char *av = PQgetvalue(res, i, a);
        char *bv = PQgetvalue(res, i, b);
        char *cv = PQgetvalue(res, i, c);
        printf("  (a=%s)(b=%s)(c=%s)\n", av, bv, cv);
    }
    printf("%d rows\n", i);

    PQclear(res);
    PQfinish(conn);
    return 0;
}

pgsql-hackers by date:

Previous
From: Andrew Dunstan
Date:
Subject: confusing log messages on pg_ctl -w start
Next
From: "Magnus Hagander"
Date:
Subject: Re: [PATCHES] Open Items