提交 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
......@@ -171,43 +175,43 @@ NullCommand(CommandDest dest)
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.
/* ----------------
* tell the fe that we saw an empty query string
* ----------------
*/
PQNotifyList *nPtr;
MemoryContext orig;
if (notifyContext == NULL)
{
notifyContext = CreateGlobalMemory("notify");
pq_putstr("I");
}
orig = MemoryContextSwitchTo((MemoryContext) notifyContext);
break;
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);
case Local:
case Debug:
case None:
default:
break;
}
pq_flush();
PQcleanNotify();/* garbage collect */
MemoryContextSwitchTo(orig);
#endif
/* ----------------
* tell the fe that the last of the queries has finished
}
/* ----------------
* 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.
* ----------------
*/
/* pq_putnchar("I", 1); */
pq_putstr("I");
/* pq_putint(0, 4); */
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
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.65 1998/04/21 04:00:06 scrappy Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.66 1998/05/06 23:51:11 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -41,14 +41,14 @@
#endif
/* use a local version instead of the one found in pqpacket.c */
static ConnStatusType connectDB(PGconn *conn);
static PGconn *makeEmptyPGconn(void);
static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn);
static int conninfo_parse(const char *conninfo, char *errorMessage);
static char *conninfo_getval(char *keyword);
static void conninfo_free(void);
/* XXX Why is this not static? */
void PQsetenv(PGconn *conn);
#define NOTIFYLIST_INITIAL_SIZE 10
......@@ -162,44 +162,30 @@ PGconn *
PQconnectdb(const char *conninfo)
{
PGconn *conn;
char errorMessage[ERROR_MSG_LENGTH];
char *tmp;
/* ----------
* Allocate memory for the conn structure
* ----------
*/
conn = (PGconn *) malloc(sizeof(PGconn));
conn = makeEmptyPGconn();
if (conn == NULL)
{
fprintf(stderr,
"FATAL: PQsetdb() -- unable to allocate memory for a PGconn");
"FATAL: PQconnectdb() -- unable to allocate memory for a PGconn");
return (PGconn *) NULL;
}
MemSet((char *) conn, 0, sizeof(PGconn));
/* ----------
* Parse the conninfo string and get the fallback resources
* Parse the conninfo string and save settings in conn structure
* ----------
*/
if (conninfo_parse(conninfo, errorMessage) < 0)
if (conninfo_parse(conninfo, conn->errorMessage) < 0)
{
conn->status = CONNECTION_BAD;
strcpy(conn->errorMessage, errorMessage);
conninfo_free();
return conn;
}
/* ----------
* Setup the conn structure
* ----------
*/
conn->lobjfuncs = (PGlobjfuncs *) NULL;
conn->Pfout = NULL;
conn->Pfin = NULL;
conn->Pfdebug = NULL;
conn->notifyList = DLNewList();
tmp = conninfo_getval("host");
conn->pghost = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("port");
......@@ -208,12 +194,12 @@ PQconnectdb(const char *conninfo)
conn->pgtty = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("options");
conn->pgoptions = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("dbname");
conn->dbName = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("user");
conn->pguser = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("password");
conn->pgpass = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval("dbname");
conn->dbName = tmp ? strdup(tmp) : NULL;
/* ----------
* Free the connection info - all is in conn now
......@@ -226,24 +212,6 @@ PQconnectdb(const char *conninfo)
* ----------
*/
conn->status = connectDB(conn);
if (conn->status == CONNECTION_OK)
{
PGresult *res;
/*
* Send a blank query to make sure everything works; in
* particular, that the database exists.
*/
res = PQexec(conn, " ");
if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY)
{
/* PQexec has put error message in conn->errorMessage */
closePGconn(conn);
}
PQclear(res);
}
PQsetenv(conn);
return conn;
}
......@@ -311,30 +279,21 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
{
PGconn *conn;
char *tmp;
char errorMessage[ERROR_MSG_LENGTH];
/* An error message from some service we call. */
bool error;
bool error = FALSE;
/* We encountered an error that prevents successful completion */
int i;
conn = (PGconn *) malloc(sizeof(PGconn));
conn = makeEmptyPGconn();
if (conn == NULL)
fprintf(stderr,
"FATAL: PQsetdb() -- unable to allocate memory for a PGconn");
else
{
conn->lobjfuncs = (PGlobjfuncs *) NULL;
conn->Pfout = NULL;
conn->Pfin = NULL;
conn->Pfdebug = NULL;
conn->notifyList = DLNewList();
fprintf(stderr,
"FATAL: PQsetdbLogin() -- unable to allocate memory for a PGconn");
return (PGconn *) NULL;
}
if ((pghost == NULL) || pghost[0] == '\0')
{
conn->pghost = NULL;
if ((tmp = getenv("PGHOST")) != NULL)
conn->pghost = strdup(tmp);
}
......@@ -370,28 +329,22 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
if (login)
{
error = FALSE;
conn->pguser = strdup(login);
}
else if ((tmp = getenv("PGUSER")) != NULL)
{
error = FALSE;
conn->pguser = strdup(tmp);
}
else
{
tmp = fe_getauthname(errorMessage);
if (tmp == 0)
conn->pguser = fe_getauthname(conn->errorMessage);
}
if (conn->pguser == NULL)
{
error = TRUE;
sprintf(conn->errorMessage,
"FATAL: PQsetdb: Unable to determine a Postgres username!\n");
}
else
{
error = FALSE;
conn->pguser = tmp;
}
"FATAL: PQsetdbLogin(): Unable to determine a Postgres username!\n");
}
if (pwd)
......@@ -403,16 +356,22 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
conn->pgpass = strdup(tmp);
}
else
{
conn->pgpass = strdup(DefaultPassword);
}
if (!error)
if ((dbName == NULL) || dbName[0] == '\0')
{
if ((((tmp = (char *) dbName) != NULL) && (dbName[0] != '\0'))
|| ((tmp = getenv("PGDATABASE"))))
if ((tmp = getenv("PGDATABASE")) != NULL)
conn->dbName = strdup(tmp);
else
else if (conn->pguser)
conn->dbName = strdup(conn->pguser);
}
else
conn->dbName = strdup(dbName);
if (conn->dbName)
{
/*
* if the database name is surrounded by double-quotes, then
* don't convert case
......@@ -420,41 +379,19 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
if (*conn->dbName == '"')
{
strcpy(conn->dbName, conn->dbName + 1);
*(conn->dbName + strlen(conn->dbName) - 1) = '\0';
conn->dbName[strlen(conn->dbName) - 1] = '\0';
}
else
for (i = 0; conn->dbName[i]; i++)
if (isupper(conn->dbName[i]))
conn->dbName[i] = tolower(conn->dbName[i]);
}
else
conn->dbName = NULL;
if (error)
conn->status = CONNECTION_BAD;
else
{
conn->status = connectDB(conn);
/* Puts message in conn->errorMessage */
if (conn->status == CONNECTION_OK)
{
PGresult *res;
/*
* Send a blank query to make sure everything works; in
* particular, that the database exists.
*/
res = PQexec(conn, " ");
if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY)
{
/* PQexec has put error message in conn->errorMessage */
closePGconn(conn);
}
PQclear(res);
}
PQsetenv(conn);
}
}
return conn;
}
......@@ -468,6 +405,7 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
static ConnStatusType
connectDB(PGconn *conn)
{
PGresult *res;
struct hostent *hp;
StartupPacket sp;
......@@ -476,6 +414,7 @@ connectDB(PGconn *conn)
int portno,
family,
len;
char beresp;
/*
* Initialize the startup packet.
......@@ -506,16 +445,17 @@ connectDB(PGconn *conn)
conn->pghost);
goto connect_errReturn;
}
family = AF_INET;
}
else
else {
hp = NULL;
family = AF_UNIX;
}
#if FALSE
MemSet((char *) &port->raddr, 0, sizeof(port->raddr));
#endif
portno = atoi(conn->pgport);
family = (conn->pghost != NULL) ? AF_INET : AF_UNIX;
MemSet((char *) &conn->raddr, 0, sizeof(conn->raddr));
conn->raddr.sa.sa_family = family;
portno = atoi(conn->pgport);
if (family == AF_INET)
{
memmove((char *) &(conn->raddr.in.sin_addr),
......@@ -528,7 +468,8 @@ connectDB(PGconn *conn)
{
len = UNIXSOCK_PATH(conn->raddr.un, portno);
}
/* connect to the server */
/* Connect to the server */
if ((conn->sock = socket(family, SOCK_STREAM, 0)) < 0)
{
(void) sprintf(conn->errorMessage,
......@@ -545,6 +486,20 @@ connectDB(PGconn *conn)
conn->pgport);
goto connect_errReturn;
}
/*
* Set the right options.
* We need nonblocking I/O, and we don't want delay of outgoing data.
*/
if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) < 0)
{
(void) sprintf(conn->errorMessage,
"connectDB() -- fcntl() failed: errno=%d\n%s\n",
errno, strerror(errno));
goto connect_errReturn;
}
if (family == AF_INET)
{
struct protoent *pe;
......@@ -561,12 +516,13 @@ connectDB(PGconn *conn)
&on, sizeof(on)) < 0)
{
(void) sprintf(conn->errorMessage,
"connectDB(): setsockopt failed\n");
"connectDB() -- setsockopt failed: errno=%d\n%s\n",
errno, strerror(errno));
goto connect_errReturn;
}
}
/* fill in the client address */
/* Fill in the client address */
if (getsockname(conn->sock, &conn->laddr.sa, &laddrlen) < 0)
{
(void) sprintf(conn->errorMessage,
......@@ -575,95 +531,140 @@ connectDB(PGconn *conn)
goto connect_errReturn;
}
/* set up the socket file descriptors */
conn->Pfout = fdopen(conn->sock, "w");
conn->Pfin = fdopen(dup(conn->sock), "r");
if ((conn->Pfout == NULL) || (conn->Pfin == NULL))
{
(void) sprintf(conn->errorMessage,
"connectDB() -- fdopen() failed: errno=%d\n%s\n",
errno, strerror(errno));
goto connect_errReturn;
}
/* Ensure our buffers are empty */
conn->inStart = conn->inCursor = conn->inEnd = 0;
conn->outCount = 0;
/* Send the startup packet. */
if (packetSend(conn, (char *) &sp, sizeof(StartupPacket)) != STATUS_OK)
{
sprintf(conn->errorMessage,
"connectDB() -- couldn't send complete packet: errno=%d\n%s\n", errno, strerror(errno));
"connectDB() -- couldn't send startup packet: errno=%d\n%s\n",
errno, strerror(errno));
goto connect_errReturn;
}
/*
* Get the response from the backend, either an error message or an
* authentication request.
* Perform the authentication exchange:
* wait for backend messages and respond as necessary.
* We fall out of this loop when done talking to the postmaster.
*/
do
{
int beresp;
if ((beresp = pqGetc(conn->Pfin, conn->Pfdebug)) == EOF)
for (;;)
{
(void) sprintf(conn->errorMessage,
"connectDB() -- error getting authentication request\n");
/* Wait for some data to arrive (or for the channel to close) */
if (pqWait(TRUE, FALSE, conn))
goto connect_errReturn;
}
/* Load data, or detect EOF */
if (pqReadData(conn) < 0)
goto connect_errReturn;
/* Scan the message.
* If we run out of data, loop around to try again.
*/
conn->inCursor = conn->inStart;
/* Handle errors. */
if (pqGetc(&beresp, conn))
continue; /* no data yet */
/* Handle errors. */
if (beresp == 'E')
{
pqGets(conn->errorMessage, sizeof(conn->errorMessage),
conn->Pfin, conn->Pfdebug);
if (pqGets(conn->errorMessage, sizeof(conn->errorMessage), conn))
continue;
goto connect_errReturn;
}
/* Check it was an authentication request. */
/* Otherwise it should be an authentication request. */
if (beresp != 'R')
{
(void) sprintf(conn->errorMessage,
"connectDB() -- expected authentication request\n");
goto connect_errReturn;
}
/* Get the type of request. */
if (pqGetInt((int *) &areq, 4, conn))
continue;
if (pqGetInt((int *) &areq, 4, conn->Pfin, conn->Pfdebug))
/* Get the password salt if there is one. */
if (areq == AUTH_REQ_CRYPT)
{
(void) sprintf(conn->errorMessage,
"connectDB() -- error getting authentication request type\n");
if (pqGetnchar(conn->salt, sizeof(conn->salt), conn))
continue;
}
/* OK, we successfully read the message; mark data consumed */
conn->inStart = conn->inCursor;
/* Respond to the request if necessary. */
if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
conn->errorMessage) != STATUS_OK)
goto connect_errReturn;
if (pqFlush(conn))
goto connect_errReturn;
}
/* Get the password salt if there is one. */
/* Are we done? */
if (areq == AUTH_REQ_OK)
break;
}
if (areq == AUTH_REQ_CRYPT &&
pqGetnchar(conn->salt, sizeof(conn->salt),
conn->Pfin, conn->Pfdebug))
{
(void) sprintf(conn->errorMessage,
"connectDB() -- error getting password salt\n");
/*
* Now we expect to hear from the backend.
* A ReadyForQuery message indicates that startup is successful,
* but we might also get an Error message indicating failure.
* (Notice messages indicating nonfatal warnings are also allowed
* by the protocol.)
* Easiest way to handle this is to let PQgetResult() read the messages.
* We just have to fake it out about the state of the connection.
*/
conn->status = CONNECTION_OK;
conn->asyncStatus = PGASYNC_BUSY;
res = PQgetResult(conn);
/* NULL return indicating we have gone to IDLE state is expected */
if (res) {
if (res->resultStatus != PGRES_FATAL_ERROR)
sprintf(conn->errorMessage,
"connectDB() -- unexpected message during startup\n");
PQclear(res);
goto connect_errReturn;
}
/* Given the new protocol that sends a ReadyForQuery message
* after successful backend startup, it should no longer be
* necessary to send an empty query to test for startup.
*/
/* Respond to the request. */
#if 0
if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
conn->errorMessage) != STATUS_OK)
/*
* Send a blank query to make sure everything works; in
* particular, that the database exists.
*/
res = PQexec(conn, " ");
if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY)
{
/* PQexec has put error message in conn->errorMessage */
closePGconn(conn);
PQclear(res);
goto connect_errReturn;
}
while (areq != AUTH_REQ_OK);
PQclear(res);
/* free the password so it's not hanging out in memory forever */
#endif
/* Post-connection housekeeping.
* Send environment variables to server
*/
PQsetenv(conn);
/* Free the password so it's not hanging out in memory forever */
/* XXX Is this *really* a good idea? The security gain is marginal
* if not totally illusory, and it breaks PQreset() for databases
* that use passwords.
*/
if (conn->pgpass != NULL)
{
free(conn->pgpass);
......@@ -673,6 +674,11 @@ connectDB(PGconn *conn)
return CONNECTION_OK;
connect_errReturn:
if (conn->sock >= 0)
{
close(conn->sock);
conn->sock = -1;
}
return CONNECTION_BAD;
}
......@@ -704,6 +710,36 @@ PQsetenv(PGconn *conn)
}
} /* PQsetenv() */
/*
* makeEmptyPGconn
* - create a PGconn data structure with (as yet) no interesting data
*/
static PGconn *
makeEmptyPGconn(void)
{
PGconn *conn = (PGconn *) malloc(sizeof(PGconn));
if (conn == NULL)
return conn;
/* Zero all pointers */
MemSet((char *) conn, 0, sizeof(PGconn));
conn->status = CONNECTION_BAD;
conn->asyncStatus = PGASYNC_IDLE;
conn->notifyList = DLNewList();
conn->sock = -1;
conn->inBufSize = 8192;
conn->inBuffer = (char *) malloc(conn->inBufSize);
conn->outBufSize = 8192;
conn->outBuffer = (char *) malloc(conn->outBufSize);
if (conn->inBuffer == NULL || conn->outBuffer == NULL)
{
freePGconn(conn);
conn = NULL;
}
return conn;
}
/*
* freePGconn
* - free the PGconn data structure
......@@ -714,22 +750,32 @@ freePGconn(PGconn *conn)
{
if (!conn)
return;
PQclearAsyncResult(conn); /* deallocate result and curTuple */
if (conn->sock >= 0)
close(conn->sock); /* shouldn't happen, but... */
if (conn->pghost)
free(conn->pghost);
if (conn->pgport)
free(conn->pgport);
if (conn->pgtty)
free(conn->pgtty);
if (conn->pgoptions)
free(conn->pgoptions);
if (conn->pgport)
free(conn->pgport);
if (conn->dbName)
free(conn->dbName);
if (conn->pguser)
free(conn->pguser);
if (conn->pgpass)
free(conn->pgpass);
/* Note that conn->Pfdebug is not ours to close or free */
if (conn->notifyList)
DLFreeList(conn->notifyList);
if (conn->lobjfuncs)
free(conn->lobjfuncs);
if (conn->inBuffer)
free(conn->inBuffer);
if (conn->outBuffer)
free(conn->outBuffer);
free(conn);
}
......@@ -740,42 +786,54 @@ freePGconn(PGconn *conn)
static void
closePGconn(PGconn *conn)
{
/* GH: What to do for !USE_POSIX_SIGNALS ? */
#if defined(USE_POSIX_SIGNALS)
struct sigaction ignore_action;
/*
* This is used as a constant, but not declared as such because the
* sigaction structure is defined differently on different systems
*/
struct sigaction oldaction;
if (conn->sock >= 0)
{
/*
* Try to send close message.
* If connection is already gone, that's cool. No reason for kernel
* to kill us when we try to write to it. So ignore SIGPIPE signals.
*/
#if defined(USE_POSIX_SIGNALS)
struct sigaction ignore_action;
struct sigaction oldaction;
ignore_action.sa_handler = SIG_IGN;
sigemptyset(&ignore_action.sa_mask);
ignore_action.sa_flags = 0;
sigaction(SIGPIPE, (struct sigaction *) & ignore_action, &oldaction);
fputs("X\0", conn->Pfout);
fflush(conn->Pfout);
(void) pqPuts("X", conn);
(void) pqFlush(conn);
sigaction(SIGPIPE, &oldaction, NULL);
#else
signal(SIGPIPE, SIG_IGN);
fputs("X\0", conn->Pfout);
fflush(conn->Pfout);
signal(SIGPIPE, SIG_DFL);
void (*oldsignal)(int);
oldsignal = signal(SIGPIPE, SIG_IGN);
(void) pqPuts("X", conn);
(void) pqFlush(conn);
signal(SIGPIPE, oldsignal);
#endif
if (conn->Pfout)
fclose(conn->Pfout);
if (conn->Pfin)
fclose(conn->Pfin);
if (conn->Pfdebug)
fclose(conn->Pfdebug);
}
/*
* Close the connection, reset all transient state, flush I/O buffers.
*/
if (conn->sock >= 0)
close(conn->sock);
conn->sock = -1;
conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just
* absent */
conn->asyncStatus = PGASYNC_IDLE;
PQclearAsyncResult(conn); /* deallocate result and curTuple */
if (conn->lobjfuncs)
free(conn->lobjfuncs);
conn->lobjfuncs = NULL;
conn->inStart = conn->inCursor = conn->inEnd = 0;
conn->outCount = 0;
}
/*
......@@ -793,7 +851,6 @@ PQfinish(PGconn *conn)
}
else
{
if (conn->status == CONNECTION_OK)
closePGconn(conn);
freePGconn(conn);
}
......@@ -818,12 +875,8 @@ PQreset(PGconn *conn)
}
/*
* PacketSend()
*
this is just like PacketSend(), defined in backend/libpq/pqpacket.c
but we define it here to avoid linking in all of libpq.a
* packetSend -- send a single-packet message.
* PacketSend() -- send a single-packet message.
* this is like PacketSend(), defined in backend/libpq/pqpacket.c
*
* RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
* SIDE_EFFECTS: may block.
......@@ -833,15 +886,16 @@ packetSend(PGconn *conn, const char *buf, size_t len)
{
/* Send the total packet size. */
if (pqPutInt(4 + len, 4, conn->Pfout, conn->Pfdebug))
if (pqPutInt(4 + len, 4, conn))
return STATUS_ERROR;
/* Send the packet itself. */
if (pqPutnchar(buf, len, conn->Pfout, conn->Pfdebug))
if (pqPutnchar(buf, len, conn))
return STATUS_ERROR;
pqFlush(conn->Pfout, conn->Pfdebug);
if (pqFlush(conn))
return STATUS_ERROR;
return STATUS_OK;
}
......@@ -1203,6 +1257,17 @@ PQerrorMessage(PGconn *conn)
return conn->errorMessage;
}
int
PQsocket(PGconn *conn)
{
if (!conn)
{
fprintf(stderr, "PQsocket() -- pointer to PGconn is null\n");
return -1;
}
return conn->sock;
}
void
PQtrace(PGconn *conn, FILE *debug_port)
{
......@@ -1218,8 +1283,8 @@ PQtrace(PGconn *conn, FILE *debug_port)
void
PQuntrace(PGconn *conn)
{
if (conn == NULL ||
conn->status == CONNECTION_BAD)
/* note: better allow untrace even when connection bad */
if (conn == NULL)
{
return;
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册