提交 edbd5139 编写于 作者: B Bruce Momjian

What I've done:

1. Rewritten libpq to allow asynchronous clients.

2. Implemented client side of cancel protocol in library,
   and patched psql.c to send a cancel request upon SIGINT.  The
   backend doesn't notice it yet :-(

3. Implemented 'Z' protocol message addition and renaming of
   copy in/out start messages.  These are implemented conditionally,
   ie, the client protocol version is checked; so the code should
   still work with 1.0 clients.

4. Revised protocol and libpq sgml documents (don't have an SGML
   compiler, though, so there may be some markup glitches here).


What remains to be done:

1. Implement addition of atttypmod field to RowDescriptor messages.
   The client-side code is there but ifdef'd out.  I have no idea
   what to change on the backend side.  The field should be sent
   only if protocol >= 2.0, of course.

2. Implement backend response to cancel requests received as OOB
   messages.  (This prolly need not be conditional on protocol
   version; just do it if you get SIGURG.)

3. Update libpq.3.  (I'm hoping this can be generated mechanically
   from libpq.sgml... if not, will do it by hand.)  Is there any
   other doco to fix?

4. Update non-libpq interfaces as necessary.  I patched libpgtcl
   so that it would compile, but haven't tested it.  Dunno what
   needs to be done with the other interfaces.

Have at it!

