Commit bd8d4417 authored by Tom Lane's avatar Tom Lane

Second round of FE/BE protocol changes. Frontend->backend messages now

have length counts, and COPY IN data is packetized into messages.
parent 54b38d29
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.118 2003/04/17 22:26:00 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.119 2003/04/19 00:02:29 tgl Exp $
-->
<chapter id="libpq">
......@@ -1749,9 +1749,10 @@ state will never end.
<term><function>PQflush</function></term>
<listitem>
<para>
Attempts to flush any data queued to the server,
returns 0 if successful (or if the send queue is empty) or <symbol>EOF</symbol> if it failed for
some reason.
Attempts to flush any data queued to the server.
Returns 0 if successful (or if the send queue is empty), -1 if it failed for
some reason, or 1 if it was unable to send all the data in the send queue yet
(this case can only occur if the connection is nonblocking).
<synopsis>
int PQflush(PGconn *conn);
</synopsis>
......
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.27 2003/04/16 20:53:38 tgl Exp $ -->
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.28 2003/04/19 00:02:29 tgl Exp $ -->
<chapter id="protocol">
<title>Frontend/Backend Protocol</title>
......@@ -819,8 +819,9 @@
Copy-in mode (data transfer to the server) is initiated when the
backend executes a <command>COPY FROM STDIN</> SQL statement. The backend
sends a CopyInResponse message to the frontend. The frontend should
then send zero or more CopyDataRow messages, one per row to be loaded.
(For <command>COPY BINARY</>, send CopyBinaryRow messages instead.)
then send zero or more CopyData messages, forming a stream of input
data. (The message boundaries are not required to have anything to do
with row boundaries, although that is often a reasonable choice.)
The frontend can terminate the copy-in mode by sending either a CopyDone
message (allowing successful termination) or a CopyFail message (which
will cause the <command>COPY</> SQL statement to fail with an
......@@ -833,37 +834,33 @@
<para>
In the event of a backend-detected error during copy-in mode (including
receipt of a CopyFail message, or indeed any frontend message other than
CopyDataRow, CopyBinaryRow, or CopyDone), the backend will issue an
ErrorResponse
CopyData or CopyDone), the backend will issue an ErrorResponse
message. If the <command>COPY</> command was issued via an extended-query
message, the backend will now discard frontend messages until a Sync
message is received, then it will issue ReadyForQuery and return to normal
processing. If the <command>COPY</> command was issued in a simple
Query message, the rest of that message is discarded and ReadyForQuery
is issued. In either case, any subsequent CopyDataRow, CopyBinaryRow,
CopyDone, or CopyFail messages issued by the frontend will simply be
dropped.
is issued. In either case, any subsequent CopyData, CopyDone, or CopyFail
messages issued by the frontend will simply be dropped.
</para>
<para>
Copy-out mode (data transfer from the server) is initiated when the
backend executes a <command>COPY TO STDOUT</> SQL statement. The backend
sends a CopyOutResponse message to the frontend, followed by
zero or more CopyDataRow messages, one per row, followed by CopyDone.
(For <command>COPY BINARY</>, CopyBinaryRow messages are sent instead.)
zero or more CopyData messages (always one per row), followed by CopyDone.
The backend then reverts to the command-processing mode it was
in before the <command>COPY</> started, and sends CommandComplete.
The frontend cannot abort
the transfer (short of closing the connection), but it can discard
unwanted CopyDataRow, CopyBinaryRow, and CopyDone messages.
The frontend cannot abort the transfer (short of closing the connection),
but it can discard unwanted CopyData and CopyDone messages.
</para>
<para>
In the event of a backend-detected error during copy-out mode,
the backend will issue an ErrorResponse message and revert to normal
processing. The frontend should treat receipt of ErrorResponse (or
indeed any message type other than CopyDataRow, CopyBinaryRow, or
CopyDone) as terminating the copy-out mode.
indeed any message type other than CopyData or CopyDone) as terminating
the copy-out mode.
</para>
</sect2>
......@@ -1157,7 +1154,9 @@ indicate that it may be sent by a frontend (F), a backend (B), or both
(F &amp; B).
Notice that although each message includes a byte count at the beginning,
the message format is defined so that the message end can be found without
reference to the byte count. This aids validity checking.
reference to the byte count. This aids validity checking. (The CopyData
message is an exception, because it forms part of a data stream; the contents
may not be interpretable on their own.)
</para>
<VariableList>
......@@ -2002,83 +2001,7 @@ CommandComplete (B)
<VarListEntry>
<Term>
CopyBinaryRow (F &amp; B)
</Term>
<ListItem>
<Para>
<VariableList>
<VarListEntry>
<Term>
Byte1('b')
</Term>
<ListItem>
<Para>
Identifies the message as binary COPY data.
Note that the message body format is identical to the
<command>COPY BINARY</> file-format representation for
a single row of data.
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
Int32
</Term>
<ListItem>
<Para>
Length of message contents in bytes, including self.
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
Int16
</Term>
<ListItem>
<Para>
Specifies the number of fields in the row (can be zero).
</Para>
</ListItem>
</VarListEntry>
</VariableList>
Then, for each field, there is the following:
<VariableList>
<VarListEntry>
<Term>
Int16
</Term>
<ListItem>
<Para>
Zero if the field is null, otherwise the <varname>typlen</>
for the field datatype.
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
Byte<Replaceable>n</Replaceable>
</Term>
<ListItem>
<Para>
The value of the field itself in binary format.
Omitted if the field is null.
<Replaceable>n</Replaceable> is the <varname>typlen</>
value if <varname>typlen</> is positive. If
<varname>typlen</> is -1 then the field value begins with
its own length as an Int32 (the length includes itself).
</Para>
</ListItem>
</VarListEntry>
</VariableList>
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
CopyDataRow (F &amp; B)
CopyData (F &amp; B)
</Term>
<ListItem>
<Para>
......@@ -2089,7 +2012,7 @@ CopyDataRow (F &amp; B)
</Term>
<ListItem>
<Para>
Identifies the message as textual COPY data.
Identifies the message as COPY data.
</Para>
</ListItem>
</VarListEntry>
......@@ -2105,12 +2028,14 @@ CopyDataRow (F &amp; B)
</VarListEntry>
<VarListEntry>
<Term>
String
Byte<Replaceable>n</Replaceable>
</Term>
<ListItem>
<Para>
The textual representation of a single row of table data.
It should end with a newline.
Data that forms part of a COPY datastream. Messages sent
from the backend will always correspond to single data rows,
but messages sent by frontends may divide the datastream
arbitrarily.
</Para>
</ListItem>
</VarListEntry>
......@@ -2236,8 +2161,7 @@ CopyInResponse (B)
</Term>
<ListItem>
<Para>
0 for textual copy (CopyDataRow is expected), 1 for
binary copy (CopyBinaryRow is expected).
0 for textual copy, 1 for binary copy.
</Para>
</ListItem>
</VarListEntry>
......@@ -2283,8 +2207,7 @@ CopyOutResponse (B)
</Term>
<ListItem>
<Para>
0 for textual copy (CopyDataRow will follow), 1 for
binary copy (CopyBinaryRow will follow).
0 for textual copy, 1 for binary copy.
</Para>
</ListItem>
</VarListEntry>
......@@ -3606,8 +3529,9 @@ StartupMessage (F)
<ListItem>
<Para>
The protocol version number. The most significant 16 bits are
the major version number (3 for the format described here).
The least 16 significant bits are the minor version number.
the major version number (3 or more for the format described
here).
The least significant 16 bits are the minor version number.
</Para>
</ListItem>
</VarListEntry>
......@@ -3654,17 +3578,18 @@ StartupMessage (F)
<ListItem>
<Para>
Command-line arguments for the backend. (This is
deprecated in favor of setting individual GUC
deprecated in favor of setting individual run-time
parameters.)
</Para>
</ListItem>
</VarListEntry>
</VariableList>
In addition to the above, any GUC parameter that can be
In addition to the above, any run-time parameter that can be
set at backend start time may be listed. Such settings
will be applied during backend start (after parsing the
command-line options if any).
command-line options if any). The values will act as
session defaults.
</Para>
</ListItem>
</VarListEntry>
......@@ -3913,4 +3838,41 @@ not line breaks.
</sect1>
<Sect1 id="protocol-changes">
<Title>Summary of Changes since Protocol 2.0</Title>
<para>
This section provides a quick checklist of changes, for the benefit of
developers trying to update existing client libraries to protocol 3.0.
</para>
<para>
The initial startup packet uses a flexible list-of-strings format
instead of a fixed format. Notice that session default values for run-time
parameters can now be specified directly in the startup packet. (Actually,
you could do that before using the <literal>options</> field, but given the
limited width of <literal>options</> and the lack of any way to quote
whitespace in the values, it wasn't a very safe technique.)
</para>
<para>
All messages now have a length count immediately following the message type
byte (except for startup packets, which have no type byte). Also note that
PasswordMessage now has a type byte.
</para>
<para>
COPY data is now encapsulated into CopyData and CopyDone messages. There
is a well-defined way to recover from errors during COPY.
</para>
<note>
<para>
Additional changes will be documented as they are implemented.
</para>
</note>
</sect1>
</Chapter>
This diff is collapsed.
......@@ -9,15 +9,15 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: stringinfo.c,v 1.32 2002/09/04 20:31:18 momjian Exp $
* $Id: stringinfo.c,v 1.33 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "lib/stringinfo.h"
/*
* makeStringInfo
*
......@@ -50,41 +50,7 @@ initStringInfo(StringInfo str)
str->maxlen = size;
str->len = 0;
str->data[0] = '\0';
}
/*
* enlargeStringInfo
*
* Internal routine: make sure there is enough space for 'needed' more bytes
* ('needed' does not include the terminating null).
*
* NB: because we use repalloc() to enlarge the buffer, the string buffer
* will remain allocated in the same memory context that was current when
* initStringInfo was called, even if another context is now current.
* This is the desired and indeed critical behavior!
*/
static void
enlargeStringInfo(StringInfo str, int needed)
{
int newlen;
needed += str->len + 1; /* total space required now */
if (needed <= str->maxlen)
return; /* got enough space already */
/*
* We don't want to allocate just a little more space with each
* append; for efficiency, double the buffer size each time it
* overflows. Actually, we might need to more than double it if
* 'needed' is big...
*/
newlen = 2 * str->maxlen;
while (needed > newlen)
newlen = 2 * newlen;
str->data = (char *) repalloc(str->data, newlen);
str->maxlen = newlen;
str->cursor = 0;
}
/*
......@@ -147,8 +113,9 @@ appendStringInfo(StringInfo str, const char *fmt,...)
}
}
/*------------------------
/*
* appendStringInfoChar
*
* Append a single byte to str.
* Like appendStringInfo(str, "%c", ch) but much faster.
*/
......@@ -189,3 +156,44 @@ appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
*/
str->data[str->len] = '\0';
}
/*
* enlargeStringInfo
*
* Make sure there is enough space for 'needed' more bytes
* ('needed' does not include the terminating null).
*
* External callers need not concern themselves with this, since all
* stringinfo.c routines do it automatically. However, if a caller
* knows that a StringInfo will eventually become X bytes large, it
* can save some palloc overhead by enlarging the buffer before starting
* to store data in it.
*
* NB: because we use repalloc() to enlarge the buffer, the string buffer
* will remain allocated in the same memory context that was current when
* initStringInfo was called, even if another context is now current.
* This is the desired and indeed critical behavior!
*/
void
enlargeStringInfo(StringInfo str, int needed)
{
int newlen;
needed += str->len + 1; /* total space required now */
if (needed <= str->maxlen)
return; /* got enough space already */
/*
* We don't want to allocate just a little more space with each
* append; for efficiency, double the buffer size each time it
* overflows. Actually, we might need to more than double it if
* 'needed' is big...
*/
newlen = 2 * str->maxlen;
while (needed > newlen)
newlen = 2 * newlen;
str->data = (char *) repalloc(str->data, newlen);
str->maxlen = newlen;
}
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.98 2003/04/17 22:26:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.99 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -37,6 +37,7 @@
static void sendAuthRequest(Port *port, AuthRequest areq);
static void auth_failed(Port *port, int status);
static char *recv_password_packet(Port *port);
static int recv_and_check_password_packet(Port *port);
char *pg_krb_server_keyfile;
......@@ -539,11 +540,9 @@ sendAuthRequest(Port *port, AuthRequest areq)
*/
static int
pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_response ** resp, void *appdata_ptr)
pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
struct pam_response ** resp, void *appdata_ptr)
{
StringInfoData buf;
int32 len;
if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
{
switch (msg[0]->msg_style)
......@@ -574,23 +573,20 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re
*/
if (strlen(appdata_ptr) == 0)
{
sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
return PAM_CONV_ERR; /* client didn't want to send password */
char *passwd;
initStringInfo(&buf);
if (pq_getstr_bounded(&buf, 1000) == EOF)
return PAM_CONV_ERR; /* EOF while reading password */
sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
passwd = recv_password_packet(pam_port_cludge);
/* Do not echo failed password to logs, for security. */
elog(DEBUG5, "received PAM packet");
if (passwd == NULL)
return PAM_CONV_ERR; /* client didn't want to send password */
if (strlen(buf.data) == 0)
if (strlen(passwd) == 0)
{
elog(LOG, "pam_passwd_conv_proc: no password");
return PAM_CONV_ERR;
}
appdata_ptr = buf.data;
appdata_ptr = passwd;
}
/*
......@@ -601,8 +597,6 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re
if (!*resp)
{
elog(LOG, "pam_passwd_conv_proc: Out of memory!");
if (buf.data)
pfree(buf.data);
return PAM_CONV_ERR;
}
......@@ -708,42 +702,87 @@ CheckPAMAuth(Port *port, char *user, char *password)
/*
* Called when we have received the password packet.
* Collect password response packet from frontend.
*
* Returns NULL if couldn't get password, else palloc'd string.
*/
static int
recv_and_check_password_packet(Port *port)
static char *
recv_password_packet(Port *port)
{
StringInfoData buf;
int32 len;
int result;
if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
return STATUS_EOF; /* client didn't want to send password */
if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
{
/* Expect 'p' message type */
int mtype;
mtype = pq_getbyte();
if (mtype != 'p')
{
/*
* If the client just disconnects without offering a password,
* don't make a log entry. This is legal per protocol spec and
* in fact commonly done by psql, so complaining just clutters
* the log.
*/
if (mtype != EOF)
elog(COMMERROR, "Expected password response, got %c", mtype);
return NULL; /* EOF or bad message type */
}
}
else
{
/* For pre-3.0 clients, avoid log entry if they just disconnect */
if (pq_peekbyte() == EOF)
return NULL; /* EOF */
}
initStringInfo(&buf);
if (pq_getstr_bounded(&buf, 1000) == EOF) /* receive password */
if (pq_getmessage(&buf, 1000)) /* receive password */
{
/* EOF - pq_getmessage already logged a suitable message */
pfree(buf.data);
return STATUS_EOF;
return NULL;
}
/*
* We don't actually use the password packet length the frontend sent
* us; however, it's a reasonable sanity check to ensure that we
* actually read as much data as we expected to.
*
* The password packet size is the length of the buffer, plus the size
* field itself (4 bytes), plus a 1-byte terminator.
* Apply sanity check: password packet length should agree with length
* of contained string. Note it is safe to use strlen here because
* StringInfo is guaranteed to have an appended '\0'.
*/
if (len != (buf.len + 4 + 1))
elog(LOG, "unexpected password packet size: read %d, expected %d",
buf.len + 4 + 1, len);
if (strlen(buf.data) + 1 != buf.len)
elog(COMMERROR, "bogus password packet size");
/* Do not echo password to logs, for security. */
elog(DEBUG5, "received password packet");
result = md5_crypt_verify(port, port->user_name, buf.data);
/*
* Return the received string. Note we do not attempt to do any
* character-set conversion on it; since we don't yet know the
* client's encoding, there wouldn't be much point.
*/
return buf.data;
}
/*
* Called when we have sent an authorization request for a password.
* Get the response and check it.
*/
static int
recv_and_check_password_packet(Port *port)
{
char *passwd;
int result;
passwd = recv_password_packet(port);
if (passwd == NULL)
return STATUS_EOF; /* client wouldn't send password */
result = md5_crypt_verify(port, port->user_name, passwd);
pfree(passwd);
pfree(buf.data);
return result;
}
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.29 2003/04/10 23:03:08 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.30 2003/04/19 00:02:29 tgl Exp $
*
* Since the server static private key ($DataDir/server.key)
* will normally be stored unencrypted so that the database
......@@ -110,13 +110,6 @@
extern void ExitPostmaster(int);
extern void postmaster_error(const char *fmt,...);
int secure_initialize(void);
void secure_destroy(void);
int secure_open_server(Port *);
void secure_close(Port *);
ssize_t secure_read(Port *, void *ptr, size_t len);
ssize_t secure_write(Port *, void *ptr, size_t len);
#ifdef USE_SSL
static DH *load_dh_file(int keylength);
static DH *load_dh_buffer(const char *, size_t);
......
......@@ -6,8 +6,8 @@
* These routines handle the low-level details of communication between
* frontend and backend. They just shove data across the communication
* channel, and are ignorant of the semantics of the data --- or would be,
* except for major brain damage in the design of the COPY OUT protocol.
* Unfortunately, COPY OUT is designed to commandeer the communication
* except for major brain damage in the design of the old COPY OUT protocol.
* Unfortunately, COPY OUT was designed to commandeer the communication
* channel (it just transfers data without wrapping it into messages).
* No other messages can be sent while COPY OUT is in progress; and if the
* copy is aborted by an elog(ERROR), we need to close out the copy so that
......@@ -29,7 +29,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pqcomm.c,v 1.149 2003/04/02 00:49:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.150 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -48,12 +48,13 @@
* low-level I/O:
* pq_getbytes - get a known number of bytes from connection
* pq_getstring - get a null terminated string from connection
* pq_getmessage - get a message with length word from connection
* pq_getbyte - get next byte from connection
* pq_peekbyte - peek at next byte from connection
* pq_putbytes - send bytes to connection (not flushed until pq_flush)
* pq_flush - flush pending output
*
* message-level I/O (and COPY OUT cruft):
* message-level I/O (and old-style-COPY-OUT cruft):
* pq_putmessage - send a normal message (suppressed in COPY OUT mode)
* pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
* pq_endcopyout - end a COPY OUT transfer
......@@ -85,9 +86,6 @@
#include "miscadmin.h"
#include "storage/ipc.h"
extern void secure_close(Port *);
extern ssize_t secure_read(Port *, void *, size_t);
extern ssize_t secure_write(Port *, const void *, size_t);
static void pq_close(void);
......@@ -562,8 +560,10 @@ pq_recvbuf(void)
}
if (r == 0)
{
/* as above, only write to postmaster log */
elog(COMMERROR, "pq_recvbuf: unexpected EOF on client connection");
/*
* EOF detected. We used to write a log message here, but it's
* better to expect the ultimate caller to do that.
*/
return EOF;
}
/* r contains number of bytes read, so just incr length */
......@@ -636,35 +636,29 @@ pq_getbytes(char *s, size_t len)
/* --------------------------------
* pq_getstring - get a null terminated string from connection
*
* The return value is placed in an expansible StringInfo.
* Note that space allocation comes from the current memory context!
* The return value is placed in an expansible StringInfo, which has
* already been initialized by the caller.
*
* If maxlen is not zero, it is an upper limit on the length of the
* string we are willing to accept. We abort the connection (by
* returning EOF) if client tries to send more than that. Note that
* since we test maxlen in the outer per-bufferload loop, the limit
* is fuzzy: we might accept up to PQ_BUFFER_SIZE more bytes than
* specified. This is fine for the intended purpose, which is just
* to prevent DoS attacks from not-yet-authenticated clients.
*
* NOTE: this routine does not do any character set conversion,
* even though it is presumably useful only for text, because
* no code in this module should depend on the encoding.
* See pq_getstr_bounded in pqformat.c for that.
* This is used only for dealing with old-protocol clients. The idea
* is to produce a StringInfo that looks the same as we would get from
* pq_getmessage() with a newer client; we will then process it with
* pq_getmsgstring. Therefore, no character set conversion is done here,
* even though this is presumably useful only for text.
*
* returns 0 if OK, EOF if trouble
* --------------------------------
*/
int
pq_getstring(StringInfo s, int maxlen)
pq_getstring(StringInfo s)
{
int i;
/* Reset string to empty */
s->len = 0;
s->data[0] = '\0';
s->cursor = 0;
/* Read until we get the terminating '\0' or overrun maxlen */
/* Read until we get the terminating '\0' */
for (;;)
{
while (PqRecvPointer >= PqRecvLength)
......@@ -677,9 +671,9 @@ pq_getstring(StringInfo s, int maxlen)
{
if (PqRecvBuffer[i] == '\0')
{
/* does not copy the \0 */
/* include the '\0' in the copy */
appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
i - PqRecvPointer);
i - PqRecvPointer + 1);
PqRecvPointer = i + 1; /* advance past \0 */
return 0;
}
......@@ -689,11 +683,70 @@ pq_getstring(StringInfo s, int maxlen)
appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
PqRecvLength - PqRecvPointer);
PqRecvPointer = PqRecvLength;
}
}
/* --------------------------------
* pq_getmessage - get a message with length word from connection
*
* The return value is placed in an expansible StringInfo, which has
* already been initialized by the caller.
* Only the message body is placed in the StringInfo; the length word
* is removed. Also, s->cursor is initialized to zero for convenience
* in scanning the message contents.
*
* If maxlen is not zero, it is an upper limit on the length of the
* message we are willing to accept. We abort the connection (by
* returning EOF) if client tries to send more than that.
*
* returns 0 if OK, EOF if trouble
* --------------------------------
*/
int
pq_getmessage(StringInfo s, int maxlen)
{
int32 len;
/* Reset message buffer to empty */
s->len = 0;
s->data[0] = '\0';
s->cursor = 0;
/* If maxlen is specified, check for overlength input. */
if (maxlen > 0 && s->len > maxlen)
/* Read message length word */
if (pq_getbytes((char *) &len, 4) == EOF)
{
elog(COMMERROR, "unexpected EOF within message length word");
return EOF;
}
len = ntohl(len);
len -= 4; /* discount length itself */
if (len < 0 ||
(maxlen > 0 && len > maxlen))
{
elog(COMMERROR, "invalid message length");
return EOF;
}
if (len > 0)
{
/* Allocate space for message */
enlargeStringInfo(s, len);
/* And grab the message */
if (pq_getbytes(s->data, len) == EOF)
{
elog(COMMERROR, "incomplete client message");
return EOF;
}
s->len = len;
/* Place a trailing null per StringInfo convention */
s->data[len] = '\0';
}
return 0;
}
......@@ -781,34 +834,10 @@ pq_flush(void)
}
/*
* Return EOF if the connection has been broken, else 0.
*/
int
pq_eof(void)
{
char x;
int res;
res = recv(MyProcPort->sock, &x, 1, MSG_PEEK);
if (res < 0)
{
/* can log to postmaster log only */
elog(COMMERROR, "pq_eof: recv() failed: %m");
return EOF;
}
if (res == 0)
return EOF;
else
return 0;
}
/* --------------------------------
* Message-level I/O routines begin here.
*
* These routines understand about COPY OUT protocol.
* These routines understand about the old-style COPY OUT protocol.
* --------------------------------
*/
......@@ -840,7 +869,8 @@ pq_putmessage(char msgtype, const char *s, size_t len)
}
/* --------------------------------
* pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
* pq_startcopyout - inform libpq that an old-style COPY OUT transfer
* is beginning
* --------------------------------
*/
void
......
......@@ -8,15 +8,17 @@
* formatting/conversion routines that are needed to produce valid messages.
* Note in particular the distinction between "raw data" and "text"; raw data
* is message protocol characters and binary values that are not subject to
* character set conversion, while text is converted by character encoding rules.
* character set conversion, while text is converted by character encoding
* rules.
*
* Incoming messages are read directly off the wire, as it were, but there
* are still data-conversion tasks to be performed.
* Incoming messages are similarly read into a StringInfo buffer, via
* pq_getmessage, and then parsed and converted from that using the routines
* in this module.
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pqformat.c,v 1.26 2003/04/02 00:49:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.27 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -37,12 +39,13 @@
* Special-case message output:
* pq_puttextmessage - generate a character set-converted message in one step
*
* Message input:
* pq_getint - get an integer from connection
* pq_getstr_bounded - get a null terminated string from connection
* pq_getstr_bounded performs character set conversion on the collected
* string. Use the raw pqcomm.c routines pq_getstring or pq_getbytes
* to fetch data without conversion.
* Message parsing after input:
* pq_getmsgbyte - get a raw byte from a message buffer
* pq_getmsgint - get a binary integer from a message buffer
* pq_getmsgbytes - get raw data from a message buffer
* pq_copymsgbytes - copy raw data from a message buffer
* pq_getmsgstring - get a null-terminated text string (with conversion)
* pq_getmsgend - verify message fully consumed
*/
#include "postgres.h"
......@@ -206,16 +209,29 @@ pq_puttextmessage(char msgtype, const char *str)
return pq_putmessage(msgtype, str, slen + 1);
}
/* --------------------------------
* pq_getint - get an integer from connection
*
* returns 0 if OK, EOF if trouble
* pq_getmsgbyte - get a raw byte from a message buffer
* --------------------------------
*/
int
pq_getint(int *result, int b)
pq_getmsgbyte(StringInfo msg)
{
if (msg->cursor >= msg->len)
elog(ERROR, "pq_getmsgbyte: no data left in message");
return (unsigned char) msg->data[msg->cursor++];
}
/* --------------------------------
* pq_getmsgint - get a binary integer from a message buffer
*
* Values are treated as unsigned.
* --------------------------------
*/
unsigned int
pq_getmsgint(StringInfo msg, int b)
{
int status;
unsigned int result;
unsigned char n8;
uint16 n16;
uint32 n32;
......@@ -223,59 +239,93 @@ pq_getint(int *result, int b)
switch (b)
{
case 1:
status = pq_getbytes((char *) &n8, 1);
*result = (int) n8;
pq_copymsgbytes(msg, (char *) &n8, 1);
result = n8;
break;
case 2:
status = pq_getbytes((char *) &n16, 2);
*result = (int) (ntohs(n16));
pq_copymsgbytes(msg, (char *) &n16, 2);
result = ntohs(n16);
break;
case 4:
status = pq_getbytes((char *) &n32, 4);
*result = (int) (ntohl(n32));
pq_copymsgbytes(msg, (char *) &n32, 4);
result = ntohl(n32);
break;
default:
/*
* if we elog(ERROR) here, we will lose sync with the
* frontend, so just complain to postmaster log instead...
*/
elog(COMMERROR, "pq_getint: unsupported size %d", b);
status = EOF;
*result = 0;
elog(ERROR, "pq_getmsgint: unsupported size %d", b);
result = 0; /* keep compiler quiet */
break;
}
return status;
return result;
}
/* --------------------------------
* pq_getstr_bounded - get a null terminated string from connection
* pq_getmsgbytes - get raw data from a message buffer
*
* The return value is placed in an expansible StringInfo.
* Note that space allocation comes from the current memory context!
* Returns a pointer directly into the message buffer; note this
* may not have any particular alignment.
* --------------------------------
*/
const char *
pq_getmsgbytes(StringInfo msg, int datalen)
{
const char *result;
if (datalen > (msg->len - msg->cursor))
elog(ERROR, "pq_getmsgbytes: insufficient data left in message");
result = &msg->data[msg->cursor];
msg->cursor += datalen;
return result;
}
/* --------------------------------
* pq_copymsgbytes - copy raw data from a message buffer
*
* The maxlen parameter is interpreted as per pq_getstring.
* Same as above, except data is copied to caller's buffer.
* --------------------------------
*/
void
pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
{
if (datalen > (msg->len - msg->cursor))
elog(ERROR, "pq_copymsgbytes: insufficient data left in message");
memcpy(buf, &msg->data[msg->cursor], datalen);
msg->cursor += datalen;
}
/* --------------------------------
* pq_getmsgstring - get a null-terminated text string (with conversion)
*
* returns 0 if OK, EOF if trouble
* May return a pointer directly into the message buffer, or a pointer
* to a palloc'd conversion result.
* --------------------------------
*/
int
pq_getstr_bounded(StringInfo s, int maxlen)
const char *
pq_getmsgstring(StringInfo msg)
{
int result;
char *p;
char *str;
int slen;
result = pq_getstring(s, maxlen);
str = &msg->data[msg->cursor];
/*
* It's safe to use strlen() here because a StringInfo is guaranteed
* to have a trailing null byte. But check we found a null inside
* the message.
*/
slen = strlen(str);
if (msg->cursor + slen >= msg->len)
elog(ERROR, "pq_getmsgstring: invalid string in message");
msg->cursor += slen + 1;
p = (char *) pg_client_to_server((unsigned char *) s->data, s->len);
if (p != s->data) /* actual conversion has been done? */
{
/* reset s to empty, and append the new string p */
s->len = 0;
s->data[0] = '\0';
appendBinaryStringInfo(s, p, strlen(p));
pfree(p);
}
return (const char *) pg_client_to_server((unsigned char *) str, slen);
}
return result;
/* --------------------------------
* pq_getmsgend - verify message fully consumed
* --------------------------------
*/
void
pq_getmsgend(StringInfo msg)
{
if (msg->cursor != msg->len)
elog(ERROR, "pq_getmsgend: invalid message format");
}
......@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.312 2003/04/18 01:03:42 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.313 2003/04/19 00:02:29 tgl Exp $
*
* NOTES
*
......@@ -1118,7 +1118,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
if (pq_getbytes((char *) &len, 4) == EOF)
{
elog(LOG, "incomplete startup packet");
elog(COMMERROR, "incomplete startup packet");
return STATUS_ERROR;
}
......@@ -1142,7 +1142,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
if (pq_getbytes(buf, len) == EOF)
{
elog(LOG, "incomplete startup packet");
elog(COMMERROR, "incomplete startup packet");
return STATUS_ERROR;
}
......@@ -1189,6 +1189,16 @@ ProcessStartupPacket(Port *port, bool SSLdone)
/* Could add additional special packet types here */
/*
* XXX temporary for 3.0 protocol development: we are using the minor
* number as a test-version number. Insist it match exactly so people
* don't get burnt by using yesterday's libpq with today's server.
* XXX this must go away before release!!!
*/
if (PG_PROTOCOL_MAJOR(proto) == 3 &&
PG_PROTOCOL_MINOR(proto) != PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))
elog(FATAL, "Your development libpq is out of sync with the server");
/* Check we can handle the protocol the frontend is using. */
if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
......@@ -1201,16 +1211,6 @@ ProcessStartupPacket(Port *port, bool SSLdone)
PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST),
PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST));
/*
* XXX temporary for 3.0 protocol development: we are using the minor
* number as a test-version number. Insist it match exactly so people
* don't get burnt by using yesterday's libpq with today's server.
* XXX this must go away before release!!!
*/
if (PG_PROTOCOL_MAJOR(proto) == 3 &&
PG_PROTOCOL_MINOR(proto) != PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))
elog(FATAL, "Your development libpq is out of sync with the server");
/*
* Now fetch parameters out of startup packet and save them into the
* Port structure. All data structures attached to the Port struct
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.51 2003/03/27 16:51:29 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.52 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -134,37 +134,6 @@ EndCommand(const char *commandTag, CommandDest dest)
}
}
/*
* These are necessary to sync communications between fe/be processes doing
* COPY rel TO stdout
*
* or
*
* 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)
{
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
pq_putbytes("H", 1); /* new way */
else
pq_putbytes("B", 1); /* old way */
}
void
ReceiveCopyBegin(void)
{
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
pq_putbytes("G", 1); /* new way */
else
pq_putbytes("D", 1); /* old way */
/* We *must* flush here to ensure FE knows it can send. */
pq_flush();
}
/* ----------------
* NullCommand - tell dest that an empty query string was recognized
*
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.57 2003/01/09 18:00:23 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.58 2003/04/19 00:02:29 tgl Exp $
*
* NOTES
* This cruft is the server side of PQfn.
......@@ -28,38 +28,13 @@
* back to the frontend. If the return returns by reference,
* send down only the data portion and set the return size appropriately.
*
* OLD COMMENTS FOLLOW
*
* The VAR_LENGTH_{ARGS,RESULT} stuff is limited to MAX_STRING_LENGTH
* (see src/backend/tmp/fastpath.h) for no obvious reason. Since its
* primary use (for us) is for Inversion path names, it should probably
* be increased to 256 (MAXPATHLEN for Inversion, hidden in pg_type
* as well as utils/adt/filename.c).
*
* Quoth PMA on 08/15/93:
*
* This code has been almost completely rewritten with an eye to
* keeping it as compatible as possible with the previous (broken)
* implementation.
*
* The previous implementation would assume (1) that any value of
* length <= 4 bytes was passed-by-value, and that any other value
* was a struct varlena (by-reference). There was NO way to pass a
* fixed-length by-reference argument (like name) or a struct
* varlena of size <= 4 bytes.
*
* The new implementation checks the catalogs to determine whether
* a value is by-value (type "0" is null-delimited character string,
* as it is for, e.g., the parser). The only other item obtained
* from the catalogs is whether or not the value should be placed in
* a struct varlena or not. Otherwise, the size given by the
* frontend is assumed to be correct (probably a bad decision, but
* we do strange things in the name of compatibility).
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include "access/xact.h"
#include "catalog/pg_proc.h"
#include "libpq/libpq.h"
......@@ -72,6 +47,67 @@
#include "utils/tqual.h"
/* ----------------
* GetOldFunctionMessage
*
* In pre-3.0 protocol, there is no length word on the message, so we have
* to have code that understands the message layout to absorb the message
* into a buffer. We want to do this before we start execution, so that
* we do not lose sync with the frontend if there's an error.
*
* The caller should already have initialized buf to empty.
* ----------------
*/
static int
GetOldFunctionMessage(StringInfo buf)
{
int32 ibuf;
int nargs;
/* Dummy string argument */
if (pq_getstring(buf))
return EOF;
/* Function OID */
if (pq_getbytes((char *) &ibuf, 4))
return EOF;
appendBinaryStringInfo(buf, (char *) &ibuf, 4);
/* Number of arguments */
if (pq_getbytes((char *) &ibuf, 4))
return EOF;
appendBinaryStringInfo(buf, (char *) &ibuf, 4);
nargs = ntohl(ibuf);
/* For each argument ... */
while (nargs-- > 0)
{
int argsize;
/* argsize */
if (pq_getbytes((char *) &ibuf, 4))
return EOF;
appendBinaryStringInfo(buf, (char *) &ibuf, 4);
argsize = ntohl(ibuf);
if (argsize < 0)
{
/* FATAL here since no hope of regaining message sync */
elog(FATAL, "HandleFunctionRequest: bogus argsize %d",
argsize);
}
/* and arg contents */
if (argsize > 0)
{
/* Allocate space for arg */
enlargeStringInfo(buf, argsize);
/* And grab it */
if (pq_getbytes(buf->data + buf->len, argsize))
return EOF;
buf->len += argsize;
/* Place a trailing null per StringInfo convention */
buf->data[buf->len] = '\0';
}
}
return 0;
}
/* ----------------
* SendFunctionResult
*
......@@ -205,6 +241,12 @@ fetch_fp_info(Oid func_id, struct fp_info * fip)
* Server side of PQfn (fastpath function calls from the frontend).
* This corresponds to the libpq protocol symbol "F".
*
* INPUT:
* In protocol version 3, postgres.c has already read the message body
* and will pass it in msgBuf.
* In old protocol, the passed msgBuf is empty and we must read the
* message here.
*
* RETURNS:
* 0 if successful completion, EOF if frontend connection lost.
*
......@@ -218,54 +260,44 @@ fetch_fp_info(Oid func_id, struct fp_info * fip)
* control returns to PostgresMain.
*/
int
HandleFunctionRequest(void)
HandleFunctionRequest(StringInfo msgBuf)
{
Oid fid;
int argsize;
int nargs;
int tmp;
AclResult aclresult;
FunctionCallInfoData fcinfo;
Datum retval;
int i;
char *p;
struct fp_info my_fp;
struct fp_info *fip;
/*
* XXX FIXME: This protocol is misdesigned.
*
* We really do not want to elog() before having swallowed all of the
* frontend's fastpath message; otherwise we will lose sync with the
* input datastream. What should happen is we absorb all of the input
* message per protocol syntax, and *then* do error checking
* (including lookup of the given function ID) and elog if
* appropriate. Unfortunately, because we cannot even read the
* message properly without knowing whether the data types are
* pass-by-ref or pass-by-value, it's not all that easy to do :-(. The
* protocol should require the client to supply what it thinks is the
* typbyval and typlen value for each arg, so that we can read the
* data without having to do any lookups. Then after we've read the
* message, we should do the lookups, verify agreement of the actual
* function arg types with what we received, and finally call the
* function.
*
* As things stand, not only will we lose sync for an invalid message
* (such as requested function OID doesn't exist), but we may lose
* sync for a perfectly valid message if we are in transaction-aborted
* state! This can happen because our database lookup attempts may
* fail entirely in abort state.
*
* Unfortunately I see no way to fix this without breaking a lot of
* existing clients. Maybe do it as part of next protocol version
* change.
* Read message contents if not already done.
*/
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
{
if (GetOldFunctionMessage(msgBuf))
{
elog(COMMERROR, "unexpected EOF on client connection");
return EOF;
}
}
if (pq_getint(&tmp, 4)) /* function oid */
return EOF;
fid = (Oid) tmp;
if (pq_getint(&nargs, 4)) /* # of arguments */
return EOF;
/*
* Now that we've eaten the input message, check to see if we actually
* want to do the function call or not. It's now safe to elog(); we won't
* lose sync with the frontend.
*/
if (IsAbortedTransactionBlockState())
elog(ERROR, "current transaction is aborted, "
"queries ignored until end of transaction block");
/*
* Parse the buffer contents.
*/
(void) pq_getmsgstring(msgBuf); /* dummy string */
fid = (Oid) pq_getmsgint(msgBuf, 4); /* function oid */
nargs = pq_getmsgint(msgBuf, 4); /* # of arguments */
/*
* There used to be a lame attempt at caching lookup info here. Now we
......@@ -274,11 +306,14 @@ HandleFunctionRequest(void)
fip = &my_fp;
fetch_fp_info(fid, fip);
/* Check permission to call function */
aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, get_func_name(fid));
if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
{
elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
nargs, fip->flinfo.fn_nargs);
}
MemSet(&fcinfo, 0, sizeof(fcinfo));
fcinfo.flinfo = &fip->flinfo;
......@@ -286,21 +321,21 @@ HandleFunctionRequest(void)
/*
* Copy supplied arguments into arg vector. Note there is no way for
* frontend to specify a NULL argument --- more misdesign.
* frontend to specify a NULL argument --- this protocol is misdesigned.
*/
for (i = 0; i < nargs; ++i)
{
if (pq_getint(&argsize, 4))
return EOF;
int argsize;
char *p;
argsize = pq_getmsgint(msgBuf, 4);
if (fip->argbyval[i])
{ /* by-value */
if (argsize < 1 || argsize > 4)
elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
argsize);
/* XXX should we demand argsize == fip->arglen[i] ? */
if (pq_getint(&tmp, argsize))
return EOF;
fcinfo.arg[i] = (Datum) tmp;
fcinfo.arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
}
else
{ /* by-reference ... */
......@@ -309,13 +344,9 @@ HandleFunctionRequest(void)
if (argsize < 0)
elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
argsize);
/* I suspect this +1 isn't really needed - tgl 5/2000 */
p = palloc(argsize + VARHDRSZ + 1); /* Added +1 to solve
* memory leak - Peter
* 98 Jan 6 */
p = palloc(argsize + VARHDRSZ);
VARATT_SIZEP(p) = argsize + VARHDRSZ;
if (pq_getbytes(VARDATA(p), argsize))
return EOF;
pq_copymsgbytes(msgBuf, VARDATA(p), argsize);
}
else
{ /* ... fixed */
......@@ -323,29 +354,12 @@ HandleFunctionRequest(void)
elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d",
argsize, fip->arglen[i]);
p = palloc(argsize + 1); /* +1 in case argsize is 0 */
if (pq_getbytes(p, argsize))
return EOF;
pq_copymsgbytes(msgBuf, p, argsize);
}
fcinfo.arg[i] = PointerGetDatum(p);
}
}
/*
* Now that we've eaten the input message, check to see if we actually
* want to do the function call or not.
*
* Currently, we report an error if in ABORT state, or return a dummy
* NULL response if fastpath support has been compiled out.
*/
if (IsAbortedTransactionBlockState())
elog(ERROR, "current transaction is aborted, "
"queries ignored until end of transaction block");
/* Check permission to call function */
aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, get_func_name(fid));
/*
* Set up a query snapshot in case function needs one. (It is not safe
* to do this if we are in transaction-abort state, so we have to postpone
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.321 2003/04/17 22:26:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.322 2003/04/19 00:02:29 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
......@@ -133,7 +133,9 @@ static const char *CreateCommandTag(Node *parsetree);
/* ----------------
* InteractiveBackend() is called for user interactive connections
* the string entered by the user is placed in its parameter inBuf.
*
* the string entered by the user is placed in its parameter inBuf,
* and we act like a Q message was received.
*
* EOF is returned if end-of-file input is seen; time to shut down.
* ----------------
......@@ -155,6 +157,7 @@ InteractiveBackend(StringInfo inBuf)
/* Reset inBuf to empty */
inBuf->len = 0;
inBuf->data[0] = '\0';
inBuf->cursor = 0;
for (;;)
{
......@@ -214,6 +217,9 @@ InteractiveBackend(StringInfo inBuf)
break;
}
/* Add '\0' to make it look the same as message case. */
appendStringInfoChar(inBuf, (char) '\0');
/*
* if the query echo flag was given, print the query..
*/
......@@ -227,66 +233,79 @@ InteractiveBackend(StringInfo inBuf)
/* ----------------
* SocketBackend() Is called for frontend-backend connections
*
* If the input is a query (case 'Q') then the string entered by
* the user is placed in its parameter inBuf.
*
* If the input is a fastpath function call (case 'F') then
* the function call is processed in HandleFunctionRequest()
* (now called from PostgresMain()).
* Returns the message type code, and loads message body data into inBuf.
*
* EOF is returned if the connection is lost.
* ----------------
*/
static int
SocketBackend(StringInfo inBuf)
{
int qtype;
/*
* get input from the frontend
* Get message type code from the frontend.
*/
qtype = pq_getbyte();
if (qtype == EOF) /* frontend disconnected */
{
elog(COMMERROR, "unexpected EOF on client connection");
return qtype;
}
/*
* Validate message type code before trying to read body; if we have
* lost sync, better to say "command unknown" than to run out of memory
* because we used garbage as a length word.
*/
switch (qtype)
{
case EOF:
/* frontend disconnected */
case 'Q': /* simple query */
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
{
/* old style without length word; convert */
if (pq_getstring(inBuf))
{
elog(COMMERROR, "unexpected EOF on client connection");
return EOF;
}
}
break;
/*
* 'Q': user entered a query
*/
case 'Q':
if (pq_getstr(inBuf))
return EOF;
case 'F': /* fastpath function call */
break;
/*
* 'F': calling user/system functions
*/
case 'F':
if (pq_getstr(inBuf))
return EOF; /* ignore "string" at start of F message */
case 'X': /* terminate */
break;
/*
* 'X': frontend is exiting
*/
case 'X':
case 'd': /* copy data */
case 'c': /* copy done */
case 'f': /* copy fail */
/* Accept but ignore these messages, per protocol spec */
break;
default:
/*
* otherwise we got garbage from the frontend.
*
* XXX are we certain that we want to do an elog(FATAL) here?
* -cim 1/24/90
* Otherwise we got garbage from the frontend. We treat this
* as fatal because we have probably lost message boundary sync,
* and there's no good way to recover.
*/
default:
elog(FATAL, "Socket command type %c unknown", qtype);
break;
}
/*
* In protocol version 3, all frontend messages have a length word
* next after the type code; we can read the message contents
* independently of the type.
*/
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
if (pq_getmessage(inBuf, 0))
return EOF; /* suitable message already logged */
}
return qtype;
}
......@@ -1220,19 +1239,17 @@ int
PostgresMain(int argc, char *argv[], const char *username)
{
int flag;
const char *DBName = NULL;
char *potential_DataDir = NULL;
bool secure;
int errs = 0;
int debug_flag = 0;
GucContext ctx;
GucSource gucsource;
char *tmp;
int firstchar;
StringInfo parser_input;
char *potential_DataDir = NULL;
bool send_rfq;
/*
* Catch standard options before doing much else. This even works on
......@@ -1815,7 +1832,7 @@ PostgresMain(int argc, char *argv[], const char *username)
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
puts("$Revision: 1.321 $ $Date: 2003/04/17 22:26:01 $\n");
puts("$Revision: 1.322 $ $Date: 2003/04/19 00:02:29 $\n");
}
/*
......@@ -1902,6 +1919,8 @@ PostgresMain(int argc, char *argv[], const char *username)
PG_SETMASK(&UnBlockSig);
send_rfq = true; /* initially, or after error */
/*
* Non-error queries loop here.
*/
......@@ -1922,7 +1941,11 @@ PostgresMain(int argc, char *argv[], const char *username)
*
* Note: this includes fflush()'ing the last of the prior output.
*/
ReadyForQuery(whereToSendOutput);
if (send_rfq)
{
ReadyForQuery(whereToSendOutput);
send_rfq = false;
}
/* ----------
* Tell the statistics collector what we've collected
......@@ -1986,20 +2009,36 @@ PostgresMain(int argc, char *argv[], const char *username)
*/
switch (firstchar)
{
case 'Q': /* simple query */
/*
* 'F' indicates a fastpath call.
*/
case 'F':
/* ----------
* Tell the collector what we're doing
* ----------
* Process the query string.
*
* Note: transaction command start/end is now done within
* pg_exec_query_string(), not here.
*/
if (log_statement_stats)
ResetUsage();
pgstat_report_activity(parser_input->data);
pg_exec_query_string(parser_input,
whereToSendOutput,
QueryContext);
if (log_statement_stats)
ShowUsage("QUERY STATISTICS");
send_rfq = true;
break;
case 'F': /* fastpath function call */
/* Tell the collector what we're doing */
pgstat_report_activity("<FASTPATH> function call");
/* start an xact for this function invocation */
start_xact_command();
if (HandleFunctionRequest() == EOF)
if (HandleFunctionRequest(parser_input) == EOF)
{
/* lost frontend connection during F message input */
......@@ -2015,29 +2054,8 @@ PostgresMain(int argc, char *argv[], const char *username)
/* commit the function-invocation transaction */
finish_xact_command(false);
break;
/*
* 'Q' indicates a user query
*/
case 'Q':
/*
* otherwise, process the input string.
*
* Note: transaction command start/end is now done within
* pg_exec_query_string(), not here.
*/
if (log_statement_stats)
ResetUsage();
pgstat_report_activity(parser_input->data);
pg_exec_query_string(parser_input,
whereToSendOutput,
QueryContext);
if (log_statement_stats)
ShowUsage("QUERY STATISTICS");
send_rfq = true;
break;
/*
......@@ -2064,8 +2082,18 @@ PostgresMain(int argc, char *argv[], const char *username)
*/
proc_exit(0);
case 'd': /* copy data */
case 'c': /* copy done */
case 'f': /* copy fail */
/*
* Accept but ignore these messages, per protocol spec;
* we probably got here because a COPY failed, and the
* frontend is still sending data.
*/
break;
default:
elog(ERROR, "unknown frontend message was received");
elog(FATAL, "Socket command type %c unknown", firstchar);
}
#ifdef MEMORY_CONTEXT_CHECKING
......
......@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: stringinfo.h,v 1.24 2002/06/20 20:29:49 momjian Exp $
* $Id: stringinfo.h,v 1.25 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -27,6 +27,9 @@
* string size (including the terminating '\0' char) that we can
* currently store in 'data' without having to reallocate
* more space. We must always have maxlen > len.
* cursor is initialized to zero by makeStringInfo or initStringInfo,
* but is not otherwise touched by the stringinfo.c routines.
* Some routines use it to scan through a StringInfo.
*-------------------------
*/
typedef struct StringInfoData
......@@ -34,6 +37,7 @@ typedef struct StringInfoData
char *data;
int len;
int maxlen;
int cursor;
} StringInfoData;
typedef StringInfoData *StringInfo;
......@@ -111,4 +115,10 @@ extern void appendStringInfoChar(StringInfo str, char ch);
extern void appendBinaryStringInfo(StringInfo str,
const char *data, int datalen);
/*------------------------
* enlargeStringInfo
* Make sure a StringInfo's buffer can hold at least 'needed' more bytes.
*/
extern void enlargeStringInfo(StringInfo str, int needed);
#endif /* STRINGINFO_H */
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq.h,v 1.56 2003/01/25 05:19:47 tgl Exp $
* $Id: libpq.h,v 1.57 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -52,14 +52,24 @@ extern void StreamClose(int sock);
extern void TouchSocketFile(void);
extern void pq_init(void);
extern int pq_getbytes(char *s, size_t len);
extern int pq_getstring(StringInfo s, int maxlen);
extern int pq_getstring(StringInfo s);
extern int pq_getmessage(StringInfo s, int maxlen);
extern int pq_getbyte(void);
extern int pq_peekbyte(void);
extern int pq_putbytes(const char *s, size_t len);
extern int pq_flush(void);
extern int pq_eof(void);
extern int pq_putmessage(char msgtype, const char *s, size_t len);
extern void pq_startcopyout(void);
extern void pq_endcopyout(bool errorAbort);
/*
* prototypes for functions in be-secure.c
*/
extern int secure_initialize(void);
extern void secure_destroy(void);
extern int secure_open_server(Port *port);
extern void secure_close(Port *port);
extern ssize_t secure_read(Port *port, void *ptr, size_t len);
extern ssize_t secure_write(Port *port, void *ptr, size_t len);
#endif /* LIBPQ_H */
......@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pqcomm.h,v 1.76 2003/04/17 22:26:01 tgl Exp $
* $Id: pqcomm.h,v 1.77 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -106,7 +106,7 @@ typedef union SockAddr
/* The earliest and latest frontend/backend protocol version supported. */
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0)
#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,100) /* XXX temporary value */
#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,101) /* XXX temporary value */
typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pqformat.h,v 1.13 2002/09/04 23:31:35 tgl Exp $
* $Id: pqformat.h,v 1.14 2003/04/19 00:02:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -26,9 +26,11 @@ extern void pq_endmessage(StringInfo buf);
extern int pq_puttextmessage(char msgtype, const char *str);
extern int pq_getint(int *result, int b);
extern int pq_getstr_bounded(StringInfo s, int maxlen);
#define pq_getstr(s) pq_getstr_bounded(s, 0)
extern int pq_getmsgbyte(StringInfo msg);
extern unsigned int pq_getmsgint(StringInfo msg, int b);
extern const char *pq_getmsgbytes(StringInfo msg, int datalen);
extern void pq_copymsgbytes(StringInfo msg, char *buf, int datalen);
extern const char *pq_getmsgstring(StringInfo msg);
extern void pq_getmsgend(StringInfo msg);
#endif /* PQFORMAT_H */
......@@ -44,7 +44,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: dest.h,v 1.33 2003/03/27 16:51:29 momjian Exp $
* $Id: dest.h,v 1.34 2003/04/19 00:02:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -102,8 +102,6 @@ extern void EndCommand(const char *commandTag, CommandDest dest);
/* Additional functions that go with destination management, more or less. */
extern void SendCopyBegin(void);
extern void ReceiveCopyBegin(void);
extern void NullCommand(CommandDest dest);
extern void ReadyForQuery(CommandDest dest);
......
......@@ -6,13 +6,15 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: fastpath.h,v 1.13 2002/06/20 20:29:52 momjian Exp $
* $Id: fastpath.h,v 1.14 2003/04/19 00:02:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef FASTPATH_H
#define FASTPATH_H
extern int HandleFunctionRequest(void);
#include "lib/stringinfo.h"
extern int HandleFunctionRequest(StringInfo msgBuf);
#endif /* FASTPATH_H */
......@@ -10,7 +10,7 @@
* exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.75 2003/04/17 22:26:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.76 2003/04/19 00:02:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -559,7 +559,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
default:
return STATUS_ERROR;
}
ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
if (areq == AUTH_REQ_MD5)
free(crypt_pwd);
return ret;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.232 2003/04/17 22:26:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.233 2003/04/19 00:02:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1820,11 +1820,11 @@ makeEmptyPGconn(void)
#endif
/*
* The output buffer size is set to 8K, which is the usual size of
* pipe buffers on Unix systems. That way, when we are sending a
* We try to send at least 8K at a time, which is the usual size
* of pipe buffers on Unix systems. That way, when we are sending a
* large amount of data, we avoid incurring extra kernel context swaps
* for partial bufferloads. Note that we currently don't ever enlarge
* the output buffer.
* for partial bufferloads. The output buffer is initially made 16K
* in size, and we try to dump it after accumulating 8K.
*
* With the same goal of minimizing context swaps, the input buffer will
* be enlarged anytime it has less than 8K free, so we initially
......@@ -1832,7 +1832,7 @@ makeEmptyPGconn(void)
*/
conn->inBufSize = 16 * 1024;
conn->inBuffer = (char *) malloc(conn->inBufSize);
conn->outBufSize = 8 * 1024;
conn->outBufSize = 16 * 1024;
conn->outBuffer = (char *) malloc(conn->outBufSize);
conn->nonblocking = FALSE;
initPQExpBuffer(&conn->errorMessage);
......@@ -1918,11 +1918,10 @@ closePGconn(PGconn *conn)
{
/*
* Try to send "close connection" message to backend. Ignore any
* error. Note: this routine used to go to substantial lengths to
* avoid getting SIGPIPE'd if the connection were already closed.
* Now we rely on pqFlush to avoid the signal.
* error.
*/
pqPutc('X', conn);
pqPutMsgStart('X', conn);
pqPutMsgEnd(conn);
pqFlush(conn);
}
......@@ -2152,7 +2151,7 @@ cancel_errReturn:
/*
* pqPacketSend() -- send a single-packet message.
* pqPacketSend() -- convenience routine to send a message to server.
*
* pack_type: the single-byte message type code. (Pass zero for startup
* packets, which have no message type code.)
......@@ -2167,19 +2166,18 @@ int
pqPacketSend(PGconn *conn, char pack_type,
const void *buf, size_t buf_len)
{
/* Send the message type. */
if (pack_type != 0)
if (pqPutc(pack_type, conn))
return STATUS_ERROR;
/* Send the (self-inclusive) message length word. */
if (pqPutInt(buf_len + 4, 4, conn))
/* Start the message. */
if (pqPutMsgStart(pack_type, conn))
return STATUS_ERROR;
/* Send the message body. */
if (pqPutnchar(buf, buf_len, conn))
return STATUS_ERROR;
/* Finish the message. */
if (pqPutMsgEnd(conn))
return STATUS_ERROR;
/* Flush to ensure backend gets it. */
if (pqFlush(conn))
return STATUS_ERROR;
......@@ -2624,7 +2622,7 @@ build_startup_packet(const PGconn *conn, char *packet)
packet_len += sizeof(ProtocolVersion);
/* Add user name, database name, options */
if (conn->pguser)
if (conn->pguser && conn->pguser[0])
{
if (packet)
strcpy(packet + packet_len, "user");
......@@ -2633,7 +2631,7 @@ build_startup_packet(const PGconn *conn, char *packet)
strcpy(packet + packet_len, conn->pguser);
packet_len += strlen(conn->pguser) + 1;
}
if (conn->dbName)
if (conn->dbName && conn->dbName[0])
{
if (packet)
strcpy(packet + packet_len, "database");
......@@ -2642,7 +2640,7 @@ build_startup_packet(const PGconn *conn, char *packet)
strcpy(packet + packet_len, conn->dbName);
packet_len += strlen(conn->dbName) + 1;
}
if (conn->pgoptions)
if (conn->pgoptions && conn->pgoptions[0])
{
if (packet)
strcpy(packet + packet_len, "options");
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.128 2003/03/25 02:44:36 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.129 2003/04/19 00:02:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -734,7 +734,6 @@ addTuple(PGresult *res, PGresAttValue * tup)
* Returns: 1 if successfully submitted
* 0 if error (conn->errorMessage is set)
*/
int
PQsendQuery(PGconn *conn, const char *query)
{
......@@ -770,51 +769,24 @@ PQsendQuery(PGconn *conn, const char *query)
conn->result = NULL;
conn->curTuple = NULL;
/* send the query to the backend; */
/* construct the outgoing Query message */
if (pqPutMsgStart('Q', conn) < 0 ||
pqPuts(query, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
{
handleSendFailure(conn);
return 0;
}
/*
* in order to guarantee that we don't send a partial query where we
* would become out of sync with the backend and/or block during a
* non-blocking connection we must first flush the send buffer before
* sending more data
*
* an alternative is to implement 'queue reservations' where we are able
* to roll up a transaction (the 'Q' along with our query) and make
* sure we have enough space for it all in the send buffer.
* Give the data a push. In nonblock mode, don't complain if we're
* unable to send it all; PQconsumeInput() will do any additional flushing
* needed.
*/
if (pqIsnonblocking(conn))
if (pqFlush(conn) < 0)
{
/*
* the buffer must have emptied completely before we allow a new
* query to be buffered
*/
if (pqFlush(conn))
return 0;
/* 'Q' == queries */
/* XXX: if we fail here we really ought to not block */
if (pqPutc('Q', conn) != 0 || pqPuts(query, conn) != 0)
{
handleSendFailure(conn);
return 0;
}
/*
* give the data a push, ignore the return value as ConsumeInput()
* will do any additional flushing if needed
*/
pqFlush(conn);
}
else
{
/*
* the frontend-backend protocol uses 'Q' to designate queries
*/
if (pqPutc('Q', conn) != 0 || pqPuts(query, conn) != 0 ||
pqFlush(conn) != 0)
{
handleSendFailure(conn);
return 0;
}
handleSendFailure(conn);
return 0;
}
/* OK, it's launched! */
......@@ -830,7 +802,6 @@ PQsendQuery(PGconn *conn, const char *query)
*
* NOTE: this routine should only be called in PGASYNC_IDLE state.
*/
static void
handleSendFailure(PGconn *conn)
{
......@@ -854,13 +825,23 @@ handleSendFailure(PGconn *conn)
* 0 return: some kind of trouble
* 1 return: no problem
*/
int
PQconsumeInput(PGconn *conn)
{
if (!conn)
return 0;
/*
* for non-blocking connections try to flush the send-queue,
* otherwise we may never get a response for something that may
* not have already been sent because it's in our write buffer!
*/
if (pqIsnonblocking(conn))
{
if (pqFlush(conn) < 0)
return 0;
}
/*
* Load more data, if available. We do this no matter what state we
* are in, since we are probably getting called because the
......@@ -868,16 +849,8 @@ PQconsumeInput(PGconn *conn)
* we will NOT block waiting for more input.
*/
if (pqReadData(conn) < 0)
{
/*
* for non-blocking connections try to flush the send-queue
* otherwise we may never get a responce for something that may
* not have already been sent because it's in our write buffer!
*/
if (pqIsnonblocking(conn))
(void) pqFlush(conn);
return 0;
}
/* Parsing of the data waits till later. */
return 1;
}
......@@ -1733,14 +1706,13 @@ PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
* PQputline -- sends a string to the backend.
* Returns 0 if OK, EOF if not.
*
* Chiefly here so that applications can use "COPY <rel> from stdin".
* This exists to support "COPY <rel> from stdin". The backend will ignore
* the string if not doing COPY.
*/
int
PQputline(PGconn *conn, const char *s)
{
if (!conn || conn->sock < 0)
return EOF;
return pqPutnchar(s, strlen(s), conn);
return PQputnbytes(conn, s, strlen(s));
}
/*
......@@ -1752,7 +1724,14 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
{
if (!conn || conn->sock < 0)
return EOF;
return pqPutnchar(buffer, nbytes, conn);
if (nbytes > 0)
{
if (pqPutMsgStart('d', conn) < 0 ||
pqPutnchar(buffer, nbytes, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
return EOF;
}
return 0;
}
/*
......@@ -1780,6 +1759,14 @@ PQendcopy(PGconn *conn)
return 1;
}
/* Send the CopyDone message if needed */
if (conn->asyncStatus == PGASYNC_COPY_IN)
{
if (pqPutMsgStart('c', conn) < 0 ||
pqPutMsgEnd(conn) < 0)
return 1;
}
/*
* make sure no data is waiting to be sent, abort if we are
* non-blocking and the flush fails
......@@ -1884,9 +1871,10 @@ PQfn(PGconn *conn,
return NULL;
}
if (pqPuts("F ", conn) != 0 || /* function */
pqPutInt(fnid, 4, conn) != 0 || /* function id */
pqPutInt(nargs, 4, conn) != 0) /* # of args */
if (pqPutMsgStart('F', conn) < 0 || /* function call msg */
pqPuts("", conn) < 0 || /* useless string */
pqPutInt(fnid, 4, conn) < 0 || /* function id */
pqPutInt(nargs, 4, conn) < 0) /* # of args */
{
handleSendFailure(conn);
return NULL;
......@@ -1917,7 +1905,9 @@ PQfn(PGconn *conn,
}
}
}
if (pqFlush(conn))
if (pqPutMsgEnd(conn) < 0 ||
pqFlush(conn))
{
handleSendFailure(conn);
return NULL;
......@@ -2409,7 +2399,6 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
int
PQsetnonblocking(PGconn *conn, int arg)
{
arg = (arg == TRUE) ? 1 : 0;
/* early out if the socket is already in the state requested */
if (arg == conn->nonblocking)
......@@ -2437,7 +2426,6 @@ PQsetnonblocking(PGconn *conn, int arg)
int
PQisnonblocking(const PGconn *conn)
{
return (pqIsnonblocking(conn));
}
......@@ -2445,18 +2433,9 @@ PQisnonblocking(const PGconn *conn)
int
PQflush(PGconn *conn)
{
return (pqFlush(conn));
}
/* try to force data out, really only useful for non-blocking users.
* This implementation actually works for non-blocking connections */
int
PQsendSome(PGconn *conn)
{
return pqSendSome(conn);
}
/*
* PQfreeNotify - free's the memory associated with a PGnotify
*
......@@ -2473,5 +2452,3 @@ PQfreeNotify(PGnotify *notify)
{
PQfreemem(notify);
}
This diff is collapsed.
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-fe.h,v 1.91 2003/03/25 02:44:36 momjian Exp $
* $Id: libpq-fe.h,v 1.92 2003/04/19 00:02:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -284,7 +284,6 @@ extern int PQisnonblocking(const PGconn *conn);
/* Force the write buffer to be written (or at least try) */
extern int PQflush(PGconn *conn);
extern int PQsendSome(PGconn *conn);
/*
* "Fast path" interface --- not really recommended for application
......
......@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-int.h,v 1.61 2003/04/17 22:26:02 tgl Exp $
* $Id: libpq-int.h,v 1.62 2003/04/19 00:02:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -56,7 +56,7 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast
* pqcomm.h describe what the backend knows, not what libpq knows.
*/
#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,100) /* XXX temporary value */
#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,101) /* XXX temporary value */
/*
* POSTGRES backend dependent Constants.
......@@ -266,6 +266,10 @@ struct pg_conn
int outBufSize; /* allocated size of buffer */
int outCount; /* number of chars waiting in buffer */
/* State for constructing messages in outBuffer */
int outMsgStart; /* offset to msg start (length word) */
int outMsgEnd; /* offset to msg end (so far) */
/* Status for asynchronous result construction */
PGresult *result; /* result being constructed */
PGresAttValue *curTuple; /* tuple currently being read */
......@@ -334,9 +338,10 @@ extern int pqGetnchar(char *s, size_t len, PGconn *conn);
extern int pqPutnchar(const char *s, size_t len, PGconn *conn);
extern int pqGetInt(int *result, size_t bytes, PGconn *conn);
extern int pqPutInt(int value, size_t bytes, PGconn *conn);
extern int pqPutMsgStart(char msg_type, PGconn *conn);
extern int pqPutMsgEnd(PGconn *conn);
extern int pqReadData(PGconn *conn);
extern int pqFlush(PGconn *conn);
extern int pqSendSome(PGconn *conn);
extern int pqWait(int forRead, int forWrite, PGconn *conn);
extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn,
time_t finish_time);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment