mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Second round of FE/BE protocol changes. Frontend->backend messages now
have length counts, and COPY IN data is packetized into messages.
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$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>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!-- $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 & 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 & 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 & B)
|
||||
CopyData (F & B)
|
||||
</Term>
|
||||
<ListItem>
|
||||
<Para>
|
||||
@ -2089,7 +2012,7 @@ CopyDataRow (F & 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 & 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>
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.191 2003/04/04 20:42:11 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.192 2003/04/19 00:02:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -31,6 +31,7 @@
|
||||
#include "commands/trigger.h"
|
||||
#include "executor/executor.h"
|
||||
#include "libpq/libpq.h"
|
||||
#include "libpq/pqformat.h"
|
||||
#include "mb/pg_wchar.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
@ -49,6 +50,17 @@
|
||||
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
|
||||
#define OCTVALUE(c) ((c) - '0')
|
||||
|
||||
/*
|
||||
* Represents the different source/dest cases we need to worry about at
|
||||
* the bottom level
|
||||
*/
|
||||
typedef enum CopyDest
|
||||
{
|
||||
COPY_FILE, /* to/from file */
|
||||
COPY_OLD_FE, /* to/from frontend (old protocol) */
|
||||
COPY_NEW_FE /* to/from frontend (new protocol) */
|
||||
} CopyDest;
|
||||
|
||||
/*
|
||||
* Represents the type of data returned by CopyReadAttribute()
|
||||
*/
|
||||
@ -61,13 +73,13 @@ typedef enum CopyReadResult
|
||||
|
||||
/* non-export function prototypes */
|
||||
static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
FILE *fp, char *delim, char *null_print);
|
||||
char *delim, char *null_print);
|
||||
static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
FILE *fp, char *delim, char *null_print);
|
||||
char *delim, char *null_print);
|
||||
static Oid GetInputFunction(Oid type);
|
||||
static Oid GetTypeElement(Oid type);
|
||||
static char *CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result);
|
||||
static void CopyAttributeOut(FILE *fp, char *string, char *delim);
|
||||
static char *CopyReadAttribute(const char *delim, CopyReadResult *result);
|
||||
static void CopyAttributeOut(char *string, char *delim);
|
||||
static List *CopyGetAttnums(Relation rel, List *attnamelist);
|
||||
|
||||
static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
|
||||
@ -77,7 +89,11 @@ static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
|
||||
* never been reentrant...
|
||||
*/
|
||||
int copy_lineno = 0; /* exported for use by elog() -- dz */
|
||||
static bool fe_eof;
|
||||
|
||||
static CopyDest copy_dest;
|
||||
static FILE *copy_file; /* if copy_dest == COPY_FILE */
|
||||
static StringInfo copy_msgbuf; /* if copy_dest == COPY_NEW_FE */
|
||||
static bool fe_eof; /* true if detected end of copy data */
|
||||
|
||||
/*
|
||||
* These static variables are used to avoid incurring overhead for each
|
||||
@ -96,98 +112,229 @@ static int server_encoding;
|
||||
/*
|
||||
* Internal communications functions
|
||||
*/
|
||||
static void CopySendData(void *databuf, int datasize, FILE *fp);
|
||||
static void CopySendString(const char *str, FILE *fp);
|
||||
static void CopySendChar(char c, FILE *fp);
|
||||
static void CopyGetData(void *databuf, int datasize, FILE *fp);
|
||||
static int CopyGetChar(FILE *fp);
|
||||
static int CopyGetEof(FILE *fp);
|
||||
static int CopyPeekChar(FILE *fp);
|
||||
static void CopyDonePeek(FILE *fp, int c, bool pickup);
|
||||
static void SendCopyBegin(bool binary);
|
||||
static void ReceiveCopyBegin(bool binary);
|
||||
static void SendCopyEnd(bool binary);
|
||||
static void CopySendData(void *databuf, int datasize);
|
||||
static void CopySendString(const char *str);
|
||||
static void CopySendChar(char c);
|
||||
static void CopyGetData(void *databuf, int datasize);
|
||||
static int CopyGetChar(void);
|
||||
#define CopyGetEof() (fe_eof)
|
||||
static int CopyPeekChar(void);
|
||||
static void CopyDonePeek(int c, bool pickup);
|
||||
|
||||
/*
|
||||
* CopySendData sends output data either to the file
|
||||
* specified by fp or, if fp is NULL, using the standard
|
||||
* backend->frontend functions
|
||||
*
|
||||
* Send copy start/stop messages for frontend copies. These have changed
|
||||
* in past protocol redesigns.
|
||||
*/
|
||||
static void
|
||||
SendCopyBegin(bool binary)
|
||||
{
|
||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
||||
{
|
||||
pq_putbytes("H", 1); /* new way */
|
||||
/* XXX grottiness needed for old protocol */
|
||||
pq_startcopyout();
|
||||
copy_dest = COPY_NEW_FE;
|
||||
}
|
||||
else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
||||
{
|
||||
pq_putbytes("H", 1); /* old way */
|
||||
/* grottiness needed for old protocol */
|
||||
pq_startcopyout();
|
||||
copy_dest = COPY_OLD_FE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pq_putbytes("B", 1); /* very old way */
|
||||
/* grottiness needed for old protocol */
|
||||
pq_startcopyout();
|
||||
copy_dest = COPY_OLD_FE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ReceiveCopyBegin(bool binary)
|
||||
{
|
||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
||||
{
|
||||
pq_putbytes("G", 1); /* new way */
|
||||
copy_dest = COPY_NEW_FE;
|
||||
copy_msgbuf = makeStringInfo();
|
||||
}
|
||||
else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
||||
{
|
||||
pq_putbytes("G", 1); /* old way */
|
||||
copy_dest = COPY_OLD_FE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pq_putbytes("D", 1); /* very old way */
|
||||
copy_dest = COPY_OLD_FE;
|
||||
}
|
||||
/* We *must* flush here to ensure FE knows it can send. */
|
||||
pq_flush();
|
||||
}
|
||||
|
||||
static void
|
||||
SendCopyEnd(bool binary)
|
||||
{
|
||||
if (!binary)
|
||||
CopySendData("\\.\n", 3);
|
||||
pq_endcopyout(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* CopySendData sends output data to the destination (file or frontend)
|
||||
* CopySendString does the same for null-terminated strings
|
||||
* CopySendChar does the same for single characters
|
||||
*
|
||||
* NB: no data conversion is applied by these functions
|
||||
*/
|
||||
static void
|
||||
CopySendData(void *databuf, int datasize, FILE *fp)
|
||||
CopySendData(void *databuf, int datasize)
|
||||
{
|
||||
if (!fp)
|
||||
switch (copy_dest)
|
||||
{
|
||||
case COPY_FILE:
|
||||
fwrite(databuf, datasize, 1, copy_file);
|
||||
if (ferror(copy_file))
|
||||
elog(ERROR, "CopySendData: %m");
|
||||
break;
|
||||
case COPY_OLD_FE:
|
||||
if (pq_putbytes((char *) databuf, datasize))
|
||||
fe_eof = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fwrite(databuf, datasize, 1, fp);
|
||||
if (ferror(fp))
|
||||
elog(ERROR, "CopySendData: %m");
|
||||
break;
|
||||
case COPY_NEW_FE:
|
||||
/* XXX fix later */
|
||||
if (pq_putbytes((char *) databuf, datasize))
|
||||
fe_eof = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
CopySendString(const char *str, FILE *fp)
|
||||
CopySendString(const char *str)
|
||||
{
|
||||
CopySendData((void *) str, strlen(str), fp);
|
||||
CopySendData((void *) str, strlen(str));
|
||||
}
|
||||
|
||||
static void
|
||||
CopySendChar(char c, FILE *fp)
|
||||
CopySendChar(char c)
|
||||
{
|
||||
CopySendData(&c, 1, fp);
|
||||
CopySendData(&c, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* CopyGetData reads output data either from the file
|
||||
* specified by fp or, if fp is NULL, using the standard
|
||||
* backend->frontend functions
|
||||
*
|
||||
* CopyGetData reads data from the source (file or frontend)
|
||||
* CopyGetChar does the same for single characters
|
||||
* CopyGetEof checks if it's EOF on the input (or, check for EOF result
|
||||
* from CopyGetChar)
|
||||
*
|
||||
* CopyGetEof checks if EOF was detected by previous Get operation.
|
||||
*
|
||||
* Note: when copying from the frontend, we expect a proper EOF mark per
|
||||
* protocol; if the frontend simply drops the connection, we raise error.
|
||||
* It seems unwise to allow the COPY IN to complete normally in that case.
|
||||
*
|
||||
* NB: no data conversion is applied by these functions
|
||||
*/
|
||||
static void
|
||||
CopyGetData(void *databuf, int datasize, FILE *fp)
|
||||
CopyGetData(void *databuf, int datasize)
|
||||
{
|
||||
if (!fp)
|
||||
switch (copy_dest)
|
||||
{
|
||||
if (pq_getbytes((char *) databuf, datasize))
|
||||
case COPY_FILE:
|
||||
fread(databuf, datasize, 1, copy_file);
|
||||
if (feof(copy_file))
|
||||
fe_eof = true;
|
||||
break;
|
||||
case COPY_OLD_FE:
|
||||
if (pq_getbytes((char *) databuf, datasize))
|
||||
{
|
||||
/* Only a \. terminator is legal EOF in old protocol */
|
||||
elog(ERROR, "unexpected EOF on client connection");
|
||||
}
|
||||
break;
|
||||
case COPY_NEW_FE:
|
||||
while (datasize > 0 && !fe_eof)
|
||||
{
|
||||
int avail;
|
||||
|
||||
while (copy_msgbuf->cursor >= copy_msgbuf->len)
|
||||
{
|
||||
/* Try to receive another message */
|
||||
int mtype;
|
||||
|
||||
mtype = pq_getbyte();
|
||||
if (mtype == EOF)
|
||||
elog(ERROR, "unexpected EOF on client connection");
|
||||
if (pq_getmessage(copy_msgbuf, 0))
|
||||
elog(ERROR, "unexpected EOF on client connection");
|
||||
switch (mtype)
|
||||
{
|
||||
case 'd': /* CopyData */
|
||||
break;
|
||||
case 'c': /* CopyDone */
|
||||
/* COPY IN correctly terminated by frontend */
|
||||
fe_eof = true;
|
||||
return;
|
||||
case 'f': /* CopyFail */
|
||||
elog(ERROR, "COPY IN failed: %s",
|
||||
pq_getmsgstring(copy_msgbuf));
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unexpected message type %c during COPY IN",
|
||||
mtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
avail = copy_msgbuf->len - copy_msgbuf->cursor;
|
||||
if (avail > datasize)
|
||||
avail = datasize;
|
||||
pq_copymsgbytes(copy_msgbuf, databuf, avail);
|
||||
databuf = (void *) ((char *) databuf + avail);
|
||||
datasize =- avail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
fread(databuf, datasize, 1, fp);
|
||||
}
|
||||
|
||||
static int
|
||||
CopyGetChar(FILE *fp)
|
||||
CopyGetChar(void)
|
||||
{
|
||||
if (!fp)
|
||||
{
|
||||
int ch = pq_getbyte();
|
||||
int ch;
|
||||
|
||||
switch (copy_dest)
|
||||
{
|
||||
case COPY_FILE:
|
||||
ch = getc(copy_file);
|
||||
break;
|
||||
case COPY_OLD_FE:
|
||||
ch = pq_getbyte();
|
||||
if (ch == EOF)
|
||||
{
|
||||
/* Only a \. terminator is legal EOF in old protocol */
|
||||
elog(ERROR, "unexpected EOF on client connection");
|
||||
}
|
||||
break;
|
||||
case COPY_NEW_FE:
|
||||
{
|
||||
unsigned char cc;
|
||||
|
||||
CopyGetData(&cc, 1);
|
||||
if (fe_eof)
|
||||
ch = EOF;
|
||||
else
|
||||
ch = cc;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ch = EOF;
|
||||
break;
|
||||
}
|
||||
if (ch == EOF)
|
||||
fe_eof = true;
|
||||
return ch;
|
||||
}
|
||||
else
|
||||
return getc(fp);
|
||||
}
|
||||
|
||||
static int
|
||||
CopyGetEof(FILE *fp)
|
||||
{
|
||||
if (!fp)
|
||||
return fe_eof;
|
||||
else
|
||||
return feof(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -200,40 +347,74 @@ CopyGetEof(FILE *fp)
|
||||
* (if pickup is true) or leave it on the stream (if pickup is false).
|
||||
*/
|
||||
static int
|
||||
CopyPeekChar(FILE *fp)
|
||||
CopyPeekChar(void)
|
||||
{
|
||||
if (!fp)
|
||||
{
|
||||
int ch = pq_peekbyte();
|
||||
int ch;
|
||||
|
||||
switch (copy_dest)
|
||||
{
|
||||
case COPY_FILE:
|
||||
ch = getc(copy_file);
|
||||
break;
|
||||
case COPY_OLD_FE:
|
||||
ch = pq_peekbyte();
|
||||
if (ch == EOF)
|
||||
{
|
||||
/* Only a \. terminator is legal EOF in old protocol */
|
||||
elog(ERROR, "unexpected EOF on client connection");
|
||||
}
|
||||
break;
|
||||
case COPY_NEW_FE:
|
||||
{
|
||||
unsigned char cc;
|
||||
|
||||
CopyGetData(&cc, 1);
|
||||
if (fe_eof)
|
||||
ch = EOF;
|
||||
else
|
||||
ch = cc;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ch = EOF;
|
||||
break;
|
||||
}
|
||||
if (ch == EOF)
|
||||
fe_eof = true;
|
||||
return ch;
|
||||
}
|
||||
else
|
||||
return getc(fp);
|
||||
}
|
||||
|
||||
static void
|
||||
CopyDonePeek(FILE *fp, int c, bool pickup)
|
||||
CopyDonePeek(int c, bool pickup)
|
||||
{
|
||||
if (!fp)
|
||||
if (fe_eof)
|
||||
return; /* can't unget an EOF */
|
||||
switch (copy_dest)
|
||||
{
|
||||
case COPY_FILE:
|
||||
if (!pickup)
|
||||
{
|
||||
/* We don't want to pick it up - so put it back in there */
|
||||
ungetc(c, copy_file);
|
||||
}
|
||||
/* If we wanted to pick it up, it's already done */
|
||||
break;
|
||||
case COPY_OLD_FE:
|
||||
if (pickup)
|
||||
{
|
||||
/* We want to pick it up */
|
||||
(void) pq_getbyte();
|
||||
}
|
||||
/* If we didn't want to pick it up, just leave it where it sits */
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
case COPY_NEW_FE:
|
||||
if (!pickup)
|
||||
{
|
||||
/* We don't want to pick it up - so put it back in there */
|
||||
ungetc(c, fp);
|
||||
copy_msgbuf->cursor--;
|
||||
}
|
||||
/* If we wanted to pick it up, it's already done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,7 +468,6 @@ DoCopy(const CopyStmt *stmt)
|
||||
bool oids = false;
|
||||
char *delim = NULL;
|
||||
char *null_print = NULL;
|
||||
FILE *fp;
|
||||
Relation rel;
|
||||
AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT);
|
||||
AclResult aclresult;
|
||||
@ -397,6 +577,11 @@ DoCopy(const CopyStmt *stmt)
|
||||
client_encoding = pg_get_client_encoding();
|
||||
server_encoding = GetDatabaseEncoding();
|
||||
|
||||
copy_dest = COPY_FILE; /* default */
|
||||
copy_file = NULL;
|
||||
copy_msgbuf = NULL;
|
||||
fe_eof = false;
|
||||
|
||||
if (is_from)
|
||||
{ /* copy from file to database */
|
||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||
@ -414,33 +599,30 @@ DoCopy(const CopyStmt *stmt)
|
||||
if (pipe)
|
||||
{
|
||||
if (IsUnderPostmaster)
|
||||
{
|
||||
ReceiveCopyBegin();
|
||||
fp = NULL;
|
||||
}
|
||||
ReceiveCopyBegin(binary);
|
||||
else
|
||||
fp = stdin;
|
||||
copy_file = stdin;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
fp = AllocateFile(filename, PG_BINARY_R);
|
||||
copy_file = AllocateFile(filename, PG_BINARY_R);
|
||||
|
||||
if (fp == NULL)
|
||||
if (copy_file == NULL)
|
||||
elog(ERROR, "COPY command, running in backend with "
|
||||
"effective uid %d, could not open file '%s' for "
|
||||
"reading. Errno = %s (%d).",
|
||||
(int) geteuid(), filename, strerror(errno), errno);
|
||||
|
||||
fstat(fileno(fp), &st);
|
||||
fstat(fileno(copy_file), &st);
|
||||
if (S_ISDIR(st.st_mode))
|
||||
{
|
||||
FreeFile(fp);
|
||||
FreeFile(copy_file);
|
||||
elog(ERROR, "COPY: %s is a directory", filename);
|
||||
}
|
||||
}
|
||||
CopyFrom(rel, attnumlist, binary, oids, fp, delim, null_print);
|
||||
CopyFrom(rel, attnumlist, binary, oids, delim, null_print);
|
||||
}
|
||||
else
|
||||
{ /* copy from database to file */
|
||||
@ -459,13 +641,9 @@ DoCopy(const CopyStmt *stmt)
|
||||
if (pipe)
|
||||
{
|
||||
if (IsUnderPostmaster)
|
||||
{
|
||||
SendCopyBegin();
|
||||
pq_startcopyout();
|
||||
fp = NULL;
|
||||
}
|
||||
SendCopyBegin(binary);
|
||||
else
|
||||
fp = stdout;
|
||||
copy_file = stdout;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -481,33 +659,28 @@ DoCopy(const CopyStmt *stmt)
|
||||
" COPY command");
|
||||
|
||||
oumask = umask((mode_t) 022);
|
||||
fp = AllocateFile(filename, PG_BINARY_W);
|
||||
copy_file = AllocateFile(filename, PG_BINARY_W);
|
||||
umask(oumask);
|
||||
|
||||
if (fp == NULL)
|
||||
if (copy_file == NULL)
|
||||
elog(ERROR, "COPY command, running in backend with "
|
||||
"effective uid %d, could not open file '%s' for "
|
||||
"writing. Errno = %s (%d).",
|
||||
(int) geteuid(), filename, strerror(errno), errno);
|
||||
fstat(fileno(fp), &st);
|
||||
fstat(fileno(copy_file), &st);
|
||||
if (S_ISDIR(st.st_mode))
|
||||
{
|
||||
FreeFile(fp);
|
||||
FreeFile(copy_file);
|
||||
elog(ERROR, "COPY: %s is a directory", filename);
|
||||
}
|
||||
}
|
||||
CopyTo(rel, attnumlist, binary, oids, fp, delim, null_print);
|
||||
CopyTo(rel, attnumlist, binary, oids, delim, null_print);
|
||||
}
|
||||
|
||||
if (!pipe)
|
||||
FreeFile(fp);
|
||||
else if (!is_from)
|
||||
{
|
||||
if (!binary)
|
||||
CopySendData("\\.\n", 3, fp);
|
||||
if (IsUnderPostmaster)
|
||||
pq_endcopyout(false);
|
||||
}
|
||||
FreeFile(copy_file);
|
||||
else if (IsUnderPostmaster && !is_from)
|
||||
SendCopyEnd(binary);
|
||||
pfree(attribute_buf.data);
|
||||
|
||||
/*
|
||||
@ -525,7 +698,7 @@ DoCopy(const CopyStmt *stmt)
|
||||
*/
|
||||
static void
|
||||
CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
FILE *fp, char *delim, char *null_print)
|
||||
char *delim, char *null_print)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
TupleDesc tupDesc;
|
||||
@ -589,18 +762,18 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
int32 tmp;
|
||||
|
||||
/* Signature */
|
||||
CopySendData((char *) BinarySignature, 12, fp);
|
||||
CopySendData((char *) BinarySignature, 12);
|
||||
/* Integer layout field */
|
||||
tmp = 0x01020304;
|
||||
CopySendData(&tmp, sizeof(int32), fp);
|
||||
CopySendData(&tmp, sizeof(int32));
|
||||
/* Flags field */
|
||||
tmp = 0;
|
||||
if (oids)
|
||||
tmp |= (1 << 16);
|
||||
CopySendData(&tmp, sizeof(int32), fp);
|
||||
CopySendData(&tmp, sizeof(int32));
|
||||
/* No header extension */
|
||||
tmp = 0;
|
||||
CopySendData(&tmp, sizeof(int32), fp);
|
||||
CopySendData(&tmp, sizeof(int32));
|
||||
}
|
||||
|
||||
mySnapshot = CopyQuerySnapshot();
|
||||
@ -621,15 +794,15 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
/* Binary per-tuple header */
|
||||
int16 fld_count = attr_count;
|
||||
|
||||
CopySendData(&fld_count, sizeof(int16), fp);
|
||||
CopySendData(&fld_count, sizeof(int16));
|
||||
/* Send OID if wanted --- note fld_count doesn't include it */
|
||||
if (oids)
|
||||
{
|
||||
Oid oid = HeapTupleGetOid(tuple);
|
||||
|
||||
fld_size = sizeof(Oid);
|
||||
CopySendData(&fld_size, sizeof(int16), fp);
|
||||
CopySendData(&oid, sizeof(Oid), fp);
|
||||
CopySendData(&fld_size, sizeof(int16));
|
||||
CopySendData(&oid, sizeof(Oid));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -639,7 +812,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
{
|
||||
string = DatumGetCString(DirectFunctionCall1(oidout,
|
||||
ObjectIdGetDatum(HeapTupleGetOid(tuple))));
|
||||
CopySendString(string, fp);
|
||||
CopySendString(string);
|
||||
need_delim = true;
|
||||
}
|
||||
}
|
||||
@ -655,7 +828,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
if (!binary)
|
||||
{
|
||||
if (need_delim)
|
||||
CopySendChar(delim[0], fp);
|
||||
CopySendChar(delim[0]);
|
||||
need_delim = true;
|
||||
}
|
||||
|
||||
@ -663,12 +836,12 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
{
|
||||
if (!binary)
|
||||
{
|
||||
CopySendString(null_print, fp); /* null indicator */
|
||||
CopySendString(null_print); /* null indicator */
|
||||
}
|
||||
else
|
||||
{
|
||||
fld_size = 0; /* null marker */
|
||||
CopySendData(&fld_size, sizeof(int16), fp);
|
||||
CopySendData(&fld_size, sizeof(int16));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -679,12 +852,12 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
value,
|
||||
ObjectIdGetDatum(elements[attnum - 1]),
|
||||
Int32GetDatum(attr[attnum - 1]->atttypmod)));
|
||||
CopyAttributeOut(fp, string, delim);
|
||||
CopyAttributeOut(string, delim);
|
||||
}
|
||||
else
|
||||
{
|
||||
fld_size = attr[attnum - 1]->attlen;
|
||||
CopySendData(&fld_size, sizeof(int16), fp);
|
||||
CopySendData(&fld_size, sizeof(int16));
|
||||
if (isvarlena[attnum - 1])
|
||||
{
|
||||
/* varlena */
|
||||
@ -694,16 +867,14 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
value = PointerGetDatum(PG_DETOAST_DATUM(value));
|
||||
|
||||
CopySendData(DatumGetPointer(value),
|
||||
VARSIZE(value),
|
||||
fp);
|
||||
VARSIZE(value));
|
||||
}
|
||||
else if (!attr[attnum - 1]->attbyval)
|
||||
{
|
||||
/* fixed-length pass-by-reference */
|
||||
Assert(fld_size > 0);
|
||||
CopySendData(DatumGetPointer(value),
|
||||
fld_size,
|
||||
fp);
|
||||
fld_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -717,15 +888,14 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
*/
|
||||
store_att_byval(&datumBuf, value, fld_size);
|
||||
CopySendData(&datumBuf,
|
||||
fld_size,
|
||||
fp);
|
||||
fld_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!binary)
|
||||
CopySendChar('\n', fp);
|
||||
CopySendChar('\n');
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
@ -737,7 +907,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
/* Generate trailer for a binary copy */
|
||||
int16 fld_count = -1;
|
||||
|
||||
CopySendData(&fld_count, sizeof(int16), fp);
|
||||
CopySendData(&fld_count, sizeof(int16));
|
||||
}
|
||||
|
||||
MemoryContextDelete(mycontext);
|
||||
@ -753,7 +923,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
*/
|
||||
static void
|
||||
CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
FILE *fp, char *delim, char *null_print)
|
||||
char *delim, char *null_print)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
TupleDesc tupDesc;
|
||||
@ -905,30 +1075,30 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
int32 tmp;
|
||||
|
||||
/* Signature */
|
||||
CopyGetData(readSig, 12, fp);
|
||||
if (CopyGetEof(fp) || memcmp(readSig, BinarySignature, 12) != 0)
|
||||
CopyGetData(readSig, 12);
|
||||
if (CopyGetEof() || memcmp(readSig, BinarySignature, 12) != 0)
|
||||
elog(ERROR, "COPY BINARY: file signature not recognized");
|
||||
/* Integer layout field */
|
||||
CopyGetData(&tmp, sizeof(int32), fp);
|
||||
if (CopyGetEof(fp) || tmp != 0x01020304)
|
||||
CopyGetData(&tmp, sizeof(int32));
|
||||
if (CopyGetEof() || tmp != 0x01020304)
|
||||
elog(ERROR, "COPY BINARY: incompatible integer layout");
|
||||
/* Flags field */
|
||||
CopyGetData(&tmp, sizeof(int32), fp);
|
||||
if (CopyGetEof(fp))
|
||||
CopyGetData(&tmp, sizeof(int32));
|
||||
if (CopyGetEof())
|
||||
elog(ERROR, "COPY BINARY: bogus file header (missing flags)");
|
||||
file_has_oids = (tmp & (1 << 16)) != 0;
|
||||
tmp &= ~(1 << 16);
|
||||
if ((tmp >> 16) != 0)
|
||||
elog(ERROR, "COPY BINARY: unrecognized critical flags in header");
|
||||
/* Header extension length */
|
||||
CopyGetData(&tmp, sizeof(int32), fp);
|
||||
if (CopyGetEof(fp) || tmp < 0)
|
||||
CopyGetData(&tmp, sizeof(int32));
|
||||
if (CopyGetEof() || tmp < 0)
|
||||
elog(ERROR, "COPY BINARY: bogus file header (missing length)");
|
||||
/* Skip extension header, if present */
|
||||
while (tmp-- > 0)
|
||||
{
|
||||
CopyGetData(readSig, 1, fp);
|
||||
if (CopyGetEof(fp))
|
||||
CopyGetData(readSig, 1);
|
||||
if (CopyGetEof())
|
||||
elog(ERROR, "COPY BINARY: bogus file header (wrong length)");
|
||||
}
|
||||
}
|
||||
@ -936,6 +1106,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
|
||||
nulls = (char *) palloc(num_phys_attrs * sizeof(char));
|
||||
|
||||
/* Initialize static variables */
|
||||
copy_lineno = 0;
|
||||
fe_eof = false;
|
||||
|
||||
@ -970,7 +1141,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
|
||||
if (file_has_oids)
|
||||
{
|
||||
string = CopyReadAttribute(fp, delim, &result);
|
||||
string = CopyReadAttribute(delim, &result);
|
||||
|
||||
if (result == END_OF_FILE && *string == '\0')
|
||||
{
|
||||
@ -1006,7 +1177,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
elog(ERROR, "Missing data for column \"%s\"",
|
||||
NameStr(attr[m]->attname));
|
||||
|
||||
string = CopyReadAttribute(fp, delim, &result);
|
||||
string = CopyReadAttribute(delim, &result);
|
||||
|
||||
if (result == END_OF_FILE && *string == '\0' &&
|
||||
cur == attnumlist && !file_has_oids)
|
||||
@ -1051,8 +1222,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
int16 fld_count,
|
||||
fld_size;
|
||||
|
||||
CopyGetData(&fld_count, sizeof(int16), fp);
|
||||
if (CopyGetEof(fp) || fld_count == -1)
|
||||
CopyGetData(&fld_count, sizeof(int16));
|
||||
if (CopyGetEof() || fld_count == -1)
|
||||
{
|
||||
done = true;
|
||||
break;
|
||||
@ -1064,14 +1235,14 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
|
||||
if (file_has_oids)
|
||||
{
|
||||
CopyGetData(&fld_size, sizeof(int16), fp);
|
||||
if (CopyGetEof(fp))
|
||||
CopyGetData(&fld_size, sizeof(int16));
|
||||
if (CopyGetEof())
|
||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||
if (fld_size != (int16) sizeof(Oid))
|
||||
elog(ERROR, "COPY BINARY: sizeof(Oid) is %d, expected %d",
|
||||
(int) fld_size, (int) sizeof(Oid));
|
||||
CopyGetData(&loaded_oid, sizeof(Oid), fp);
|
||||
if (CopyGetEof(fp))
|
||||
CopyGetData(&loaded_oid, sizeof(Oid));
|
||||
if (CopyGetEof())
|
||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||
if (loaded_oid == InvalidOid)
|
||||
elog(ERROR, "COPY BINARY: Invalid Oid");
|
||||
@ -1085,8 +1256,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
|
||||
i++;
|
||||
|
||||
CopyGetData(&fld_size, sizeof(int16), fp);
|
||||
if (CopyGetEof(fp))
|
||||
CopyGetData(&fld_size, sizeof(int16));
|
||||
if (CopyGetEof())
|
||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||
if (fld_size == 0)
|
||||
continue; /* it's NULL; nulls[attnum-1] already set */
|
||||
@ -1099,17 +1270,16 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
int32 varlena_size;
|
||||
Pointer varlena_ptr;
|
||||
|
||||
CopyGetData(&varlena_size, sizeof(int32), fp);
|
||||
if (CopyGetEof(fp))
|
||||
CopyGetData(&varlena_size, sizeof(int32));
|
||||
if (CopyGetEof())
|
||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||
if (varlena_size < (int32) sizeof(int32))
|
||||
elog(ERROR, "COPY BINARY: bogus varlena length");
|
||||
varlena_ptr = (Pointer) palloc(varlena_size);
|
||||
VARATT_SIZEP(varlena_ptr) = varlena_size;
|
||||
CopyGetData(VARDATA(varlena_ptr),
|
||||
varlena_size - sizeof(int32),
|
||||
fp);
|
||||
if (CopyGetEof(fp))
|
||||
varlena_size - sizeof(int32));
|
||||
if (CopyGetEof())
|
||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||
values[m] = PointerGetDatum(varlena_ptr);
|
||||
}
|
||||
@ -1120,8 +1290,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
|
||||
Assert(fld_size > 0);
|
||||
refval_ptr = (Pointer) palloc(fld_size);
|
||||
CopyGetData(refval_ptr, fld_size, fp);
|
||||
if (CopyGetEof(fp))
|
||||
CopyGetData(refval_ptr, fld_size);
|
||||
if (CopyGetEof())
|
||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||
values[m] = PointerGetDatum(refval_ptr);
|
||||
}
|
||||
@ -1135,8 +1305,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
* how shorter data values are aligned within a Datum.
|
||||
*/
|
||||
Assert(fld_size > 0 && fld_size <= sizeof(Datum));
|
||||
CopyGetData(&datumBuf, fld_size, fp);
|
||||
if (CopyGetEof(fp))
|
||||
CopyGetData(&datumBuf, fld_size);
|
||||
if (CopyGetEof())
|
||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||
values[m] = fetch_att(&datumBuf, true, fld_size);
|
||||
}
|
||||
@ -1324,7 +1494,7 @@ GetTypeElement(Oid type)
|
||||
*/
|
||||
|
||||
static char *
|
||||
CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
||||
CopyReadAttribute(const char *delim, CopyReadResult *result)
|
||||
{
|
||||
int c;
|
||||
int delimc = (unsigned char) delim[0];
|
||||
@ -1344,7 +1514,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
c = CopyGetChar(fp);
|
||||
c = CopyGetChar();
|
||||
if (c == EOF)
|
||||
{
|
||||
*result = END_OF_FILE;
|
||||
@ -1359,7 +1529,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
||||
break;
|
||||
if (c == '\\')
|
||||
{
|
||||
c = CopyGetChar(fp);
|
||||
c = CopyGetChar();
|
||||
if (c == EOF)
|
||||
{
|
||||
*result = END_OF_FILE;
|
||||
@ -1379,16 +1549,16 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
||||
int val;
|
||||
|
||||
val = OCTVALUE(c);
|
||||
c = CopyPeekChar(fp);
|
||||
c = CopyPeekChar();
|
||||
if (ISOCTAL(c))
|
||||
{
|
||||
val = (val << 3) + OCTVALUE(c);
|
||||
CopyDonePeek(fp, c, true /* pick up */ );
|
||||
c = CopyPeekChar(fp);
|
||||
CopyDonePeek(c, true /* pick up */ );
|
||||
c = CopyPeekChar();
|
||||
if (ISOCTAL(c))
|
||||
{
|
||||
val = (val << 3) + OCTVALUE(c);
|
||||
CopyDonePeek(fp, c, true /* pick up */ );
|
||||
CopyDonePeek(c, true /* pick up */ );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1397,7 +1567,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
||||
*result = END_OF_FILE;
|
||||
goto copy_eof;
|
||||
}
|
||||
CopyDonePeek(fp, c, false /* put back */ );
|
||||
CopyDonePeek(c, false /* put back */ );
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1407,7 +1577,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
||||
*result = END_OF_FILE;
|
||||
goto copy_eof;
|
||||
}
|
||||
CopyDonePeek(fp, c, false /* put back */ );
|
||||
CopyDonePeek(c, false /* put back */ );
|
||||
}
|
||||
c = val & 0377;
|
||||
}
|
||||
@ -1441,9 +1611,21 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
||||
c = '\v';
|
||||
break;
|
||||
case '.':
|
||||
c = CopyGetChar(fp);
|
||||
c = CopyGetChar();
|
||||
if (c != '\n')
|
||||
elog(ERROR, "CopyReadAttribute: end of record marker corrupted");
|
||||
/*
|
||||
* In protocol version 3, we should ignore anything after
|
||||
* \. up to the protocol end of copy data. (XXX maybe
|
||||
* better not to treat \. as special?)
|
||||
*/
|
||||
if (copy_dest == COPY_NEW_FE)
|
||||
{
|
||||
while (c != EOF)
|
||||
{
|
||||
c = CopyGetChar();
|
||||
}
|
||||
}
|
||||
*result = END_OF_FILE;
|
||||
goto copy_eof;
|
||||
}
|
||||
@ -1458,7 +1640,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
||||
mblen = pg_encoding_mblen(client_encoding, s);
|
||||
for (j = 1; j < mblen; j++)
|
||||
{
|
||||
c = CopyGetChar(fp);
|
||||
c = CopyGetChar();
|
||||
if (c == EOF)
|
||||
{
|
||||
*result = END_OF_FILE;
|
||||
@ -1488,7 +1670,7 @@ copy_eof:
|
||||
}
|
||||
|
||||
static void
|
||||
CopyAttributeOut(FILE *fp, char *server_string, char *delim)
|
||||
CopyAttributeOut(char *server_string, char *delim)
|
||||
{
|
||||
char *string;
|
||||
char c;
|
||||
@ -1511,30 +1693,30 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim)
|
||||
switch (c)
|
||||
{
|
||||
case '\b':
|
||||
CopySendString("\\b", fp);
|
||||
CopySendString("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
CopySendString("\\f", fp);
|
||||
CopySendString("\\f");
|
||||
break;
|
||||
case '\n':
|
||||
CopySendString("\\n", fp);
|
||||
CopySendString("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
CopySendString("\\r", fp);
|
||||
CopySendString("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
CopySendString("\\t", fp);
|
||||
CopySendString("\\t");
|
||||
break;
|
||||
case '\v':
|
||||
CopySendString("\\v", fp);
|
||||
CopySendString("\\v");
|
||||
break;
|
||||
case '\\':
|
||||
CopySendString("\\\\", fp);
|
||||
CopySendString("\\\\");
|
||||
break;
|
||||
default:
|
||||
if (c == delimc)
|
||||
CopySendChar('\\', fp);
|
||||
CopySendChar(c, fp);
|
||||
CopySendChar('\\');
|
||||
CopySendChar(c);
|
||||
|
||||
/*
|
||||
* We can skip pg_encoding_mblen() overhead when encoding
|
||||
@ -1546,7 +1728,7 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim)
|
||||
/* send additional bytes of the char, if any */
|
||||
mblen = pg_encoding_mblen(client_encoding, string);
|
||||
for (i = 1; i < mblen; i++)
|
||||
CopySendChar(string[i], fp);
|
||||
CopySendChar(string[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
char *passwd;
|
||||
|
||||
sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
|
||||
if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
|
||||
passwd = recv_password_packet(pam_port_cludge);
|
||||
|
||||
if (passwd == NULL)
|
||||
return PAM_CONV_ERR; /* client didn't want to send password */
|
||||
|
||||
initStringInfo(&buf);
|
||||
if (pq_getstr_bounded(&buf, 1000) == EOF)
|
||||
return PAM_CONV_ERR; /* EOF while reading password */
|
||||
|
||||
/* Do not echo failed password to logs, for security. */
|
||||
elog(DEBUG5, "received PAM packet");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* If maxlen is specified, check for overlength input. */
|
||||
if (maxlen > 0 && s->len > maxlen)
|
||||
|
||||
/* --------------------------------
|
||||
* 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;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
int status;
|
||||
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)
|
||||
{
|
||||
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!
|
||||
*
|
||||
* The maxlen parameter is interpreted as per pq_getstring.
|
||||
*
|
||||
* returns 0 if OK, EOF if trouble
|
||||
* Returns a pointer directly into the message buffer; note this
|
||||
* may not have any particular alignment.
|
||||
* --------------------------------
|
||||
*/
|
||||
int
|
||||
pq_getstr_bounded(StringInfo s, int maxlen)
|
||||
const char *
|
||||
pq_getmsgbytes(StringInfo msg, int datalen)
|
||||
{
|
||||
int result;
|
||||
char *p;
|
||||
|
||||
result = pq_getstring(s, maxlen);
|
||||
|
||||
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);
|
||||
}
|
||||
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
|
||||
*
|
||||
* 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)
|
||||
*
|
||||
* May return a pointer directly into the message buffer, or a pointer
|
||||
* to a palloc'd conversion result.
|
||||
* --------------------------------
|
||||
*/
|
||||
const char *
|
||||
pq_getmsgstring(StringInfo msg)
|
||||
{
|
||||
char *str;
|
||||
int slen;
|
||||
|
||||
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;
|
||||
|
||||
return (const char *) pg_client_to_server((unsigned char *) str, slen);
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* 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 */
|
||||
break;
|
||||
|
||||
/*
|
||||
* 'Q': user entered a query
|
||||
*/
|
||||
case 'Q':
|
||||
if (pq_getstr(inBuf))
|
||||
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;
|
||||
|
||||
/*
|
||||
* 'F': calling user/system functions
|
||||
*/
|
||||
case 'F':
|
||||
if (pq_getstr(inBuf))
|
||||
return EOF; /* ignore "string" at start of F message */
|
||||
case 'F': /* fastpath function call */
|
||||
break;
|
||||
|
||||
/*
|
||||
* 'X': frontend is exiting
|
||||
*/
|
||||
case 'X':
|
||||
case 'X': /* terminate */
|
||||
break;
|
||||
|
||||
case 'd': /* copy data */
|
||||
case 'c': /* copy done */
|
||||
case 'f': /* copy fail */
|
||||
/* Accept but ignore these messages, per protocol spec */
|
||||
break;
|
||||
|
||||
/*
|
||||
* otherwise we got garbage from the frontend.
|
||||
*
|
||||
* XXX are we certain that we want to do an elog(FATAL) here?
|
||||
* -cim 1/24/90
|
||||
*/
|
||||
default:
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
if (send_rfq)
|
||||
{
|
||||
ReadyForQuery(whereToSendOutput);
|
||||
send_rfq = false;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Tell the statistics collector what we've collected
|
||||
@ -1986,43 +2009,9 @@ 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
|
||||
* ----------
|
||||
*/
|
||||
pgstat_report_activity("<FASTPATH> function call");
|
||||
|
||||
/* start an xact for this function invocation */
|
||||
start_xact_command();
|
||||
|
||||
if (HandleFunctionRequest() == EOF)
|
||||
{
|
||||
/* lost frontend connection during F message input */
|
||||
|
||||
/*
|
||||
* Reset whereToSendOutput to prevent elog from
|
||||
* attempting to send any more messages to client.
|
||||
*/
|
||||
if (whereToSendOutput == Remote)
|
||||
whereToSendOutput = None;
|
||||
|
||||
proc_exit(0);
|
||||
}
|
||||
|
||||
/* commit the function-invocation transaction */
|
||||
finish_xact_command(false);
|
||||
break;
|
||||
|
||||
/*
|
||||
* 'Q' indicates a user query
|
||||
*/
|
||||
case 'Q':
|
||||
/*
|
||||
* otherwise, process the input string.
|
||||
* Process the query string.
|
||||
*
|
||||
* Note: transaction command start/end is now done within
|
||||
* pg_exec_query_string(), not here.
|
||||
@ -2038,6 +2027,35 @@ PostgresMain(int argc, char *argv[], const char *username)
|
||||
|
||||
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(parser_input) == EOF)
|
||||
{
|
||||
/* lost frontend connection during F message input */
|
||||
|
||||
/*
|
||||
* Reset whereToSendOutput to prevent elog from
|
||||
* attempting to send any more messages to client.
|
||||
*/
|
||||
if (whereToSendOutput == Remote)
|
||||
whereToSendOutput = None;
|
||||
|
||||
proc_exit(0);
|
||||
}
|
||||
|
||||
/* commit the function-invocation transaction */
|
||||
finish_xact_command(false);
|
||||
|
||||
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,52 +769,25 @@ PQsendQuery(PGconn *conn, const char *query)
|
||||
conn->result = NULL;
|
||||
conn->curTuple = NULL;
|
||||
|
||||
/* send the query to the backend; */
|
||||
|
||||
/*
|
||||
* in order to guarantee that we don't send a partial query where we
|
||||
* would become out of sync with the backend and/or block during a
|
||||
* non-blocking connection we must first flush the send buffer before
|
||||
* sending more data
|
||||
*
|
||||
* an alternative is to implement 'queue reservations' where we are able
|
||||
* to roll up a transaction (the 'Q' along with our query) and make
|
||||
* sure we have enough space for it all in the send buffer.
|
||||
*/
|
||||
if (pqIsnonblocking(conn))
|
||||
{
|
||||
/*
|
||||
* the buffer must have emptied completely before we allow a new
|
||||
* query to be buffered
|
||||
*/
|
||||
if (pqFlush(conn))
|
||||
return 0;
|
||||
/* 'Q' == queries */
|
||||
/* XXX: if we fail here we really ought to not block */
|
||||
if (pqPutc('Q', conn) != 0 || pqPuts(query, conn) != 0)
|
||||
/* construct the outgoing Query message */
|
||||
if (pqPutMsgStart('Q', conn) < 0 ||
|
||||
pqPuts(query, conn) < 0 ||
|
||||
pqPutMsgEnd(conn) < 0)
|
||||
{
|
||||
handleSendFailure(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* give the data a push, ignore the return value as ConsumeInput()
|
||||
* will do any additional flushing if needed
|
||||
* 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.
|
||||
*/
|
||||
pqFlush(conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* the frontend-backend protocol uses 'Q' to designate queries
|
||||
*/
|
||||
if (pqPutc('Q', conn) != 0 || pqPuts(query, conn) != 0 ||
|
||||
pqFlush(conn) != 0)
|
||||
if (pqFlush(conn) < 0)
|
||||
{
|
||||
handleSendFailure(conn);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* OK, it's launched! */
|
||||
conn->asyncStatus = PGASYNC_BUSY;
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,16 +16,14 @@
|
||||
* will cause repeat printouts.
|
||||
*
|
||||
* We must speak the same transmitted data representations as the backend
|
||||
* routines. Note that this module supports *only* network byte order
|
||||
* for transmitted ints, whereas the backend modules (as of this writing)
|
||||
* still handle either network or little-endian byte order.
|
||||
* routines.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.88 2003/04/02 00:49:28 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.89 2003/04/19 00:02:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -63,15 +61,15 @@
|
||||
#define DONOTICE(conn,message) \
|
||||
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
|
||||
|
||||
static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn);
|
||||
static int pqSendSome(PGconn *conn, int len);
|
||||
static int pqSocketCheck(PGconn *conn, int forRead, int forWrite,
|
||||
time_t end_time);
|
||||
static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time);
|
||||
static int pqPutBytes(const char *s, size_t nbytes, PGconn *conn);
|
||||
|
||||
|
||||
/*
|
||||
* pqGetc:
|
||||
* get a character from the connection
|
||||
* pqGetc: get 1 character from the connection
|
||||
*
|
||||
* All these routines return 0 on success, EOF on error.
|
||||
* Note that for the Get routines, EOF only means there is not enough
|
||||
@ -93,12 +91,12 @@ pqGetc(char *result, PGconn *conn)
|
||||
|
||||
|
||||
/*
|
||||
* write 1 char to the connection
|
||||
* pqPutc: write 1 char to the current message
|
||||
*/
|
||||
int
|
||||
pqPutc(char c, PGconn *conn)
|
||||
{
|
||||
if (pqPutBytes(&c, 1, conn) == EOF)
|
||||
if (pqPutMsgBytes(&c, 1, conn))
|
||||
return EOF;
|
||||
|
||||
if (conn->Pfdebug)
|
||||
@ -108,93 +106,6 @@ pqPutc(char c, PGconn *conn)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* pqPutBytes: local routine to write N bytes to the connection,
|
||||
* with buffering
|
||||
*/
|
||||
static int
|
||||
pqPutBytes(const char *s, size_t nbytes, PGconn *conn)
|
||||
{
|
||||
/*
|
||||
* Strategy to handle blocking and non-blocking connections: Fill the
|
||||
* output buffer and flush it repeatedly until either all data has
|
||||
* been sent or is at least queued in the buffer.
|
||||
*
|
||||
* For non-blocking connections, grow the buffer if not all data fits
|
||||
* into it and the buffer can't be sent because the socket would
|
||||
* block.
|
||||
*/
|
||||
|
||||
while (nbytes)
|
||||
{
|
||||
size_t avail,
|
||||
remaining;
|
||||
|
||||
/* fill the output buffer */
|
||||
avail = Max(conn->outBufSize - conn->outCount, 0);
|
||||
remaining = Min(avail, nbytes);
|
||||
memcpy(conn->outBuffer + conn->outCount, s, remaining);
|
||||
conn->outCount += remaining;
|
||||
s += remaining;
|
||||
nbytes -= remaining;
|
||||
|
||||
/*
|
||||
* if the data didn't fit completely into the buffer, try to flush
|
||||
* the buffer
|
||||
*/
|
||||
if (nbytes)
|
||||
{
|
||||
int send_result = pqSendSome(conn);
|
||||
|
||||
/* if there were errors, report them */
|
||||
if (send_result < 0)
|
||||
return EOF;
|
||||
|
||||
/*
|
||||
* if not all data could be sent, increase the output buffer,
|
||||
* put the rest of s into it and return successfully. This
|
||||
* case will only happen in a non-blocking connection
|
||||
*/
|
||||
if (send_result > 0)
|
||||
{
|
||||
/*
|
||||
* try to grow the buffer. FIXME: The new size could be
|
||||
* chosen more intelligently.
|
||||
*/
|
||||
size_t buflen = (size_t) conn->outCount + nbytes;
|
||||
|
||||
if (buflen > (size_t) conn->outBufSize)
|
||||
{
|
||||
char *newbuf = realloc(conn->outBuffer, buflen);
|
||||
|
||||
if (!newbuf)
|
||||
{
|
||||
/* realloc failed. Probably out of memory */
|
||||
printfPQExpBuffer(&conn->errorMessage,
|
||||
"cannot allocate memory for output buffer\n");
|
||||
return EOF;
|
||||
}
|
||||
conn->outBuffer = newbuf;
|
||||
conn->outBufSize = buflen;
|
||||
}
|
||||
/* put the data into it */
|
||||
memcpy(conn->outBuffer + conn->outCount, s, nbytes);
|
||||
conn->outCount += nbytes;
|
||||
|
||||
/* report success. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pqSendSome was able to send all data. Continue with the next
|
||||
* chunk of s.
|
||||
*/
|
||||
} /* while */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* pqGets:
|
||||
* get a null-terminated string from the connection,
|
||||
@ -232,14 +143,17 @@ pqGets(PQExpBuffer buf, PGconn *conn)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* pqPuts: write a null-terminated string to the current message
|
||||
*/
|
||||
int
|
||||
pqPuts(const char *s, PGconn *conn)
|
||||
{
|
||||
if (pqPutBytes(s, strlen(s) + 1, conn))
|
||||
if (pqPutMsgBytes(s, strlen(s) + 1, conn))
|
||||
return EOF;
|
||||
|
||||
if (conn->Pfdebug)
|
||||
fprintf(conn->Pfdebug, "To backend> %s\n", s);
|
||||
fprintf(conn->Pfdebug, "To backend> '%s'\n", s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -267,12 +181,12 @@ pqGetnchar(char *s, size_t len, PGconn *conn)
|
||||
|
||||
/*
|
||||
* pqPutnchar:
|
||||
* send a string of exactly len bytes, no null termination needed
|
||||
* write exactly len bytes to the current message
|
||||
*/
|
||||
int
|
||||
pqPutnchar(const char *s, size_t len, PGconn *conn)
|
||||
{
|
||||
if (pqPutBytes(s, len, conn))
|
||||
if (pqPutMsgBytes(s, len, conn))
|
||||
return EOF;
|
||||
|
||||
if (conn->Pfdebug)
|
||||
@ -325,7 +239,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
|
||||
|
||||
/*
|
||||
* pgPutInt
|
||||
* send an integer of 2 or 4 bytes, converting from host byte order
|
||||
* write an integer of 2 or 4 bytes, converting from host byte order
|
||||
* to network byte order.
|
||||
*/
|
||||
int
|
||||
@ -339,12 +253,12 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
|
||||
{
|
||||
case 2:
|
||||
tmp2 = htons((uint16) value);
|
||||
if (pqPutBytes((const char *) &tmp2, 2, conn))
|
||||
if (pqPutMsgBytes((const char *) &tmp2, 2, conn))
|
||||
return EOF;
|
||||
break;
|
||||
case 4:
|
||||
tmp4 = htonl((uint32) value);
|
||||
if (pqPutBytes((const char *) &tmp4, 4, conn))
|
||||
if (pqPutMsgBytes((const char *) &tmp4, 4, conn))
|
||||
return EOF;
|
||||
break;
|
||||
default:
|
||||
@ -362,24 +276,162 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
|
||||
}
|
||||
|
||||
/*
|
||||
* pqReadReady: is select() saying the file is ready to read?
|
||||
* JAB: -or- if SSL is enabled and used, is it buffering bytes?
|
||||
* Returns -1 on failure, 0 if not ready, 1 if ready.
|
||||
* Make sure conn's output buffer can hold bytes_needed bytes (caller must
|
||||
* include existing outCount into the value!)
|
||||
*
|
||||
* Returns 0 on success, EOF on error
|
||||
*/
|
||||
int
|
||||
pqReadReady(PGconn *conn)
|
||||
static int
|
||||
checkOutBufferSpace(int bytes_needed, PGconn *conn)
|
||||
{
|
||||
return pqSocketCheck(conn, 1, 0, (time_t) 0);
|
||||
int newsize = conn->outBufSize;
|
||||
char *newbuf;
|
||||
|
||||
if (bytes_needed <= newsize)
|
||||
return 0;
|
||||
/*
|
||||
* If we need to enlarge the buffer, we first try to double it in size;
|
||||
* if that doesn't work, enlarge in multiples of 8K. This avoids
|
||||
* thrashing the malloc pool by repeated small enlargements.
|
||||
*
|
||||
* Note: tests for newsize > 0 are to catch integer overflow.
|
||||
*/
|
||||
do {
|
||||
newsize *= 2;
|
||||
} while (bytes_needed > newsize && newsize > 0);
|
||||
|
||||
if (bytes_needed <= newsize)
|
||||
{
|
||||
newbuf = realloc(conn->outBuffer, newsize);
|
||||
if (newbuf)
|
||||
{
|
||||
/* realloc succeeded */
|
||||
conn->outBuffer = newbuf;
|
||||
conn->outBufSize = newsize;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
newsize = conn->outBufSize;
|
||||
do {
|
||||
newsize += 8192;
|
||||
} while (bytes_needed > newsize && newsize > 0);
|
||||
|
||||
if (bytes_needed <= newsize)
|
||||
{
|
||||
newbuf = realloc(conn->outBuffer, newsize);
|
||||
if (newbuf)
|
||||
{
|
||||
/* realloc succeeded */
|
||||
conn->outBuffer = newbuf;
|
||||
conn->outBufSize = newsize;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* realloc failed. Probably out of memory */
|
||||
printfPQExpBuffer(&conn->errorMessage,
|
||||
"cannot allocate memory for output buffer\n");
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/*
|
||||
* pqWriteReady: is select() saying the file is ready to write?
|
||||
* Returns -1 on failure, 0 if not ready, 1 if ready.
|
||||
* pqPutMsgStart: begin construction of a message to the server
|
||||
*
|
||||
* msg_type is the message type byte, or 0 for a message without type byte
|
||||
* (only startup messages have no type byte)
|
||||
*
|
||||
* Returns 0 on success, EOF on error
|
||||
*
|
||||
* The idea here is that we construct the message in conn->outBuffer,
|
||||
* beginning just past any data already in outBuffer (ie, at
|
||||
* outBuffer+outCount). We enlarge the buffer as needed to hold the message.
|
||||
* When the message is complete, we fill in the length word and then advance
|
||||
* outCount past the message, making it eligible to send. The state
|
||||
* variable conn->outMsgStart points to the incomplete message's length word
|
||||
* (it is either outCount or outCount+1 depending on whether there is a
|
||||
* type byte). The state variable conn->outMsgEnd is the end of the data
|
||||
* collected so far.
|
||||
*/
|
||||
int
|
||||
pqWriteReady(PGconn *conn)
|
||||
pqPutMsgStart(char msg_type, PGconn *conn)
|
||||
{
|
||||
return pqSocketCheck(conn, 0, 1, (time_t) 0);
|
||||
int lenPos;
|
||||
|
||||
/* where the message length word will go */
|
||||
if (msg_type)
|
||||
lenPos = conn->outCount + 1;
|
||||
else
|
||||
lenPos = conn->outCount;
|
||||
/* make sure there is room for it */
|
||||
if (checkOutBufferSpace(lenPos + 4, conn))
|
||||
return EOF;
|
||||
/* okay, save the message type byte if any */
|
||||
if (msg_type)
|
||||
conn->outBuffer[conn->outCount] = msg_type;
|
||||
/* set up the message pointers */
|
||||
conn->outMsgStart = lenPos;
|
||||
conn->outMsgEnd = lenPos + 4;
|
||||
/* length word will be filled in by pqPutMsgEnd */
|
||||
|
||||
if (conn->Pfdebug)
|
||||
fprintf(conn->Pfdebug, "To backend> Msg %c\n",
|
||||
msg_type ? msg_type : ' ');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* pqPutMsgBytes: add bytes to a partially-constructed message
|
||||
*
|
||||
* Returns 0 on success, EOF on error
|
||||
*/
|
||||
static int
|
||||
pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
|
||||
{
|
||||
/* make sure there is room for it */
|
||||
if (checkOutBufferSpace(conn->outMsgEnd + len, conn))
|
||||
return EOF;
|
||||
/* okay, save the data */
|
||||
memcpy(conn->outBuffer + conn->outMsgEnd, buf, len);
|
||||
conn->outMsgEnd += len;
|
||||
/* no Pfdebug call here, caller should do it */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* pqPutMsgEnd: finish constructing a message and possibly send it
|
||||
*
|
||||
* Returns 0 on success, EOF on error
|
||||
*
|
||||
* We don't actually send anything here unless we've accumulated at least
|
||||
* 8K worth of data (the typical size of a pipe buffer on Unix systems).
|
||||
* This avoids sending small partial packets. The caller must use pqFlush
|
||||
* when it's important to flush all the data out to the server.
|
||||
*/
|
||||
int
|
||||
pqPutMsgEnd(PGconn *conn)
|
||||
{
|
||||
uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
|
||||
|
||||
if (conn->Pfdebug)
|
||||
fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
|
||||
msgLen);
|
||||
|
||||
msgLen = htonl(msgLen);
|
||||
memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
|
||||
conn->outCount = conn->outMsgEnd;
|
||||
|
||||
if (conn->outCount >= 8192)
|
||||
{
|
||||
int toSend = conn->outCount - (conn->outCount % 8192);
|
||||
|
||||
if (pqSendSome(conn, toSend) < 0)
|
||||
return EOF;
|
||||
/* in nonblock mode, don't complain if unable to send it all */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
@ -580,16 +632,20 @@ definitelyFailed:
|
||||
}
|
||||
|
||||
/*
|
||||
* pqSendSome: send any data waiting in the output buffer.
|
||||
* pqSendSome: send data waiting in the output buffer.
|
||||
*
|
||||
* Return 0 on sucess, -1 on failure and 1 when data remains because the
|
||||
* socket would block and the connection is non-blocking.
|
||||
* len is how much to try to send (typically equal to outCount, but may
|
||||
* be less).
|
||||
*
|
||||
* Return 0 on success, -1 on failure and 1 when not all data could be sent
|
||||
* because the socket would block and the connection is non-blocking.
|
||||
*/
|
||||
int
|
||||
pqSendSome(PGconn *conn)
|
||||
static int
|
||||
pqSendSome(PGconn *conn, int len)
|
||||
{
|
||||
char *ptr = conn->outBuffer;
|
||||
int len = conn->outCount;
|
||||
int remaining = conn->outCount;
|
||||
int result = 0;
|
||||
|
||||
if (conn->sock < 0)
|
||||
{
|
||||
@ -598,13 +654,6 @@ pqSendSome(PGconn *conn)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* don't try to send zero data, allows us to use this function without
|
||||
* too much worry about overhead
|
||||
*/
|
||||
if (len == 0)
|
||||
return (0);
|
||||
|
||||
/* while there's still data to send */
|
||||
while (len > 0)
|
||||
{
|
||||
@ -648,8 +697,9 @@ pqSendSome(PGconn *conn)
|
||||
* (typically, a NOTICE message from the backend
|
||||
* telling us it's committing hara-kiri...). Leave
|
||||
* the socket open until pqReadData finds no more data
|
||||
* can be read.
|
||||
* can be read. But abandon attempt to send data.
|
||||
*/
|
||||
conn->outCount = 0;
|
||||
return -1;
|
||||
|
||||
default:
|
||||
@ -657,6 +707,7 @@ pqSendSome(PGconn *conn)
|
||||
libpq_gettext("could not send data to server: %s\n"),
|
||||
SOCK_STRERROR(SOCK_ERRNO));
|
||||
/* We don't assume it's a fatal error... */
|
||||
conn->outCount = 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -664,6 +715,7 @@ pqSendSome(PGconn *conn)
|
||||
{
|
||||
ptr += sent;
|
||||
len -= sent;
|
||||
remaining -= sent;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
@ -681,46 +733,49 @@ pqSendSome(PGconn *conn)
|
||||
#endif
|
||||
if (pqIsnonblocking(conn))
|
||||
{
|
||||
/* shift the contents of the buffer */
|
||||
memmove(conn->outBuffer, ptr, len);
|
||||
conn->outCount = len;
|
||||
return 1;
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
#ifdef USE_SSL
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pqWait(FALSE, TRUE, conn))
|
||||
return -1;
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn->outCount = 0;
|
||||
/* shift the remaining contents of the buffer */
|
||||
if (remaining > 0)
|
||||
memmove(conn->outBuffer, ptr, remaining);
|
||||
conn->outCount = remaining;
|
||||
|
||||
if (conn->Pfdebug)
|
||||
fflush(conn->Pfdebug);
|
||||
|
||||
return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* pqFlush: send any data waiting in the output buffer
|
||||
*
|
||||
* Implemented in terms of pqSendSome to recreate the old behavior which
|
||||
* returned 0 if all data was sent or EOF. EOF was sent regardless of
|
||||
* whether an error occurred or not all data was sent on a non-blocking
|
||||
* socket.
|
||||
* Return 0 on success, -1 on failure and 1 when not all data could be sent
|
||||
* because the socket would block and the connection is non-blocking.
|
||||
*/
|
||||
int
|
||||
pqFlush(PGconn *conn)
|
||||
{
|
||||
if (pqSendSome(conn))
|
||||
return EOF;
|
||||
if (conn->Pfdebug)
|
||||
fflush(conn->Pfdebug);
|
||||
|
||||
if (conn->outCount > 0)
|
||||
return pqSendSome(conn, conn->outCount);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* pqWait: wait until we can read or write the connection socket
|
||||
*
|
||||
@ -766,10 +821,31 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* pqReadReady: is select() saying the file is ready to read?
|
||||
* Returns -1 on failure, 0 if not ready, 1 if ready.
|
||||
*/
|
||||
int
|
||||
pqReadReady(PGconn *conn)
|
||||
{
|
||||
return pqSocketCheck(conn, 1, 0, (time_t) 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* pqWriteReady: is select() saying the file is ready to write?
|
||||
* Returns -1 on failure, 0 if not ready, 1 if ready.
|
||||
*/
|
||||
int
|
||||
pqWriteReady(PGconn *conn)
|
||||
{
|
||||
return pqSocketCheck(conn, 0, 1, (time_t) 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks a socket, using poll or select, for data to be read, written,
|
||||
* or both. Returns >0 if one or more conditions are met, 0 if it timed
|
||||
* out, -1 if an error occurred.
|
||||
*
|
||||
* If SSL is in use, the SSL buffer is checked prior to checking the socket
|
||||
* for read data directly.
|
||||
*/
|
||||
@ -787,8 +863,8 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* JAB: Check for SSL library buffering read bytes */
|
||||
#ifdef USE_SSL
|
||||
/* Check for SSL library buffering read bytes */
|
||||
if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
|
||||
{
|
||||
/* short-circuit the select */
|
||||
@ -819,6 +895,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
|
||||
* If neither forRead nor forWrite are set, immediately return a timeout
|
||||
* condition (without waiting). Return >0 if condition is met, 0
|
||||
* if a timeout occurred, -1 if an error or interrupt occurred.
|
||||
*
|
||||
* Timeout is infinite if end_time is -1. Timeout is immediate (no blocking)
|
||||
* if end_time is 0 (or indeed, any time before now).
|
||||
*/
|
||||
@ -830,16 +907,17 @@ pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time)
|
||||
struct pollfd input_fd;
|
||||
int timeout_ms;
|
||||
|
||||
if (!forRead && !forWrite)
|
||||
return 0;
|
||||
|
||||
input_fd.fd = sock;
|
||||
input_fd.events = 0;
|
||||
input_fd.events = POLLERR;
|
||||
input_fd.revents = 0;
|
||||
|
||||
if (forRead)
|
||||
input_fd.events |= POLLIN;
|
||||
if (forWrite)
|
||||
input_fd.events |= POLLOUT;
|
||||
if (!input_fd.events)
|
||||
return 0;
|
||||
|
||||
/* Compute appropriate timeout interval */
|
||||
if (end_time == ((time_t) -1))
|
||||
|
@ -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);
|
||||
|
Reference in New Issue
Block a user