Thread

  1. RE: [INTERFACES] Revised proposal for libpq and FE/BE protocol changes

    Peter Mount <peter@maidstone.gov.uk> — 1998-04-29T07:01:29Z

    That does exist but brings in the same problem as I'm having with JDBC - you
    can't guarantee that one thread/process will try to send a query while
    another thread/process is waiting for results.
    
    I'm keeping a keen eye on this proposal, because it would make my life a lot
    easier in making the JDBC driver thread safe, which is one of the main
    things I'm aiming for 6.4
    
    -----Original Message-----
    From: owner-pgsql-interfaces@hub.org
    [mailto:owner-pgsql-interfaces@hub.org]On Behalf Of
    watts@humbug.antnet.com
    Sent: Tuesday, April 28, 1998 9:53 PM
    To: Tom Lane
    Cc: pgsql-hackers@postgreSQL.org; pgsql-interfaces@postgreSQL.org;
    watts@humbug.antnet.com
    Subject: Re: [INTERFACES] Revised proposal for libpq and FE/BE protocol
    changes
    
    
    I suggest the application already has fork or fork/exec to
    implement an  asynchronous design. Does that also keep the
    socket out of the application's domain?
    
    Bob
    watts@humbug.antnet.com
    
    Received: from hub.org (hub.org [209.47.148.200])
    	by humbug.antnet.com (8.8.5/8.8.5) with ESMTP id LAA21503
    	for <watts@humbug.antnet.com>; Tue, 28 Apr 1998 11:28:48 -0500 (CDT)
    Received: from localhost (majordom@localhost) by hub.org (8.8.8/8.7.5) with
    SMTP id MAA01511; Tue, 28 Apr 1998 12:23:18 -0400 (EDT)
    Received: by hub.org (TLB v0.10a (1.23 tibbs 1997/01/09 00:29:32)); Tue, 28
    Apr 1998 12:23:16 -0400 (EDT)
    Received: (from majordom@localhost) by hub.org (8.8.8/8.7.5) id MAA01498 for
    pgsql-interfaces-outgoing; Tue, 28 Apr 1998 12:23:09 -0400 (EDT)
    Received: from sss.sss.pgh.pa.us (sss.pgh.pa.us [206.210.65.6]) by hub.org
    (8.8.8/8.7.5) with ESMTP id MAA01401; Tue, 28 Apr 1998 12:22:04 -0400 (EDT)
    Received: from sss.sss.pgh.pa.us (localhost [127.0.0.1])
    	by sss.sss.pgh.pa.us (8.8.5/8.8.5) with ESMTP id MAA07043;
    	Tue, 28 Apr 1998 12:21:56 -0400 (EDT)
    To: pgsql-hackers@postgreSQL.org, pgsql-interfaces@postgreSQL.org
    Subject: [INTERFACES] Revised proposal for libpq and FE/BE protocol changes
    Date: Tue, 28 Apr 1998 12:21:55 -0400
    Message-ID: <7040.893780515@sss.pgh.pa.us>
    From: Tom Lane <tgl@sss.pgh.pa.us>
    Sender: owner-pgsql-interfaces@hub.org
    Precedence: bulk
    
    Here is a revised proposal that takes into account the discussions
    of the last few days.  Any comments?
    
    
    I propose to revise libpq and modify the frontend/backend protocol
    to provide the following benefits:
     * Provide a clean way of reading multiple results from a single query
       string.  Among other things, this solves the problem of allowing a
       single query to return several result sets with different descriptors.
     * Allow a frontend to perform other work while awaiting the result of
       a query.
     * Add the ability to cancel queries in progress.
     * Eliminate the need for frontends to issue dummy queries in order
       to detect NOTIFY responses.
     * Eliminate the need for libpq to issue dummy queries internally
       to determine when a query is complete.
    
    We can't break existing code for this, so the behavior of PQexec()
    can't change.  Instead, I propose new functions to add to the API.
    Internally, PQexec will be reimplemented in terms of these new
    functions, but old applications shouldn't notice any difference.
    
    
    The new functions are:
    
    	bool PQsendQuery (PGconn *conn, const char *query);
    
    Submits a query without waiting for the result.  Returns TRUE if the
    query has been successfully dispatched, otherwise FALSE (in the FALSE
    case, an error message is left in conn->errorMessage).
    
    	PGresult* PQgetResult (PGconn *conn);
    
    Waits for input from the backend, and consumes input until (a) a result is
    available, (b) the current query is over, or (c) a copy in/out operation
    is detected.  NULL is returned if the query is over; in all other cases a
    suitable PGresult is returned (which the caller must eventually free).
    Note that no actual "wait" will occur if the necessary input has already
    been consumed; see below.
    
    	bool PQisBusy (PGconn *conn);
    
    Returns TRUE if a query operation is busy (that is, a call to PQgetResult
    would block waiting for more input).  Returns FALSE if PQgetResult would
    return immediately.
    
    	void PQconsumeInput (PGconn *conn);
    
    This can be called at any time to check for and process new input from
    the backend.  It returns no status indication, but after calling it
    the application can use PQisBusy() and/or PQnotifies() to see if a query
    was completed or a NOTIFY message arrived.  This function will never wait
    for more input to arrive.
    
    	int PQsocket (PGconn *conn);
    
    Returns the Unix file descriptor for the socket connection to the backend,
    or -1 if there is no open connection.  This is a violation of modularity,
    of course, but there is no alternative: an application that needs
    asynchronous execution needs to be able to use select() to wait for input
    from either the backend or any other input streams it may have.  To use
    select() the underlying socket must be made visible.
    
    	PGnotify *PQnotifies (PGconn *conn);
    
    This function doesn't change; we just observe that notifications may
    become available as a side effect of executing either PQgetResult() or
    PQconsumeInput(), not just PQexec().
    
    	void PQrequestCancel (PGconn *conn);
    
    Issues a cancel request if possible.  There is no direct way to tell whether
    this has any effect ... see discussion below.
    
    
    Discussion:
    
    An application can continue to use PQexec() as before, and notice
    very little difference in behavior.
    
    Applications that want to be able to handle multiple results from a
    single query should replace PQexec calls with logic like this:
    
    	// Submit the query
    	if (! PQsendQuery(conn, query))
    		reportTheError();
    	// Wait for and process result(s)
    	while ((result = PQgetResult(conn)) != NULL) {
    		switch (PQresultStatus(result)) {
    		... process result, for example:
    		case PGRES_COPY_IN:
    			// ... copy data here ...
    			if (PQendcopy(conn))
    				reportTheError();
    			break;
    		...
    		}
    		PQclear(result);
    	}
    	// When fall out of loop, we're done and ready for a new query
    
    Note that PQgetResult will always report errors by returning a PGresult
    with status PGRES_NONFATAL_ERROR or PGRES_FATAL_ERROR, not by returning
    NULL (since NULL implies non-error termination of the processing loop).
    
    PQexec() will be implemented as follows:
    
    	if (! PQsendQuery(conn, query))
    		return makeEmptyPGresult(conn, PGRES_FATAL_ERROR);
    	lastResult = NULL;
    	while ((result = PQgetResult(conn)) != NULL) {
    		PQclear(lastResult);
    		lastResult = result;
    	}
    	return lastResult;
    
    This maintains the current behavior that the last result of a series
    of commands is returned by PQexec.  (The old implementation is only
    capable of doing that correctly in a limited set of cases, but in the
    cases where it behaves usefully at all, that's how it behaves.)
    
    There is a small difference in behavior, which is that PQexec will now
    return a PGresult with status PGRES_FATAL_ERROR in cases where the old
    implementation would just have returned NULL (and set conn->errorMessage).
    However, any correctly coded application should handle this the same way.
    
    In the above examples, the frontend application is still synchronous: it
    blocks while waiting for the backend to reply to a query.  This is often
    undesirable, since the application may have other work to do, such as
    responding to user input.  Applications can now handle that by using
    PQisBusy and PQconsumeInput along with PQsendQuery and PQgetResult.
    
    The general idea is that the application's main loop will use select()
    to wait for input (from either the backend or its other input sources).
    When select() indicates that input is pending from the backend, the app
    will call PQconsumeInput, followed by checking PQisBusy and/or PQnotifies
    to see what has happened.  If PQisBusy returns FALSE then PQgetResult
    can safely be called to obtain and process a result without blocking.
    
    Note also that NOTIFY messages can arrive asynchronously from the backend.
    They can be detected *without issuing a query* by calling PQconsumeInput
    followed by PQnotifies.  I expect a lot of people will build "partially
    async" applications that detect notifies this way but still do all their
    queries through PQexec (or better, PQsendQuery followed by a synchronous
    PQgetResult loop).  This compromise allows notifies to be detected without
    wasting time by issuing null queries, yet the basic logic of issuing a
    series of queries remains simple.
    
    Finally, since the application can retain control while waiting for a
    query response, it becomes meaningful to try to cancel a query in progress.
    This is done by calling PQrequestCancel().  Note that PQrequestCancel()
    may not have any effect --- if there is no query in progress, or if the
    backend has already finished the query, then it *will* have no effect.
    The application must continue to follow the result-reading protocol after
    issuing a cancel request.  If the cancel is successful, its effect will be
    to cause the current query to fail and return an error message.
    
    
    PROTOCOL CHANGES:
    
    We should change the protocol version number to 2.0.
    It would be possible for the backend to continue to support 1.0 clients,
    if you think it's worth the trouble to do so.
    
    1. New message type:
    
    Command Done
    	Byte1('Z')
    
    The backend will emit this message at completion of processing of every
    command string, just before it resumes waiting for frontend input.
    This change eliminates libpq's current hack of issuing empty queries to
    see whether the backend is done.  Note that 'Z' must be emitted after
    *every* query or function invocation, no matter how it terminated.
    
    2. The RowDescription ('T') message is extended by adding a new value
    for each field.  Just after the type-size value, there will now be
    an int16 "atttypmod" value.  (Would someone provide text specifying
    exactly what this value means?)  libpq will store this value in
    a new "adtmod" field of PGresAttDesc structs.
    
    3. The "Start Copy In" response message is changed from 'D' to 'G',
    and the "Start Copy Out" response message is changed from 'B' to 'H'.
    These changes eliminate potential confusion with the data row messages,
    which also have message codes 'D' and 'B'.
    
    4. The frontend may request cancellation of the current query by sending
    a single byte of OOB (out-of-band) data.  The contents of the data byte
    are irrelevant, since the cancellation will be triggered by the associated
    signal and not by the data itself.  (But we should probably specify that
    the byte be zero, in case we later think of a reason to have different
    kinds of OOB messages.)  There is no specific reply to this message.
    If the backend does cancel a query, the query terminates with an ordinary
    error message indicating that the query was cancelled.
    
    
    			regards, tom lane