Tom Lane
上级 2e12331d
此差异已折叠。
......@@ -4,14 +4,15 @@
<FirstName>Phil</FirstName>
<Surname>Thompson</Surname>
</Author>
<Date>1998-02-02</Date>
<Date>1998-05-04</Date>
</DocInfo>
<Title>Frontend/Backend Protocol</Title>
<Para>
<Note>
<Para>
Written by <ULink url="mailto:phil@river-bank.demon.co.uk">Phil Thompson</ULink>
Written by <ULink url="mailto:phil@river-bank.demon.co.uk">Phil Thompson</ULink>.
Updates for protocol 2.0 by <ULink url="mailto:tgl@sss.pgh.pa.us">Tom Lane</ULink>.
</Para>
</Note>
......@@ -24,7 +25,7 @@ a way as to still allow connections from earlier versions of frontends, but
this document does not cover the protocol used by those earlier versions.
<Para>
This document describes the initial version-numbered protocol, designated v1.0.
This document describes version 2.0 of the protocol.
Higher level features built on this protocol (for example, how <FileName>libpq</FileName> passes
certain environment variables after the connection is established)
are covered elsewhere.
......@@ -47,7 +48,9 @@ and responds to the frontend accordingly.
<Para>
The frontend then sends any required authentication information. Once the
postmaster validates this it responds to the frontend that it is authenticated
and hands over to a backend.
and hands over the connection to a backend. The backend then sends a message
indicating successful startup (normal case) or failure (for example, an
invalid database name).
<Para>
Subsequent communications are query and result packets exchanged between the
......@@ -60,7 +63,7 @@ closes the connection without waiting for a response for the backend.
<Para>
Packets are sent as a data stream. The first byte determines what should be
expected in the rest of the packet. The exception is packets send from a
expected in the rest of the packet. The exception is packets sent from a
frontend to the postmaster, which comprise a packet length then the packet
itself. The difference is historical.
......@@ -70,15 +73,22 @@ itself. The difference is historical.
<Para>
This section describes the message flow. There are four different types of
flows depending on the state of the connection:
authentication, query, function call, and termination.
startup, query, function call, and termination.
There are also special provisions for notification responses and command
cancellation, which can occur at any time after the startup phase.
<Sect2>
<Title>Authentication</Title>
<Title>Startup</Title>
<Para>
Startup is divided into an authentication phase and a backend startup phase.
<Para>
The frontend sends a StartupPacket. The postmaster uses this and the contents
of the pg_hba.conf(5) file to determine what authentication method the frontend
must use. The postmaster then responds with one of the following messages:
Initially, the frontend sends a StartupPacket. The postmaster uses this info
and the contents of the pg_hba.conf(5) file to determine what authentication
method the frontend must use. The postmaster then responds with one of the
following messages:
<Para>
<VariableList>
......@@ -162,13 +172,65 @@ must use. The postmaster then responds with one of the following messages:
If the frontend does not support the authentication method requested by the
postmaster, then it should immediately close the connection.
<Para>
After sending AuthenticationOk, the postmaster attempts to launch a backend
process. Since this might fail, or the backend might encounter a failure
during startup, the frontend must wait for the backend to acknowledge
successful startup. The frontend should send no messages at this point.
The possible messages from the backend during this phase are:
<Para>
<VariableList>
<VarListEntry>
<Term>
ReadyForQuery
</Term>
<ListItem>
<Para>
Backend startup is successful. The frontend may now issue
query or function call messages.
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
ErrorResponse
</Term>
<ListItem>
<Para>
Backend startup failed. The connection is closed after
sending this message.
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
NoticeResponse
</Term>
<ListItem>
<Para>
A warning message has been issued. The frontend should
display the message but continue listening for ReadyForQuery
or ErrorResponse.
</Para>
</ListItem>
</VarListEntry>
</VariableList>
</Para>
<Sect2>
<Title>Query</Title>
<Para>
The frontend sends a Query message to the backend. The response sent by the
backend depends on the contents of the query. The possible responses are as
follows.
A Query cycle is initiated by the frontend sending a Query message to the
backend. The backend then sends one or more response messages depending
on the contents of the query command string, and finally a ReadyForQuery
response message. ReadyForQuery informs the frontend that it may safely
send a new query or function call.
<Para>
The possible response messages from the backend are:
<Para>
<VariableList>
......@@ -178,7 +240,7 @@ follows.
</Term>
<ListItem>
<Para>
The query completed normally.
An SQL command completed normally.
</Para>
</ListItem>
</VarListEntry>
......@@ -240,7 +302,7 @@ follows.
<Para>
For a fetch(l) or select(l) command, the backend sends a
RowDescription message. This is then followed by an AsciiRow
or BinaryRow message (depending on if a binary cursor was
or BinaryRow message (depending on whether a binary cursor was
specified) for each row being returned to the frontend.
Finally, the backend sends a CompletedResponse message with a
tag of "SELECT".
......@@ -253,7 +315,8 @@ follows.
</Term>
<ListItem>
<Para>
The query was empty.
An empty query string was recognized. (The need to specially
distinguish this case is historical.)
</Para>
</ListItem>
</VarListEntry>
......@@ -269,26 +332,28 @@ follows.
</VarListEntry>
<VarListEntry>
<Term>
NoticeResponse
ReadyForQuery
</Term>
<ListItem>
<Para>
A warning message has been issued in relation to the query.
Notices are in addition to other responses, ie. the backend
will send another response message immediately afterwards.
Processing of the query string is complete. A separate
message is sent to indicate this because the query string
may contain multiple SQL commands. (CompletedResponse marks
the end of processing one SQL command, not the whole string.)
ReadyForQuery will always be sent, whether processing
terminates successfully or with an error.
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
NotificationResponse
NoticeResponse
</Term>
<ListItem>
<Para>
A notify(l) command has been executed for a relation for
which a previous listen(l) command was executed. Notifications
are in addition to other responses, ie. the backend will send
another response message immediately afterwards.
A warning message has been issued in relation to the query.
Notices are in addition to other responses, ie. the backend
will continue processing the command.
</Para>
</ListItem>
</VarListEntry>
......@@ -297,15 +362,23 @@ follows.
<Para>
A frontend must be prepared to accept ErrorResponse and NoticeResponse
messages whenever it is expecting any other type of message.
messages whenever it is expecting any other type of message. Also,
if it issues any listen(l) commands then it must be prepared to accept
NotificationResponse messages at any time; see below.
<Sect2>
<Title>Function Call</Title>
<Para>
The frontend sends a FunctionCall message to the backend. The response sent by
the backend depends on the result of the function call. The possible responses
are as follows.
A Function Call cycle is initiated by the frontend sending a FunctionCall
message to the backend. The backend then sends one or more response messages
depending on the results of the function call, and finally a ReadyForQuery
response message. ReadyForQuery informs the frontend that it may safely send
a new query or function call.
<Para>
The possible response messages from the backend are:
<Para>
<VariableList>
......@@ -340,15 +413,27 @@ are as follows.
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
ReadyForQuery
</Term>
<ListItem>
<Para>
Processing of the function call is complete.
ReadyForQuery will always be sent, whether processing
terminates successfully or with an error.
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
NoticeResponse
</Term>
<ListItem>
<Para>
A warning message has been issued in relation to the function
call. Notices are in addition to other responses, ie. the
backend will send another response message immediately
afterwards.
call.
Notices are in addition to other responses, ie. the backend
will continue processing the command.
</Para>
</ListItem>
</VarListEntry>
......@@ -357,7 +442,58 @@ are as follows.
<Para>
A frontend must be prepared to accept ErrorResponse and NoticeResponse
messages whenever it is expecting any other type of message.
messages whenever it is expecting any other type of message. Also,
if it issues any listen(l) commands then it must be prepared to accept
NotificationResponse messages at any time; see below.
<Sect2>
<Title>Notification Responses</Title>
<Para>
If a frontend issues a listen(l) command, then the backend will send a
NotificationResponse message (not to be confused with NoticeResponse!)
whenever a notify(l) command is executed for the same relation name.
<Para>
Notification responses are permitted at any point in the protocol (after
startup), except within another backend message. Thus, the frontend
must be prepared to recognize a NotificationResponse message whenever it is
expecting any message. Indeed, it should be able to handle
NotificationResponse messages even when it is not engaged in a query.
<Para>
<VariableList>
<VarListEntry>
<Term>
NotificationResponse
</Term>
<ListItem>
<Para>
A notify(l) command has been executed for a relation for
which a previous listen(l) command was executed. Notifications
may be sent at any time.
</Para>
</ListItem>
</VarListEntry>
</VariableList>
</Para>
<Sect2>
<Title>Cancelling Requests in Progress</Title>
<Para>
During the processing of a query, the frontend may request cancellation of the
query by sending a single byte of OOB (out-of-band) data. The contents of the
data byte should be zero (although the backend does not currently check this).
If the cancellation is effective, it results in the current command being
terminated with an error message. Note that the backend makes no specific
reply to the cancel request itself. If the cancel request is ineffective
(say, because it arrived after processing was complete) then it will have
no visible effect at all. Thus, the frontend must continue with its normal
processing of query cycle responses after issuing a cancel request.
<Sect2>
<Title>Termination</Title>
......@@ -409,7 +545,7 @@ This section describes the base data types used in messages.
<Para>
A conventional C '\0' terminated string with no length
limitation. A frontend should always read the full string
even though it may have to discard characters if it's buffers
even though it may have to discard characters if its buffers
aren't big enough.
<Note>
<Para>
......@@ -458,8 +594,9 @@ AsciiRow (B)
</Term>
<ListItem>
<Para>
Identifies the message, in the context in which it is sent (see
CopyInResponse), as an <Acronym>ASCII</Acronym> row.
Identifies the message as an <Acronym>ASCII</Acronym> data row.
(A prior RowDescription message defines the number of
fields in the row and their data types.)
</Para>
</ListItem>
</VarListEntry>
......@@ -704,8 +841,9 @@ BinaryRow (B)
</Term>
<ListItem>
<Para>
Identifies the message, in the context in which it is sent (see
CopyOutResponse), as a binary row.
Identifies the message as a binary data row.
(A prior RowDescription message defines the number of
fields in the row and their data types.)
</Para>
</ListItem>
</VarListEntry>
......@@ -814,12 +952,12 @@ CopyInResponse (B)
<VariableList>
<VarListEntry>
<Term>
Byte1('D')
Byte1('G')
</Term>
<ListItem>
<Para>
Identifies the message, in the context in which it is sent (see
AsciiRow), as a copy in started response.
Identifies the message as a Start Copy In response.
The frontend must now send a CopyDataRows message.
</Para>
</ListItem>
</VarListEntry>
......@@ -839,12 +977,12 @@ CopyOutResponse (B)
<VariableList>
<VarListEntry>
<Term>
Byte1('B')
Byte1('H')
</Term>
<ListItem>
<Para>
Identifies the message, in the context in which it is sent (see
BinaryRow), as a copy out started response.
Identifies the message as a Start Copy Out response.
This message will be followed by a CopyDataRows message.
</Para>
</ListItem>
</VarListEntry>
......@@ -903,7 +1041,7 @@ EmptyQueryResponse (B)
</Term>
<ListItem>
<Para>
Identifies the message as an empty query response.
Identifies the message as a response to an empty query string.
</Para>
</ListItem>
</VarListEntry>
......@@ -954,6 +1092,31 @@ EncryptedPasswordPacket (F)
</VariableList>
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
ReadyForQuery (B)
</Term>
<ListItem>
<Para>
<VariableList>
<VarListEntry>
<Term>
Byte1('Z')
</Term>
<ListItem>
<Para>
Identifies the message type. ReadyForQuery is sent
whenever the backend is ready for a new query cycle.
</Para>
</ListItem>
</VarListEntry>
</VariableList>
</Para>
</ListItem>
</VarListEntry>
......@@ -1099,7 +1262,7 @@ FunctionResultResponse (B)
</Term>
<ListItem>
<Para>
Specifies that an actual result was returned.
Specifies that a nonempty result was returned.
</Para>
</ListItem>
</VarListEntry>
......@@ -1167,7 +1330,7 @@ FunctionVoidResponse (B)
</Term>
<ListItem>
<Para>
Specifies that no actual result was returned.
Specifies that an empty result was returned.
</Para>
</ListItem>
</VarListEntry>
......@@ -1269,7 +1432,7 @@ Query (F)
</Term>
<ListItem>
<Para>
Identifies the message as query.
Identifies the message as a query.
</Para>
</ListItem>
</VarListEntry>
......@@ -1279,7 +1442,7 @@ Query (F)
</Term>
<ListItem>
<Para>
The query itself.
The query string itself.
</Para>
</ListItem>
</VarListEntry>
......@@ -1348,6 +1511,16 @@ RowDescription (B)
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
Int16
</Term>
<ListItem>
<Para>
Specifies the type modifier.
</Para>
</ListItem>
</VarListEntry>
</VariableList>
</Para>
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.31 1998/04/27 04:05:08 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.32 1998/05/06 23:49:52 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -20,8 +20,7 @@
* end of commit),
* 2.a If the process is the same as the backend process that issued
* notification (we are notifying something that we are listening),
* signal the corresponding frontend over the comm channel using the
* out-of-band channel.
* signal the corresponding frontend over the comm channel.
* 2.b For all other listening processes, we send kill(2) to wake up
* the listening backend.
* 3. Upon receiving a kill(2) signal from another backend process notifying
......@@ -30,7 +29,7 @@
* 3.a We are sleeping, wake up and signal our frontend.
* 3.b We are in middle of another transaction, wait until the end of
* of the current transaction and signal our frontend.
* 4. Each frontend receives this notification and prcesses accordingly.
* 4. Each frontend receives this notification and processes accordingly.
*
* -- jw, 12/28/93
*
......@@ -547,12 +546,6 @@ Async_UnlistenOnExit(int code, /* from exitpg */
* Results:
* XXX
*
* Side effects:
*
* We make use of the out-of-band channel to transmit the
* notification to the front end. The actual data transfer takes
* place at the front end's request.
*
* --------------------------------------------------------------
*/
GlobalMemory notifyContext = NULL;
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.17 1998/02/26 04:36:24 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.18 1998/05/06 23:49:59 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -15,7 +15,8 @@
* INTERFACE ROUTINES
* BeginCommand - prepare destination for tuples of the given type
* EndCommand - tell destination that no more tuples will arrive
* NullCommand - tell dest that the last of a query sequence was processed
* NullCommand - tell dest that an empty query string was recognized
* ReadyForQuery - tell dest that we are ready for a new query
*
* NOTES
* These routines do the appropriate work before and after
......@@ -115,16 +116,10 @@ EndCommand(char *commandTag, CommandDest dest)
sprintf(buf, "%s%s", commandTag, CommandInfo);
CommandInfo[0] = 0;
pq_putstr(buf);
pq_flush();
break;
case Local:
case Debug:
break;
case CopyEnd:
pq_putnchar("Z", 1);
pq_flush();
break;
case None:
default:
break;
......@@ -139,28 +134,37 @@ EndCommand(char *commandTag, CommandDest dest)
*
* COPY rel FROM stdin
*
* NOTE: the message code letters are changed at protocol version 2.0
* to eliminate possible confusion with data tuple messages.
*/
void
SendCopyBegin(void)
{
pq_putnchar("B", 1);
/* pq_putint(0, 4); */
pq_flush();
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
pq_putnchar("H", 1); /* new way */
else
pq_putnchar("B", 1); /* old way */
}
void
ReceiveCopyBegin(void)
{
pq_putnchar("D", 1);
/* pq_putint(0, 4); */
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
pq_putnchar("G", 1); /* new way */
else
pq_putnchar("D", 1); /* old way */
/* We *must* flush here to ensure FE knows it can send. */
pq_flush();
}
/* ----------------
* NullCommand - tell dest that the last of a query sequence was processed
* NullCommand - tell dest that an empty query string was recognized
*
* Necessary to implement the hacky FE/BE interface to handle
* multiple-return queries.
* In FE/BE protocol version 1.0, this hack is necessary to support
* libpq's crufty way of determining whether a multiple-command
* query string is done. In protocol 2.0 it's probably not really
* necessary to distinguish empty queries anymore, but we still do it
* for backwards compatibility with 1.0.
* ----------------
*/
void
......@@ -168,46 +172,46 @@ NullCommand(CommandDest dest)
{
switch (dest)
{
case RemoteInternal:
case Remote:
case RemoteInternal:
case Remote:
{
#if 0
/*
* Do any asynchronous notification. If front end wants
* to poll, it can send null queries to call this
* function.
*/
PQNotifyList *nPtr;
MemoryContext orig;
if (notifyContext == NULL)
{
notifyContext = CreateGlobalMemory("notify");
}
orig = MemoryContextSwitchTo((MemoryContext) notifyContext);
for (nPtr = PQnotifies();
nPtr != NULL;
nPtr = (PQNotifyList *) SLGetSucc(&nPtr->Node))
{
pq_putnchar("A", 1);
pq_putint(0, sizeof(int4));
pq_putstr(nPtr->relname);
pq_putint(nPtr->be_pid, sizeof(nPtr->be_pid));
PQremoveNotify(nPtr);
}
pq_flush();
PQcleanNotify();/* garbage collect */
MemoryContextSwitchTo(orig);
#endif
/* ----------------
* tell the fe that the last of the queries has finished
* tell the fe that we saw an empty query string
* ----------------
*/
/* pq_putnchar("I", 1); */
pq_putstr("I");
/* pq_putint(0, 4); */
}
break;
case Local:
case Debug:
case None:
default:
break;
}
}
/* ----------------
* ReadyForQuery - tell dest that we are ready for a new query
*
* The ReadyForQuery message is sent in protocol versions 2.0 and up
* so that the FE can tell when we are done processing a query string.
*
* Note that by flushing the stdio buffer here, we can avoid doing it
* most other places and thus reduce the number of separate packets sent.
* ----------------
*/
void
ReadyForQuery(CommandDest dest)
{
switch (dest)
{
case RemoteInternal:
case Remote:
{
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
pq_putnchar("Z", 1);
/* Flush output at end of cycle in any case. */
pq_flush();
}
break;
......@@ -264,7 +268,6 @@ BeginCommand(char *pname,
* send fe info on tuples we're about to send
* ----------------
*/
pq_flush();
pq_putnchar("P", 1);/* new portal.. */
pq_putstr(pname); /* portal name */
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.16 1998/04/26 04:07:22 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.17 1998/05/06 23:50:10 momjian Exp $
*
* NOTES
* This cruft is the server side of PQfn.
......@@ -113,7 +113,6 @@ SendFunctionResult(Oid fid, /* function id */
}
pq_putnchar("0", 1);
pq_flush();
}
/*
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.67 1998/02/26 04:36:31 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.68 1998/05/06 23:50:19 momjian Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
......@@ -1302,7 +1302,7 @@ PostgresMain(int argc, char *argv[])
if (IsUnderPostmaster == false)
{
puts("\nPOSTGRES backend interactive interface");
puts("$Revision: 1.67 $ $Date: 1998/02/26 04:36:31 $");
puts("$Revision: 1.68 $ $Date: 1998/05/06 23:50:19 $");
}
/* ----------------
......@@ -1316,6 +1316,12 @@ PostgresMain(int argc, char *argv[])
for (;;)
{
/* ----------------
* (0) tell the frontend we're ready for a new query.
* ----------------
*/
ReadyForQuery(Remote);
/* ----------------
* (1) read a command.
* ----------------
......@@ -1391,8 +1397,8 @@ PostgresMain(int argc, char *argv[])
* ----------------
*/
case 'X':
IsEmptyQuery = true;
pq_close();
exitpg(0);
break;
default:
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.139 1998/05/04 02:02:01 momjian Exp $
* $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.140 1998/05/06 23:50:23 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -283,6 +283,38 @@ PSQLexec(PsqlSettings *pset, char *query)
return NULL;
}
/*
* Code to support command cancellation.
* If interactive, we enable a SIGINT signal catcher that sends
* a cancel request to the backend.
* Note that sending the cancel directly from the signal handler
* is safe only because the cancel is sent as an OOB message.
* If it were inline data, then we'd risk inserting it into the
* middle of a normal data message by doing this.
* (It's probably not too cool to write on stderr, for that matter...
* but for debugging purposes we'll risk that.)
*/
static PGconn * cancelConn = NULL; /* connection to try cancel on */
static void
handle_sigint (SIGNAL_ARGS)
{
if (cancelConn == NULL)
exit(1); /* accept signal if no connection */
/* Try to send cancel request */
if (PQrequestCancel(cancelConn))
{
fprintf(stderr, "\nCANCEL request sent\n");
}
else
{
fprintf(stderr, "\nCannot send cancel request:\n%s\n",
PQerrorMessage(cancelConn));
}
}
/*
* listAllDbs
*
......@@ -1099,8 +1131,7 @@ SendQuery(bool *success_p, PsqlSettings *pset, const char *query,
exit(2); /* we are out'ta here */
}
/* check for asynchronous returns */
notify = PQnotifies(pset->db);
if (notify)
while ((notify = PQnotifies(pset->db)) != NULL)
{
fprintf(stderr,
"ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
......@@ -1416,6 +1447,7 @@ do_connect(const char *new_dbname,
}
else
{
cancelConn = pset->db; /* redirect sigint's loving attentions */
PQfinish(olddb);
free(pset->prompt);
pset->prompt = malloc(strlen(PQdb(pset->db)) + 10);
......@@ -2462,11 +2494,18 @@ main(int argc, char **argv)
settings.opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
settings.opt.pager = 1;
if (!isatty(0) || !isatty(1))
{
/* Noninteractive defaults */
settings.notty = 1;
#ifdef USE_READLINE
}
else
{
/* Interactive defaults */
pqsignal(SIGINT, handle_sigint); /* control-C => cancel */
#ifdef USE_READLINE
settings.useReadline = 1;
#endif
}
#ifdef PSQL_ALWAYS_GET_PASSWORDS
settings.getPassword = 1;
#else
......@@ -2580,6 +2619,9 @@ main(int argc, char **argv)
PQfinish(settings.db);
exit(1);
}
cancelConn = settings.db; /* enable SIGINT to send cancel */
if (listDatabases)
{
exit(listAllDbs(&settings));
......
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pqcomm.h,v 1.24 1998/03/02 05:42:15 scrappy Exp $
* $Id: pqcomm.h,v 1.25 1998/05/06 23:50:32 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -66,7 +66,7 @@ typedef union SockAddr
/* The earliest and latest frontend/backend protocol version supported. */
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(0,0)
#define PG_PROTOCOL_LATEST PG_PROTOCOL(1,0)
#define PG_PROTOCOL_LATEST PG_PROTOCOL(2,0)
/*
* All packets sent to the postmaster start with the length. This is omitted
......
......@@ -26,7 +26,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: dest.h,v 1.13 1998/02/26 04:43:39 momjian Exp $
* $Id: dest.h,v 1.14 1998/05/06 23:50:49 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -46,10 +46,6 @@ typedef enum
Debug, /* results go to debugging output */
Local, /* results go in local portal buffer */
Remote, /* results sent to frontend process */
CopyBegin, /* results sent to frontend process but
* are strings */
CopyEnd, /* results sent to frontend process but
* are strings */
RemoteInternal, /* results sent to frontend process in
* internal (binary) form */
SPI /* results sent to SPI manager */
......@@ -70,6 +66,7 @@ extern void EndCommand(char *commandTag, CommandDest dest);
extern void SendCopyBegin(void);
extern void ReceiveCopyBegin(void);
extern void NullCommand(CommandDest dest);
extern void ReadyForQuery(CommandDest dest);
extern void
BeginCommand(char *pname, int operation, TupleDesc attinfo,
bool isIntoRel, bool isIntoPortal, char *tag,
......
......@@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.8 1998/03/15 08:03:00 scrappy Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.9 1998/05/06 23:51:00 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -48,7 +48,7 @@ int PgInputProc(DRIVER_INPUT_PROTO)
{
Pg_ConnectionId *connid;
PGconn *conn;
int c;
char c;
int avail;
connid = (Pg_ConnectionId *)cData;
......@@ -64,13 +64,24 @@ int PgInputProc(DRIVER_INPUT_PROTO)
return PgEndCopy(connid, errorCodePtr);
}
/* Try to load any newly arrived data */
errno = 0;
if (pqReadData(conn) < 0) {
*errorCodePtr = errno ? errno : EIO;
return -1;
}
/* Move data from libpq's buffer to tcl's */
conn->inCursor = conn->inStart;
avail = bufSize;
while (avail > 0 &&
(c = pqGetc(conn->Pfin, conn->Pfdebug)) != EOF) {
/* fprintf(stderr, "%d: got char %c\n", bufSize-avail, c); */
pqGetc(&c, conn) == 0) {
*buf++ = c;
--avail;
if (c == '\n' && bufSize-avail > 3) {
if (c == '\n' && bufSize-avail >= 3) {
if ((bufSize-avail == 3 || buf[-4] == '\n') &&
buf[-3] == '\\' && buf[-2] == '.') {
avail += 3;
......@@ -79,6 +90,8 @@ int PgInputProc(DRIVER_INPUT_PROTO)
}
}
}
/* Accept the data permanently */
conn->inStart = conn->inCursor;
/* fprintf(stderr, "returning %d chars\n", bufSize - avail); */
return bufSize - avail;
}
......@@ -100,16 +113,15 @@ int PgOutputProc(DRIVER_OUTPUT_PROTO)
return -1;
}
/*
fprintf(stderr, "PgOutputProc called: bufSize=%d: atend:%d <", bufSize,
strncmp(buf, "\\.\n", 3));
fwrite(buf, 1, bufSize, stderr);
fputs(">\n", stderr);
*/
fwrite(buf, 1, bufSize, conn->Pfout);
if (bufSize > 2 && strncmp(&buf[bufSize-3], "\\.\n", 3) == 0) {
/* fprintf(stderr,"checking closure\n"); */
fflush(conn->Pfout);
errno = 0;
if (pqPutnchar(buf, bufSize, conn)) {
*errorCodePtr = errno ? errno : EIO;
return -1;
}
if (bufSize >= 3 && strncmp(&buf[bufSize-3], "\\.\n", 3) == 0) {
(void) pqFlush(conn);
if (PgEndCopy(connid, errorCodePtr) == -1)
return -1;
}
......@@ -156,10 +168,10 @@ PgSetConnectionId(Tcl_Interp *interp, PGconn *conn)
for (i = 0; i < RES_START; i++) connid->results[i] = NULL;
Tcl_InitHashTable(&connid->notify_hash, TCL_STRING_KEYS);
sprintf(connid->id, "pgsql%d", fileno(conn->Pfout));
sprintf(connid->id, "pgsql%d", PQsocket(conn));
#if TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 5
conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, conn->Pfin, conn->Pfout, (ClientData)connid);
conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, NULL, NULL, (ClientData)connid);
#else
conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, (ClientData)connid,
TCL_READABLE | TCL_WRITABLE);
......
......@@ -7,7 +7,7 @@
#
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Attic/Makefile.in,v 1.16 1998/04/27 14:55:46 scrappy Exp $
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Attic/Makefile.in,v 1.17 1998/05/06 23:51:06 momjian Exp $
#
#-------------------------------------------------------------------------
......@@ -25,14 +25,13 @@ ifdef KRBVERS
CFLAGS+= $(KRBFLAGS)
endif
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-lobj.o \
dllist.o pqsignal.o pqcomprim.o
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
dllist.o pqsignal.o
# Shared library stuff
shlib :=
install-shlib-dep :=
ifeq ($(PORTNAME), linux)
LINUX_ELF=@LINUX_ELF@
ifdef LINUX_ELF
install-shlib-dep := install-shlib
shlib := libpq.so.$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION)
......@@ -84,9 +83,6 @@ fe-lobj.o: $(SRCDIR)/backend/fmgr.h
dllist.c: $(SRCDIR)/backend/lib/dllist.c
-ln -s $(SRCDIR)/backend/lib/dllist.c .
pqcomprim.c: $(SRCDIR)/backend/libpq/pqcomprim.c
-ln -s $(SRCDIR)/backend/libpq/pqcomprim.c .
# The following rules cause dependencies in the backend directory to
# get made if they don't exist, but don't cause them to get remade if they
# are out of date.
......@@ -183,7 +179,7 @@ depend dep:
.PHONY: clean
clean:
rm -f libpq.a $(shlib) $(OBJS) c.h dllist.c pqcomprim.c libpq.so
rm -f libpq.a $(shlib) $(OBJS) c.h dllist.c libpq.so
ifeq (depend,$(wildcard depend))
include depend
......
此差异已折叠。
此差异已折叠。
......@@ -5,177 +5,496 @@
*
* DESCRIPTION
* miscellaneous useful functions
* these routines are analogous to the ones in libpq/pqcomm.c
*
* The communication routines here are analogous to the ones in
* backend/libpq/pqcomm.c and backend/libpq/pqcomprim.c, but operate
* in the considerably different environment of the frontend libpq.
* In particular, we work with a bare nonblock-mode socket, rather than
* a stdio stream, so that we can avoid unwanted blocking of the application.
*
* XXX: MOVE DEBUG PRINTOUT TO HIGHER LEVEL. As is, block and restart
* will cause repeat printouts.
*
* We must speak the same transmitted data representations as the backend
* routines. Note that this module supports *only* network byte order
* for transmitted ints, whereas the backend modules (as of this writing)
* still handle either network or little-endian byte order.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.10 1998/02/26 04:45:09 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.11 1998/05/06 23:51:14 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#if !defined(NO_UNISTD_H)
#include <unistd.h>
#endif
#include <sys/types.h> /* for fd_set stuff */
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include "postgres.h"
#include "libpq-fe.h"
/* --------------------------------------------------------------------- */
/* pqGetc:
get a character from stream f
get a character from the connection
if debug is set, also echo the character fetched
All these routines return 0 on success, EOF on error.
Note that for the Get routines, EOF only means there is not enough
data in the buffer, not that there is necessarily a hard error.
*/
int
pqGetc(FILE *fin, FILE *debug)
pqGetc(char *result, PGconn *conn)
{
int c;
if (conn->inCursor >= conn->inEnd)
return EOF;
c = getc(fin);
*result = conn->inBuffer[conn->inCursor++];
if (debug && c != EOF)
fprintf(debug, "From backend> %c\n", c);
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "From backend> %c\n", *result);
return c;
return 0;
}
/* --------------------------------------------------------------------- */
/* pqPutnchar:
send a string of exactly len length into stream f
/* pqPutBytes: local routine to write N bytes to the connection,
with buffering
*/
static int
pqPutBytes(const char *s, int nbytes, PGconn *conn)
{
int avail = conn->outBufSize - conn->outCount;
while (nbytes > avail)
{
memcpy(conn->outBuffer + conn->outCount, s, avail);
conn->outCount += avail;
s += avail;
nbytes -= avail;
if (pqFlush(conn))
return EOF;
avail = conn->outBufSize;
}
returns 1 if there was an error, 0 otherwise.
memcpy(conn->outBuffer + conn->outCount, s, nbytes);
conn->outCount += nbytes;
return 0;
}
/* --------------------------------------------------------------------- */
/* pqGets:
get a null-terminated string from the connection,
and store it in a buffer of size maxlen bytes.
If the incoming string is >= maxlen bytes, all of it is read,
but the excess characters are silently discarded.
*/
int
pqPutnchar(const char *s, int len, FILE *f, FILE *debug)
pqGets(char *s, int maxlen, PGconn *conn)
{
/* Copy conn data to locals for faster search loop */
char *inBuffer = conn->inBuffer;
int inCursor = conn->inCursor;
int inEnd = conn->inEnd;
int slen;
while (inCursor < inEnd && inBuffer[inCursor])
inCursor++;
if (inCursor >= inEnd)
return EOF;
slen = inCursor - conn->inCursor;
if (slen < maxlen)
strcpy(s, inBuffer + conn->inCursor);
else
{
strncpy(s, inBuffer + conn->inCursor, maxlen-1);
s[maxlen-1] = '\0';
}
conn->inCursor = ++inCursor;
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "From backend> \"%s\"\n", s);
return 0;
}
/* --------------------------------------------------------------------- */
int
pqPuts(const char *s, PGconn *conn)
{
if (debug)
fprintf(debug, "To backend> %s\n", s);
if (pqPutBytes(s, strlen(s)+1, conn))
return EOF;
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend> %s\n", s);
return (pqPutNBytes(s, len, f) == EOF ? 1 : 0);
return 0;
}
/* --------------------------------------------------------------------- */
/* pqGetnchar:
get a string of exactly len bytes in buffer s (which must be 1 byte
longer) from stream f and terminate it with a '\0'.
longer) and terminate it with a '\0'.
*/
int
pqGetnchar(char *s, int len, FILE *f, FILE *debug)
pqGetnchar(char *s, int len, PGconn *conn)
{
int status;
if (len < 0 || len > conn->inEnd - conn->inCursor)
return EOF;
memcpy(s, conn->inBuffer + conn->inCursor, len);
s[len] = '\0';
status = pqGetNBytes(s, len, f);
conn->inCursor += len;
if (debug)
fprintf(debug, "From backend (%d)> %s\n", len, s);
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "From backend (%d)> %s\n", len, s);
return (status == EOF ? 1 : 0);
return 0;
}
/* --------------------------------------------------------------------- */
/* pqGets:
get a string of up to length len from stream f
/* pqPutnchar:
send a string of exactly len bytes
The buffer should have a terminating null, but it's not sent.
*/
int
pqGets(char *s, int len, FILE *f, FILE *debug)
pqPutnchar(const char *s, int len, PGconn *conn)
{
int status;
status = pqGetString(s, len, f);
if (pqPutBytes(s, len, conn))
return EOF;
if (debug)
fprintf(debug, "From backend> \"%s\"\n", s);
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend> %s\n", s);
return (status == EOF ? 1 : 0);
return 0;
}
/* --------------------------------------------------------------------- */
/* pgPutInt
send an integer of 2 or 4 bytes to the file stream, compensate
for host endianness.
returns 0 if successful, 1 otherwise
/* pgGetInt
read a 2 or 4 byte integer and convert from network byte order
to local byte order
*/
int
pqPutInt(const int integer, int bytes, FILE *f, FILE *debug)
pqGetInt(int *result, int bytes, PGconn *conn)
{
int retval = 0;
uint16 tmp2;
uint32 tmp4;
switch (bytes)
{
case 2:
retval = pqPutShort(integer, f);
if (conn->inCursor + 2 > conn->inEnd)
return EOF;
memcpy(&tmp2, conn->inBuffer + conn->inCursor, 2);
conn->inCursor += 2;
*result = (int) ntohs(tmp2);
break;
case 4:
retval = pqPutLong(integer, f);
if (conn->inCursor + 4 > conn->inEnd)
return EOF;
memcpy(&tmp4, conn->inBuffer + conn->inCursor, 4);
conn->inCursor += 4;
*result = (int) ntohl(tmp4);
break;
default:
fprintf(stderr, "** int size %d not supported\n", bytes);
retval = 1;
return EOF;
}
if (debug)
fprintf(debug, "To backend (%d#)> %d\n", bytes, integer);
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "From backend (#%d)> %d\n", bytes, *result);
return retval;
return 0;
}
/* --------------------------------------------------------------------- */
/* pgGetInt
read a 2 or 4 byte integer from the stream and swab it around
to compensate for different endianness
returns 0 if successful
/* pgPutInt
send an integer of 2 or 4 bytes, converting from host byte order
to network byte order.
*/
int
pqGetInt(int *result, int bytes, FILE *f, FILE *debug)
pqPutInt(int value, int bytes, PGconn *conn)
{
int retval = 0;
uint16 tmp2;
uint32 tmp4;
switch (bytes)
{
case 2:
retval = pqGetShort(result, f);
tmp2 = htons((uint16) value);
if (pqPutBytes((const char*) &tmp2, 2, conn))
return EOF;
break;
case 4:
retval = pqGetLong(result, f);
tmp4 = htonl((uint32) value);
if (pqPutBytes((const char*) &tmp4, 4, conn))
return EOF;
break;
default:
fprintf(stderr, "** int size %d not supported\n", bytes);
retval = 1;
return EOF;
}
if (debug)
fprintf(debug, "From backend (#%d)> %d\n", bytes, *result);
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend (%d#)> %d\n", bytes, value);
return retval;
return 0;
}
/* --------------------------------------------------------------------- */
/* pqReadReady: is select() saying the file is ready to read?
*/
static int
pqReadReady(PGconn *conn)
{
fd_set input_mask;
struct timeval timeout;
if (conn->sock < 0)
return 0;
FD_ZERO(&input_mask);
FD_SET(conn->sock, &input_mask);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if (select(conn->sock+1, &input_mask, (fd_set *) NULL, (fd_set *) NULL,
&timeout) < 0)
{
sprintf(conn->errorMessage,
"pqReadReady() -- select() failed: errno=%d\n%s\n",
errno, strerror(errno));
return 0;
}
return FD_ISSET(conn->sock, &input_mask);
}
/* --------------------------------------------------------------------- */
/* pqReadData: read more data, if any is available
* Possible return values:
* 1: successfully loaded at least one more byte
* 0: no data is presently available, but no error detected
* -1: error detected (including EOF = connection closure);
* conn->errorMessage set
* NOTE: callers must not assume that pointers or indexes into conn->inBuffer
* remain valid across this call!
*/
int
pqPuts(const char *s, FILE *f, FILE *debug)
pqReadData(PGconn *conn)
{
if (pqPutString(s, f) == EOF)
return 1;
int nread;
if (conn->sock < 0)
{
strcpy(conn->errorMessage, "pqReadData() -- connection not open\n");
return -1;
}
fflush(f);
/* Left-justify any data in the buffer to make room */
if (conn->inStart < conn->inEnd)
{
memmove(conn->inBuffer, conn->inBuffer + conn->inStart,
conn->inEnd - conn->inStart);
conn->inEnd -= conn->inStart;
conn->inCursor -= conn->inStart;
conn->inStart = 0;
}
else
{
conn->inStart = conn->inCursor = conn->inEnd = 0;
}
/* If the buffer is fairly full, enlarge it.
* We need to be able to enlarge the buffer in case a single message
* exceeds the initial buffer size. We enlarge before filling the
* buffer entirely so as to avoid asking the kernel for a partial packet.
* The magic constant here should be at least one TCP packet.
*/
if (conn->inBufSize - conn->inEnd < 2000)
{
int newSize = conn->inBufSize * 2;
char * newBuf = (char *) realloc(conn->inBuffer, newSize);
if (newBuf)
{
conn->inBuffer = newBuf;
conn->inBufSize = newSize;
}
}
if (debug)
fprintf(debug, "To backend> %s\n", s);
/* OK, try to read some data */
tryAgain:
nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
conn->inBufSize - conn->inEnd, 0);
if (nread < 0)
{
if (errno == EINTR)
goto tryAgain;
sprintf(conn->errorMessage,
"pqReadData() -- read() failed: errno=%d\n%s\n",
errno, strerror(errno));
return -1;
}
if (nread > 0)
{
conn->inEnd += nread;
return 1;
}
return 0;
/* A return value of 0 could mean just that no data is now available,
* or it could mean EOF --- that is, the server has closed the connection.
* Since we have the socket in nonblock mode, the only way to tell the
* difference is to see if select() is saying that the file is ready.
* Grumble. Fortunately, we don't expect this path to be taken much,
* since in normal practice we should not be trying to read data unless
* the file selected for reading already.
*/
if (! pqReadReady(conn))
return 0; /* definitely no data available */
/* Still not sure that it's EOF,
* because some data could have just arrived.
*/
tryAgain2:
nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
conn->inBufSize - conn->inEnd, 0);
if (nread < 0)
{
if (errno == EINTR)
goto tryAgain2;
sprintf(conn->errorMessage,
"pqReadData() -- read() failed: errno=%d\n%s\n",
errno, strerror(errno));
return -1;
}
if (nread > 0)
{
conn->inEnd += nread;
return 1;
}
/* OK, we are getting a zero read even though select() says ready.
* This means the connection has been closed. Cope.
*/
sprintf(conn->errorMessage,
"pqReadData() -- backend closed the channel unexpectedly.\n"
"\tThis probably means the backend terminated abnormally"
" before or while processing the request.\n");
conn->status = CONNECTION_BAD; /* No more connection to
* backend */
close(conn->sock);
conn->sock = -1;
return -1;
}
/* --------------------------------------------------------------------- */
void
pqFlush(FILE *f, FILE *debug)
/* pqFlush: send any data waiting in the output buffer
*/
int
pqFlush(PGconn *conn)
{
if (f)
fflush(f);
char * ptr = conn->outBuffer;
int len = conn->outCount;
if (conn->sock < 0)
{
strcpy(conn->errorMessage, "pqFlush() -- connection not open\n");
return EOF;
}
if (debug)
fflush(debug);
while (len > 0)
{
int sent = send(conn->sock, ptr, len, 0);
if (sent < 0)
{
/* Anything except EAGAIN or EWOULDBLOCK is trouble */
switch (errno)
{
#ifdef EAGAIN
case EAGAIN:
break;
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
case EWOULDBLOCK:
break;
#endif
default:
sprintf(conn->errorMessage,
"pqFlush() -- couldn't send data: errno=%d\n%s\n",
errno, strerror(errno));
return EOF;
}
}
else
{
ptr += sent;
len -= sent;
}
if (len > 0)
{
/* We didn't send it all, wait till we can send more */
if (pqWait(FALSE, TRUE, conn))
return EOF;
}
}
conn->outCount = 0;
if (conn->Pfdebug)
fflush(conn->Pfdebug);
return 0;
}
/* --------------------------------------------------------------------- */
/* pqWait: wait until we can read or write the connection socket
*/
int
pqWait(int forRead, int forWrite, PGconn *conn)
{
fd_set input_mask;
fd_set output_mask;
if (conn->sock < 0)
{
strcpy(conn->errorMessage, "pqWait() -- connection not open\n");
return EOF;
}
/* loop in case select returns EINTR */
for (;;) {
FD_ZERO(&input_mask);
FD_ZERO(&output_mask);
if (forRead)
FD_SET(conn->sock, &input_mask);
if (forWrite)
FD_SET(conn->sock, &output_mask);
if (select(conn->sock+1, &input_mask, &output_mask, (fd_set *) NULL,
(struct timeval *) NULL) < 0)
{
if (errno == EINTR)
continue;
sprintf(conn->errorMessage,
"pqWait() -- select() failed: errno=%d\n%s\n",
errno, strerror(errno));
return EOF;
}
/* On nonerror return, assume we're done */
break;
}
return 0;
}
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-fe.h,v 1.28 1998/03/20 04:02:57 momjian Exp $
* $Id: libpq-fe.h,v 1.29 1998/05/06 23:51:16 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -28,6 +28,8 @@ extern "C"
#include "libpq/pqcomm.h"
#include "lib/dllist.h"
/* Application-visible enum types */
typedef enum
{
CONNECTION_OK,
......@@ -41,14 +43,13 @@ extern "C"
/* anything was executed properly by the backend */
PGRES_TUPLES_OK, /* a query command that returns tuples */
/* was executed properly by the backend, PGresult */
/* contains the resulttuples */
PGRES_COPY_OUT,
PGRES_COPY_IN,
/* contains the result tuples */
PGRES_COPY_OUT, /* Copy Out data transfer in progress */
PGRES_COPY_IN, /* Copy In data transfer in progress */
PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from
* the backend */
PGRES_NONFATAL_ERROR,
PGRES_FATAL_ERROR
} ExecStatusType;
/* string descriptions of the ExecStatusTypes */
......@@ -63,29 +64,21 @@ extern "C"
#define COMMAND_LENGTH 20
#define REMARK_LENGTH 80
#define PORTAL_NAME_LENGTH 16
#define CMDSTATUS_LEN 40
/* ----------------
* PQArgBlock --
* Information (pointer to array of this structure) required
* for the PQfn() call.
* ----------------
/* PGresult and the subsidiary types PGresAttDesc, PGresAttValue
* represent the result of a query (or more precisely, of a single SQL
* command --- a query string given to PQexec can contain multiple commands).
* Note we assume that a single command can return at most one tuple group,
* hence there is no need for multiple descriptor sets.
*/
typedef struct
{
int len;
int isint;
union
{
int *ptr; /* can't use void (dec compiler barfs) */
int integer;
} u;
} PQArgBlock;
typedef struct pgresAttDesc
{
char *name; /* type name */
Oid adtid; /* type id */
short adtsize; /* type size */
short adtmod; /* type-specific modifier info */
} PGresAttDesc;
/* use char* for Attribute values,
......@@ -102,6 +95,25 @@ extern "C"
char *value; /* actual value */
} PGresAttValue;
struct pg_conn; /* forward reference */
typedef struct pg_result
{
int ntups;
int numAttributes;
PGresAttDesc *attDescs;
PGresAttValue **tuples; /* each PGresTuple is an array of
* PGresAttValue's */
int tupArrSize; /* size of tuples array allocated */
ExecStatusType resultStatus;
char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the
* last insert query */
int binary; /* binary tuple values if binary == 1,
* otherwise ASCII */
struct pg_conn *conn; /* connection we did the query on */
} PGresult;
/* PGnotify represents the occurrence of a NOTIFY message */
typedef struct pgNotify
{
char relname[NAMEDATALEN]; /* name of relation
......@@ -109,6 +121,18 @@ extern "C"
int be_pid; /* process id of backend */
} PGnotify;
/* PGAsyncStatusType is private to libpq, really shouldn't be seen by users */
typedef enum
{
PGASYNC_IDLE, /* nothing's happening, dude */
PGASYNC_BUSY, /* query in progress */
PGASYNC_READY, /* result ready for PQgetResult */
PGASYNC_COPY_IN, /* Copy In data transfer in progress */
PGASYNC_COPY_OUT /* Copy Out data transfer in progress */
} PGAsyncStatusType;
/* large-object-access data ... allocated only if large-object code is used.
* Really shouldn't be visible to users */
typedef struct pgLobjfuncs
{
Oid fn_lo_open; /* OID of backend function lo_open */
......@@ -122,54 +146,62 @@ extern "C"
Oid fn_lo_write;/* OID of backend function LOwrite */
} PGlobjfuncs;
/* PGconn encapsulates a connection to the backend */
/* PGconn encapsulates a connection to the backend.
* XXX contents of this struct really shouldn't be visible to applications
*/
typedef struct pg_conn
{
/* Saved values of connection options */
char *pghost; /* the machine on which the server is
* running */
char *pgport; /* the server's communication port */
char *pgtty; /* tty on which the backend messages is
* displayed */
char *pgport; /* the communication port with the backend */
* displayed (NOT ACTUALLY USED???) */
char *pgoptions; /* options to start the backend with */
char *dbName; /* database name */
ConnStatusType status;
char errorMessage[ERROR_MSG_LENGTH];
/* pipes for be/fe communication */
FILE *Pfin;
FILE *Pfout;
char *pguser; /* Postgres username and password, if any */
char *pgpass;
/* Optional file to write trace info to */
FILE *Pfdebug;
int sock; /* The socket */
/* Status indicators */
ConnStatusType status;
PGAsyncStatusType asyncStatus;
Dllist *notifyList; /* Notify msgs not yet handed to application */
/* Connection data */
int sock; /* Unix FD for socket, -1 if not connected */
SockAddr laddr; /* Local address */
SockAddr raddr; /* Remote address */
char salt[2];
int asyncNotifyWaiting;
Dllist *notifyList;
char *pguser; /* Postgres username of user who is
* connected */
char *pgpass;
PGlobjfuncs *lobjfuncs; /* Backend function OID's for large object
* access */
} PGconn;
#define CMDSTATUS_LEN 40
/* PGresult encapsulates the result of a query */
/* unlike the old libpq, we assume that queries only return in one group */
typedef struct pg_result
{
int ntups;
int numAttributes;
PGresAttDesc *attDescs;
PGresAttValue **tuples; /* each PGresTuple is an array of
* PGresAttValue's */
int tupArrSize; /* size of tuples array allocated */
ExecStatusType resultStatus;
char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the
* last insert query */
int binary; /* binary tuple values if binary == 1,
* otherwise ASCII */
PGconn *conn;
} PGresult;
/* Miscellaneous stuff */
char salt[2]; /* password salt received from backend */
PGlobjfuncs *lobjfuncs; /* private state for large-object access fns */
/* Buffer for data received from backend and not yet processed */
char *inBuffer; /* currently allocated buffer */
int inBufSize; /* allocated size of buffer */
int inStart; /* offset to first unconsumed data in buffer */
int inCursor; /* next byte to tentatively consume */
int inEnd; /* offset to first position after avail data */
/* Buffer for data not yet sent to backend */
char *outBuffer; /* currently allocated buffer */
int outBufSize; /* allocated size of buffer */
int outCount; /* number of chars waiting in buffer */
/* Status for asynchronous result construction */
PGresult *result; /* result being constructed */
PGresAttValue *curTuple; /* tuple currently being read */
/* Message space. Placed last for code-size reasons.
* errorMessage is the message last returned to the application.
* When asyncStatus=READY, asyncErrorMessage is the pending message
* that will be put in errorMessage by PQgetResult. */
char errorMessage[ERROR_MSG_LENGTH];
char asyncErrorMessage[ERROR_MSG_LENGTH];
} PGconn;
typedef char pqbool;
......@@ -179,7 +211,9 @@ extern "C"
* defined. Pqbool, on the other hand, is unlikely to be used.
*/
struct _PQprintOpt
/* Print options for PQprint() */
typedef struct _PQprintOpt
{
pqbool header; /* print output field headings and row
* count */
......@@ -193,15 +227,28 @@ extern "C"
char *caption; /* HTML <caption> */
char **fieldName; /* null terminated array of repalcement
* field names */
};
} PQprintOpt;
typedef struct _PQprintOpt PQprintOpt;
/* ----------------
* PQArgBlock -- structure for PQfn() arguments
* ----------------
*/
typedef struct
{
int len;
int isint;
union
{
int *ptr; /* can't use void (dec compiler barfs) */
int integer;
} u;
} PQArgBlock;
/* ----------------
* Structure for the conninfo parameter definitions of PQconnectdb()
* ----------------
*/
struct _PQconninfoOption
typedef struct _PQconninfoOption
{
char *keyword; /* The keyword of the option */
char *environ; /* Fallback environment variable name */
......@@ -215,9 +262,7 @@ extern "C"
/* "D" Debug options - don't */
/* create a field by default */
int dispsize; /* Field size in characters for dialog */
};
typedef struct _PQconninfoOption PQconninfoOption;
} PQconninfoOption;
/* === in fe-connect.c === */
/* make a new client connection to the backend */
......@@ -235,6 +280,7 @@ extern "C"
*/
extern void PQreset(PGconn *conn);
/* Accessor functions for PGconn objects */
extern char *PQdb(PGconn *conn);
extern char *PQuser(PGconn *conn);
extern char *PQhost(PGconn *conn);
......@@ -243,14 +289,38 @@ extern "C"
extern char *PQtty(PGconn *conn);
extern ConnStatusType PQstatus(PGconn *conn);
extern char *PQerrorMessage(PGconn *conn);
extern int PQsocket(PGconn *conn);
/* Enable/disable tracing */
extern void PQtrace(PGconn *conn, FILE *debug_port);
extern void PQuntrace(PGconn *conn);
/* === in fe-exec.c === */
/* Simple synchronous query */
extern PGresult *PQexec(PGconn *conn, const char *query);
extern PGnotify *PQnotifies(PGconn *conn);
/* Interface for multiple-result or asynchronous queries */
extern int PQsendQuery(PGconn *conn, const char *query);
extern PGresult *PQgetResult(PGconn *conn);
/* Routines for managing an asychronous query */
extern int PQisBusy(PGconn *conn);
extern void PQconsumeInput(PGconn *conn);
extern int PQrequestCancel(PGconn *conn);
/* Routines for copy in/out */
extern int PQgetline(PGconn *conn, char *string, int length);
extern int PQendcopy(PGconn *conn);
extern void PQputline(PGconn *conn, const char *string);
extern int PQendcopy(PGconn *conn);
/* Not really meant for application use: */
extern PGresult *PQfn(PGconn *conn,
int fnid,
int *result_buf,
int *result_len,
int result_is_int,
PQArgBlock *args,
int nargs);
extern void PQclearAsyncResult(PGconn *conn);
/* Accessor functions for PGresult objects */
extern ExecStatusType PQresultStatus(PGresult *res);
extern int PQntuples(PGresult *res);
extern int PQnfields(PGresult *res);
......@@ -258,84 +328,84 @@ extern "C"
extern int PQfnumber(PGresult *res, const char *field_name);
extern Oid PQftype(PGresult *res, int field_num);
extern short PQfsize(PGresult *res, int field_num);
extern short PQfmod(PGresult *res, int field_num);
extern char *PQcmdStatus(PGresult *res);
extern const char *PQoidStatus(PGresult *res);
extern const char *PQcmdTuples(PGresult *res);
extern char *PQgetvalue(PGresult *res, int tup_num, int field_num);
extern int PQgetlength(PGresult *res, int tup_num, int field_num);
extern int PQgetisnull(PGresult *res, int tup_num, int field_num);
/* Delete a PGresult */
extern void PQclear(PGresult *res);
/* PQdisplayTuples() is a better version of PQprintTuples() */
/* === in fe-print.c === */
extern void PQprint(FILE *fout, /* output stream */
PGresult *res,
PQprintOpt *ps /* option structure */
);
/* PQdisplayTuples() is a better version of PQprintTuples(),
* but both are obsoleted by PQprint().
*/
extern void PQdisplayTuples(PGresult *res,
FILE *fp, /* where to send the
* output */
int fillAlign, /* pad the fields with
* spaces */
const char *fieldSep, /* field separator */
int printHeader, /* display headers? */
int quiet);
FILE *fp, /* where to send the
* output */
int fillAlign, /* pad the fields with
* spaces */
const char *fieldSep, /* field separator */
int printHeader, /* display headers? */
int quiet);
extern void PQprintTuples(PGresult *res,
FILE *fout, /* output stream */
int printAttName, /* print attribute names
* or not */
int terseOutput, /* delimiter bars or
* not? */
int width /* width of column, if
* 0, use variable width */
);
extern void PQprint(FILE *fout, /* output stream */
PGresult *res,
PQprintOpt *ps /* option structure */
);
extern PGnotify *PQnotifies(PGconn *conn);
extern PGresult *PQfn(PGconn *conn,
int fnid,
int *result_buf,
int *result_len,
int result_is_int,
PQArgBlock *args,
int nargs);
FILE *fout, /* output stream */
int printAttName, /* print attribute names
* or not */
int terseOutput, /* delimiter bars or
* not? */
int width /* width of column, if
* 0, use variable width */
);
/* === in fe-auth.c === */
extern MsgType fe_getauthsvc(char *PQerrormsg);
extern void fe_setauthsvc(const char *name, char *PQerrormsg);
extern char *fe_getauthname(char *PQerrormsg);
/* === in fe-misc.c === */
/* pqGets and pqPuts gets and sends strings to the file stream
returns 0 if successful
if debug is non-null, debugging output is sent to that stream
*/
extern int pqGets(char *s, int maxlen, FILE *stream, FILE *debug);
extern int pqGetnchar(char *s, int maxlen, FILE *stream, FILE *debug);
extern int pqPutnchar(const char *s, int maxlen, FILE *stream, FILE *debug);
extern int pqPuts(const char *s, FILE *stream, FILE *debug);
extern int pqGetc(FILE *stream, FILE *debug);
/* get a n-byte integer from the stream into result */
/* returns 0 if successful */
extern int pqGetInt(int *result, int bytes, FILE *stream, FILE *debug);
/* put a n-byte integer into the stream */
/* returns 0 if successful */
extern int pqPutInt(const int n, int bytes, FILE *stream, FILE *debug);
extern void pqFlush(FILE *stream, FILE *debug);
/* "Get" and "Put" routines return 0 if successful, EOF if not.
* Note that for Get, EOF merely means the buffer is exhausted,
* not that there is necessarily any error.
*/
extern int pqGetc(char *result, PGconn *conn);
extern int pqGets(char *s, int maxlen, PGconn *conn);
extern int pqPuts(const char *s, PGconn *conn);
extern int pqGetnchar(char *s, int len, PGconn *conn);
extern int pqPutnchar(const char *s, int len, PGconn *conn);
extern int pqGetInt(int *result, int bytes, PGconn *conn);
extern int pqPutInt(int value, int bytes, PGconn *conn);
extern int pqReadData(PGconn *conn);
extern int pqFlush(PGconn *conn);
extern int pqWait(int forRead, int forWrite, PGconn *conn);
/* === in fe-lobj.c === */
int lo_open(PGconn *conn, Oid lobjId, int mode);
int lo_close(PGconn *conn, int fd);
int lo_read(PGconn *conn, int fd, char *buf, int len);
int lo_write(PGconn *conn, int fd, char *buf, int len);
int lo_lseek(PGconn *conn, int fd, int offset, int whence);
Oid lo_creat(PGconn *conn, int mode);
int lo_tell(PGconn *conn, int fd);
int lo_unlink(PGconn *conn, Oid lobjId);
Oid lo_import(PGconn *conn, char *filename);
int lo_export(PGconn *conn, Oid lobjId, char *filename);
extern int lo_open(PGconn *conn, Oid lobjId, int mode);
extern int lo_close(PGconn *conn, int fd);
extern int lo_read(PGconn *conn, int fd, char *buf, int len);
extern int lo_write(PGconn *conn, int fd, char *buf, int len);
extern int lo_lseek(PGconn *conn, int fd, int offset, int whence);
extern Oid lo_creat(PGconn *conn, int mode);
extern int lo_tell(PGconn *conn, int fd);
extern int lo_unlink(PGconn *conn, Oid lobjId);
extern Oid lo_import(PGconn *conn, char *filename);
extern int lo_export(PGconn *conn, Oid lobjId, char *filename);
/* max length of message to send */
#define MAX_MESSAGE_LEN 8193
/* maximum number of fields in a tuple */
#define BYTELEN 8
#define MAX_FIELDS 512
/* bits in a byte */
#define BYTELEN 8
/* fall back options if they are not specified by arguments or defined
by environment variables */
#define DefaultHost "localhost"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册