docs done Re: [HACKERS] LIBPQ patches ...
От | Alfred Perlstein |
---|---|
Тема | docs done Re: [HACKERS] LIBPQ patches ... |
Дата | |
Msg-id | 20000116160043.I508@fw.wintelcom.net обсуждение исходный текст |
Ответ на | Re: [HACKERS] LIBPQ patches ... (Tom Lane <tgl@sss.pgh.pa.us>) |
Ответы |
Re: [PATCHES] docs done Re: [HACKERS] LIBPQ patches ...
(Bruce Momjian <pgman@candle.pha.pa.us>)
Re: [PATCHES] docs done Re: [HACKERS] LIBPQ patches ... (Bruce Momjian <pgman@candle.pha.pa.us>) |
Список | pgsql-hackers |
* Tom Lane <tgl@sss.pgh.pa.us> [000109 08:18] wrote: > Don Baccus <dhogaza@pacifier.com> writes: > > At 05:27 PM 1/8/00 -0500, Tom Lane wrote: > >> I also object strongly to the lack of documentation. > > > ... I know there are some folks who aren't native-english speakers, so > > perhaps you don't want to require that the implementor of such patches > > provide the final documentation wording. But the information should > > be there and spelled out in a form that can be very easily moved to > > the docs. > > Oh, absolutely. Thomas, our master of the docs, has always had the > policy of "give me some words, I'll take care of formatting and > editing..." > > I was probably too harsh on Alfred last night, since in fact his code > was fairly well commented, and some minimal doco could have been > extracted from the routine headers. But on a change like this, I think > some paragraphs of coherent high-level explanation are needed: what it > does, when and why you'd use it. I didn't see that anywhere... Here's the revised patch, it includes sgml docs and changes to ensure that old style connections behave the way they are expected to: Index: doc/src/sgml/libpq.sgml =================================================================== RCS file: /home/pgcvs/pgsql/doc/src/sgml/libpq.sgml,v retrieving revision 1.25 diff -u -c -r1.25 libpq.sgml *** doc/src/sgml/libpq.sgml 2000/01/14 05:33:13 1.25 --- doc/src/sgml/libpq.sgml 2000/01/17 03:40:30 *************** *** 377,382 **** --- 377,386 ---- changed in the future. </para> <para> + These functions leave the socket in a non-blocking state as if + <function>PQsetnonblocking</function> had been called. + </para> + <para> These functions are not thread-safe. </para> </listitem> *************** *** 1168,1175 **** --- 1172,1229 ---- Applications that do not like these limitations can instead use the underlying functions that <function>PQexec</function>is built from: <function>PQsendQuery</function> and <function>PQgetResult</function>. + </para> + <para> + Older programs that used this functionality as well as + <function>PQputline</function> and <function>PQputnbytes</function> + could block waiting to send data to the backend, to + address that issue, the function <function>PQsetnonblocking</function> + was added. + </para> + <para> + Old applications can neglect to use <function>PQsetnonblocking</function> + and get the older potentially blocking behavior. Newer programs can use + <function>PQsetnonblocking</function> to achieve a completely non-blocking + connection to the backend. <itemizedlist> + <listitem> + <para> + <function>PQsetnonblocking</function> Sets the state of the connection + to non-blocking. + <synopsis> + int PQsetnonblocking(PGconn *conn) + </synopsis> + this function will ensure that calls to + <function>PQputline</function>, <function>PQputnbytes</function>, + <function>PQsendQuery</function> and <function>PQendcopy</function> + will not block but instead return an error if they need to be called + again. + </para> + <para> + When a database connection has been set to non-blocking mode and + <function>PQexec</function> is called, it will temporarily set the state + of the connection to blocking until the <function>PQexec</function> + completes. + </para> + <para> + More of libpq is expected to be made safe for + <function>PQsetnonblocking</function> functionality in the near future. + </para> + </listitem> + + <listitem> + <para> + <function>PQisnonblocking</function> + Returns the blocking status of the database connection. + <synopsis> + int PQisnonblocking(const PGconn *conn) + </synopsis> + Returns TRUE if the connection is set to non-blocking mode, + FALSE if blocking. + </para> + </listitem> + <listitem> <para> <function>PQsendQuery</function> *************** *** 1267,1286 **** <listitem> <para> <function>PQsocket</function> Obtain the file descriptor number for the backendconnection socket. ! A valid descriptor will be >= 0; a result of -1 indicates that no backend connection is currently open. <synopsis>int PQsocket(const PGconn *conn); </synopsis> <function>PQsocket</function> should be used to obtain the backendsocket descriptor in preparation for executing <function>select</function>(2). This allows an ! application to wait for either backend responses or other conditions. If the result of <function>select</function>(2) indicatesthat data can be read from the backend socket, then <function>PQconsumeInput</function> should be called to readthe data; after which, <function>PQisBusy</function>, <function>PQgetResult</function>, and/or <function>PQnotifies</function>can be used to process the response. </para> </listitem> --- 1321,1363 ---- <listitem> <para> + <function>PQflush</function> Attempt to flush any data queued to the backend, + returns 0 if successful (or if the send queue is empty) or EOF if it failed for + some reason. + <synopsis> + int PQflush(PGconn *conn); + </synopsis> + <function>PQflush</function> needs to be called on a non-blocking connection + before calling <function>select</function> to determine if a responce has + arrived. If 0 is returned it ensures that there is no data queued to the + backend that has not actually been sent. Only applications that have used + <function>PQsetnonblocking</function> have a need for this. + </para> + </listitem> + + <listitem> + <para> <function>PQsocket</function> Obtain the file descriptor number for the backend connection socket. ! A valid descriptor will be >= 0; a result of -1 indicates that no backend connection is currently open.<synopsis> int PQsocket(const PGconn *conn); </synopsis> <function>PQsocket</function> should be used to obtain thebackend socket descriptor in preparation for executing <function>select</function>(2). This allows an ! application using a blocking connection to wait for either backend responses or ! other conditions. If the result of <function>select</function>(2) indicates that data can be read from the backend socket,then <function>PQconsumeInput</function> should be called to read the data; after which, <function>PQisBusy</function>,<function>PQgetResult</function>, and/or <function>PQnotifies</function> can be used to processthe response. + </para> + <para> + Non-blocking connections (that have used <function>PQsetnonblocking</function>) + should not use <function>select</function> until <function>PQflush</function> + has returned 0 indicating that there is no buffered data waiting to be sent + to the backend. </para> </listitem> Index: src/interfaces/libpq/fe-connect.c =================================================================== RCS file: /home/pgcvs/pgsql/src/interfaces/libpq/fe-connect.c,v retrieving revision 1.111 diff -u -c -r1.111 fe-connect.c *** src/interfaces/libpq/fe-connect.c 2000/01/16 21:18:52 1.111 --- src/interfaces/libpq/fe-connect.c 2000/01/17 02:35:56 *************** *** 594,624 **** return 0; } - - /* ---------- - * connectMakeNonblocking - - * Make a connection non-blocking. - * Returns 1 if successful, 0 if not. - * ---------- - */ - static int - connectMakeNonblocking(PGconn *conn) - { - #ifndef WIN32 - if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) < 0) - #else - if (ioctlsocket(conn->sock, FIONBIO, &on) != 0) - #endif - { - printfPQExpBuffer(&conn->errorMessage, - "connectMakeNonblocking -- fcntl() failed: errno=%d\n%s\n", - errno, strerror(errno)); - return 0; - } - - return 1; - } - /* ---------- * connectNoDelay - * Sets the TCP_NODELAY socket option. --- 594,599 ---- *************** *** 789,795 **** * Ewan Mellor <eem21@cam.ac.uk>. * ---------- */ #if (!defined(WIN32) || defined(WIN32_NON_BLOCKING_CONNECTIONS))&& !defined(USE_SSL) ! if (!connectMakeNonblocking(conn)) goto connect_errReturn; #endif --- 764,770 ---- * Ewan Mellor <eem21@cam.ac.uk>. * ---------- */ #if (!defined(WIN32) || defined(WIN32_NON_BLOCKING_CONNECTIONS))&& !defined(USE_SSL) ! if (PQsetnonblocking(conn, TRUE) != 0) goto connect_errReturn; #endif *************** *** 898,904 **** /* This makes the connection non-blocking, for all those cases which forced us not to do it above.*/ #if (defined(WIN32) && !defined(WIN32_NON_BLOCKING_CONNECTIONS)) || defined(USE_SSL) ! if (!connectMakeNonblocking(conn)) goto connect_errReturn; #endif --- 873,879 ---- /* This makes the connection non-blocking, for all those cases which forced us not to do it above.*/ #if (defined(WIN32) && !defined(WIN32_NON_BLOCKING_CONNECTIONS)) || defined(USE_SSL) ! if (PQsetnonblocking(conn, TRUE) != 0) goto connect_errReturn; #endif *************** *** 1720,1725 **** --- 1695,1701 ---- conn->inBuffer = (char *) malloc(conn->inBufSize); conn->outBufSize = 8 * 1024; conn->outBuffer= (char *) malloc(conn->outBufSize); + conn->nonblocking = FALSE; initPQExpBuffer(&conn->errorMessage); initPQExpBuffer(&conn->workBuffer); if(conn->inBuffer == NULL || *************** *** 1830,1835 **** --- 1806,1812 ---- conn->lobjfuncs = NULL; conn->inStart = conn->inCursor = conn->inEnd = 0; conn->outCount =0; + conn->nonblocking = FALSE; } Index: src/interfaces/libpq/fe-exec.c =================================================================== RCS file: /home/pgcvs/pgsql/src/interfaces/libpq/fe-exec.c,v retrieving revision 1.86 diff -u -c -r1.86 fe-exec.c *** src/interfaces/libpq/fe-exec.c 1999/11/11 00:10:14 1.86 --- src/interfaces/libpq/fe-exec.c 2000/01/14 22:47:07 *************** *** 13,18 **** --- 13,19 ---- */ #include <errno.h> #include <ctype.h> + #include <fcntl.h> #include "postgres.h" #include "libpq-fe.h" *************** *** 24,30 **** #include <unistd.h> #endif - /* keep this in same order as ExecStatusType in libpq-fe.h */ const char *const pgresStatus[] = { "PGRES_EMPTY_QUERY", --- 25,30 ---- *************** *** 514,526 **** conn->curTuple = NULL; /* send the query to the backend; */ ! /* the frontend-backend protocol uses 'Q' to designate queries */ ! if (pqPutnchar("Q", 1, conn) || ! pqPuts(query, conn) || ! pqFlush(conn)) { ! handleSendFailure(conn); ! return 0; } /* OK, it's launched! */ --- 514,566 ---- conn->curTuple = NULL; /* send the query to the backend; */ ! ! /* ! * in order to guarantee that we don't send a partial query ! * where we would become out of sync with the backend and/or ! * block during a non-blocking connection we must first flush ! * the send buffer before sending more data ! * ! * an alternative is to implement 'queue reservations' where ! * we are able to roll up a transaction ! * (the 'Q' along with our query) and make sure we have ! * enough space for it all in the send buffer. ! */ ! if (pqIsnonblocking(conn)) { ! /* ! * the buffer must have emptied completely before we allow ! * a new query to be buffered ! */ ! if (pqFlush(conn)) ! return 0; ! /* 'Q' == queries */ ! /* XXX: if we fail here we really ought to not block */ ! if (pqPutnchar("Q", 1, conn) || ! pqPuts(query, conn)) ! { ! handleSendFailure(conn); ! return 0; ! } ! /* ! * give the data a push, ignore the return value as ! * ConsumeInput() will do any aditional flushing if needed ! */ ! (void) pqFlush(conn); ! } ! else ! { ! /* ! * the frontend-backend protocol uses 'Q' to ! * designate queries ! */ ! if (pqPutnchar("Q", 1, conn) || ! pqPuts(query, conn) || ! pqFlush(conn)) ! { ! handleSendFailure(conn); ! return 0; ! } } /* OK, it's launched! */ *************** *** 574,580 **** --- 614,630 ---- * we will NOT block waiting for more input. */ if (pqReadData(conn) < 0) + { + /* + * for non-blocking connections + * try to flush the send-queue otherwise we may never get a + * responce for something that may not have already been sent + * because it's in our write buffer! + */ + if (pqIsnonblocking(conn)) + (void) pqFlush(conn); return 0; + } /* Parsing of the data waits till later. */ return 1; } *************** *** 1088,1093 **** --- 1138,1153 ---- { PGresult *result; PGresult *lastResult; + bool savedblocking; + + /* + * we assume anyone calling PQexec wants blocking behaviour, + * we force the blocking status of the connection to blocking + * for the duration of this function and restore it on return + */ + savedblocking = pqIsnonblocking(conn); + if (PQsetnonblocking(conn, FALSE) == -1) + return NULL; /* * Silently discard any prior query result that application didn't *************** *** 1102,1115 **** PQclear(result); printfPQExpBuffer(&conn->errorMessage, "PQexec:you gotta get out of a COPY state yourself.\n"); ! return NULL; } PQclear(result); } /* OK to send the message */ if (!PQsendQuery(conn,query)) ! return NULL; /* * For backwards compatibility, return the last result if there are --- 1162,1176 ---- PQclear(result); printfPQExpBuffer(&conn->errorMessage, "PQexec:you gotta get out of a COPY state yourself.\n"); ! /* restore blocking status */ ! goto errout; } PQclear(result); } /* OK to send the message */ if (!PQsendQuery(conn,query)) ! goto errout; /* restore blocking status */ /* * For backwards compatibility, return the last resultif there are *************** *** 1142,1148 **** --- 1203,1217 ---- result->resultStatus == PGRES_COPY_OUT) break; } + + if (PQsetnonblocking(conn, savedblocking) == -1) + return NULL; return lastResult; + + errout: + if (PQsetnonblocking(conn, savedblocking) == -1) + return NULL; + return NULL; } *************** *** 1431,1438 **** "PQendcopy() -- I don't think there's a copy in progress.\n"); return 1; } ! (void) pqFlush(conn); /* make sure no data is waiting to be sent */ /* Return to active duty */ conn->asyncStatus= PGASYNC_BUSY; --- 1500,1516 ---- "PQendcopy() -- I don't think there's a copy in progress.\n"); return 1; } + + /* + * make sure no data is waiting to be sent, + * abort if we are non-blocking and the flush fails + */ + if (pqFlush(conn) && pqIsnonblocking(conn)) + return (1); ! /* non blocking connections may have to abort at this point. */ ! if (pqIsnonblocking(conn) && PQisBusy(conn)) ! return (1); /* Return to active duty */ conn->asyncStatus = PGASYNC_BUSY; *************** *** 2025,2028 **** --- 2103,2192 ---- return 1; else return 0; + } + + /* PQsetnonblocking: + sets the PGconn's database connection non-blocking if the arg is TRUE + or makes it non-blocking if the arg is FALSE, this will not protect + you from PQexec(), you'll only be safe when using the non-blocking + API + Needs to be called only on a connected database connection. + */ + + int + PQsetnonblocking(PGconn *conn, int arg) + { + int fcntlarg; + + arg = (arg == TRUE) ? 1 : 0; + /* early out if the socket is already in the state requested */ + if (arg == conn->nonblocking) + return (0); + + /* + * to guarantee constancy for flushing/query/result-polling behavior + * we need to flush the send queue at this point in order to guarantee + * proper behavior. + * this is ok because either they are making a transition + * _from_ or _to_ blocking mode, either way we can block them. + */ + /* if we are going from blocking to non-blocking flush here */ + if (!pqIsnonblocking(conn) && pqFlush(conn)) + return (-1); + + + #ifdef USE_SSL + if (conn->ssl) + { + printfPQExpBuffer(&conn->errorMessage, + "PQsetnonblocking() -- not supported when using SSL\n"); + return (-1); + } + #endif /* USE_SSL */ + + #ifndef WIN32 + fcntlarg = fcntl(conn->sock, F_GETFL, 0); + if (fcntlarg == -1) + return (-1); + + if ((arg == TRUE && + fcntl(conn->sock, F_SETFL, fcntlarg | O_NONBLOCK) == -1) || + (arg == FALSE && + fcntl(conn->sock, F_SETFL, fcntlarg & ~O_NONBLOCK) == -1)) + #else + fcntlarg = arg; + if (ioctlsocket(conn->sock, FIONBIO, &fcntlarg) != 0) + #endif + { + printfPQExpBuffer(&conn->errorMessage, + "PQsetblocking() -- unable to set nonblocking status to %s\n", + arg == TRUE ? "TRUE" : "FALSE"); + return (-1); + } + + conn->nonblocking = arg; + + /* if we are going from non-blocking to blocking flush here */ + if (pqIsnonblocking(conn) && pqFlush(conn)) + return (-1); + + return (0); + } + + /* return the blocking status of the database connection, TRUE == nonblocking, + FALSE == blocking + */ + int + PQisnonblocking(const PGconn *conn) + { + + return (pqIsnonblocking(conn)); + } + + /* try to force data out, really only useful for non-blocking users */ + int + PQflush(PGconn *conn) + { + + return (pqFlush(conn)); } Index: src/interfaces/libpq/fe-misc.c =================================================================== RCS file: /home/pgcvs/pgsql/src/interfaces/libpq/fe-misc.c,v retrieving revision 1.33 diff -u -c -r1.33 fe-misc.c *** src/interfaces/libpq/fe-misc.c 1999/11/30 03:08:19 1.33 --- src/interfaces/libpq/fe-misc.c 2000/01/12 03:12:14 *************** *** 86,91 **** --- 86,122 ---- { size_t avail = Max(conn->outBufSize - conn->outCount, 0); + /* + * if we are non-blocking and the send queue is too full to buffer this + * request then try to flush some and return an error + */ + if (pqIsnonblocking(conn) && nbytes > avail && pqFlush(conn)) + { + /* + * even if the flush failed we may still have written some + * data, recalculate the size of the send-queue relative + * to the amount we have to send, we may be able to queue it + * afterall even though it's not sent to the database it's + * ok, any routines that check the data coming from the + * database better call pqFlush() anyway. + */ + if (nbytes > Max(conn->outBufSize - conn->outCount, 0)) + { + printfPQExpBuffer(&conn->errorMessage, + "pqPutBytes -- pqFlush couldn't flush enough" + " data: space available: %d, space needed %d\n", + Max(conn->outBufSize - conn->outCount, 0), nbytes); + return EOF; + } + } + + /* + * is the amount of data to be sent is larger than the size of the + * output buffer then we must flush it to make more room. + * + * the code above will make sure the loop conditional is never + * true for non-blocking connections + */ while (nbytes > avail) { memcpy(conn->outBuffer + conn->outCount, s, avail); *************** *** 548,553 **** --- 579,592 ---- return EOF; } + /* + * don't try to send zero data, allows us to use this function + * without too much worry about overhead + */ + if (len == 0) + return (0); + + /* while there's still data to send */ while (len > 0) { /* Prevent being SIGPIPEd if backend has closedthe connection. */ *************** *** 556,561 **** --- 595,601 ---- #endif int sent; + #ifdef USE_SSL if (conn->ssl) sent = SSL_write(conn->ssl, ptr, len); *************** *** 585,590 **** --- 625,632 ---- case EWOULDBLOCK: break; #endif + case EINTR: + continue; case EPIPE: #ifdef ECONNRESET *************** *** 616,628 **** ptr += sent; len -= sent; } if (len > 0) { /*We didn't send it all, wait till we can send more */ - /* At first glance this looks as though it should block. I think - * that it will be OK though, as long as the socket is - * non-blocking. */ if (pqWait(FALSE, TRUE, conn)) return EOF; } --- 658,688 ---- ptr += sent; len -= sent; } + if (len > 0) { /* We didn't send it all, wait till we can send more */ + + /* + * if the socket is in non-blocking mode we may need + * to abort here + */ + #ifdef USE_SSL + /* can't do anything for our SSL users yet */ + if (conn->ssl == NULL) + { + #endif + if (pqIsnonblocking(conn)) + { + /* shift the contents of the buffer */ + memmove(conn->outBuffer, ptr, len); + conn->outCount = len; + return EOF; + } + #ifdef USE_SSL + } + #endif if (pqWait(FALSE, TRUE, conn)) return EOF; } Index: src/interfaces/libpq/libpq-fe.h =================================================================== RCS file: /home/pgcvs/pgsql/src/interfaces/libpq/libpq-fe.h,v retrieving revision 1.55 diff -u -c -r1.55 libpq-fe.h *** src/interfaces/libpq/libpq-fe.h 2000/01/15 05:37:21 1.55 --- src/interfaces/libpq/libpq-fe.h 2000/01/17 02:35:56 *************** *** 263,268 **** --- 263,275 ---- extern int PQputnbytes(PGconn *conn, const char *buffer, int nbytes); extern int PQendcopy(PGconn*conn); + /* Set blocking/nonblocking connection to the backend */ + extern int PQsetnonblocking(PGconn *conn, int arg); + extern int PQisnonblocking(const PGconn *conn); + + /* Force the write buffer to be written (or at least try) */ + extern int PQflush(PGconn *conn); + /* * "Fast path" interface --- not really recommended for application * use Index: src/interfaces/libpq/libpq-int.h =================================================================== RCS file: /home/pgcvs/pgsql/src/interfaces/libpq/libpq-int.h,v retrieving revision 1.16 diff -u -c -r1.16 libpq-int.h *** src/interfaces/libpq/libpq-int.h 2000/01/15 05:37:21 1.16 --- src/interfaces/libpq/libpq-int.h 2000/01/17 02:35:56 *************** *** 214,219 **** --- 214,222 ---- int inEnd; /* offset to first position after avail * data */ + int nonblocking; /* whether this connection is using a blocking + * socket to the backend or not */ + /* Buffer for data not yet sent to backend */ char *outBuffer; /* currently allocated buffer */ int outBufSize; /* allocated size of buffer */ *************** *** 299,303 **** --- 302,312 ---- #define strerror(A) (sys_errlist[(A)]) #endif /* sunos4 */ #endif /* !strerror */ + + /* + * this is so that we can check is a connection is non-blocking internally + * without the overhead of a function call + */ + #define pqIsnonblocking(conn) (conn->nonblocking) #endif /* LIBPQ_INT_H */
В списке pgsql-hackers по дате отправления:
Предыдущее
От: Bruce MomjianДата:
Сообщение: Re: [HACKERS] I think we need an explicit parsetree node for CAST
Следующее
От: Xun ChengДата:
Сообщение: Re: [HACKERS] hybrid hash, cont. of development suggestion needed