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">
|
<chapter id="libpq">
|
||||||
@ -1749,9 +1749,10 @@ state will never end.
|
|||||||
<term><function>PQflush</function></term>
|
<term><function>PQflush</function></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Attempts to flush any data queued to the server,
|
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
|
Returns 0 if successful (or if the send queue is empty), -1 if it failed for
|
||||||
some reason.
|
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>
|
<synopsis>
|
||||||
int PQflush(PGconn *conn);
|
int PQflush(PGconn *conn);
|
||||||
</synopsis>
|
</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">
|
<chapter id="protocol">
|
||||||
<title>Frontend/Backend Protocol</title>
|
<title>Frontend/Backend Protocol</title>
|
||||||
@ -819,8 +819,9 @@
|
|||||||
Copy-in mode (data transfer to the server) is initiated when the
|
Copy-in mode (data transfer to the server) is initiated when the
|
||||||
backend executes a <command>COPY FROM STDIN</> SQL statement. The backend
|
backend executes a <command>COPY FROM STDIN</> SQL statement. The backend
|
||||||
sends a CopyInResponse message to the frontend. The frontend should
|
sends a CopyInResponse message to the frontend. The frontend should
|
||||||
then send zero or more CopyDataRow messages, one per row to be loaded.
|
then send zero or more CopyData messages, forming a stream of input
|
||||||
(For <command>COPY BINARY</>, send CopyBinaryRow messages instead.)
|
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
|
The frontend can terminate the copy-in mode by sending either a CopyDone
|
||||||
message (allowing successful termination) or a CopyFail message (which
|
message (allowing successful termination) or a CopyFail message (which
|
||||||
will cause the <command>COPY</> SQL statement to fail with an
|
will cause the <command>COPY</> SQL statement to fail with an
|
||||||
@ -833,37 +834,33 @@
|
|||||||
<para>
|
<para>
|
||||||
In the event of a backend-detected error during copy-in mode (including
|
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
|
receipt of a CopyFail message, or indeed any frontend message other than
|
||||||
CopyDataRow, CopyBinaryRow, or CopyDone), the backend will issue an
|
CopyData or CopyDone), the backend will issue an ErrorResponse
|
||||||
ErrorResponse
|
|
||||||
message. If the <command>COPY</> command was issued via an extended-query
|
message. If the <command>COPY</> command was issued via an extended-query
|
||||||
message, the backend will now discard frontend messages until a Sync
|
message, the backend will now discard frontend messages until a Sync
|
||||||
message is received, then it will issue ReadyForQuery and return to normal
|
message is received, then it will issue ReadyForQuery and return to normal
|
||||||
processing. If the <command>COPY</> command was issued in a simple
|
processing. If the <command>COPY</> command was issued in a simple
|
||||||
Query message, the rest of that message is discarded and ReadyForQuery
|
Query message, the rest of that message is discarded and ReadyForQuery
|
||||||
is issued. In either case, any subsequent CopyDataRow, CopyBinaryRow,
|
is issued. In either case, any subsequent CopyData, CopyDone, or CopyFail
|
||||||
CopyDone, or CopyFail messages issued by the frontend will simply be
|
messages issued by the frontend will simply be dropped.
|
||||||
dropped.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Copy-out mode (data transfer from the server) is initiated when the
|
Copy-out mode (data transfer from the server) is initiated when the
|
||||||
backend executes a <command>COPY TO STDOUT</> SQL statement. The backend
|
backend executes a <command>COPY TO STDOUT</> SQL statement. The backend
|
||||||
sends a CopyOutResponse message to the frontend, followed by
|
sends a CopyOutResponse message to the frontend, followed by
|
||||||
zero or more CopyDataRow messages, one per row, followed by CopyDone.
|
zero or more CopyData messages (always one per row), followed by CopyDone.
|
||||||
(For <command>COPY BINARY</>, CopyBinaryRow messages are sent instead.)
|
|
||||||
The backend then reverts to the command-processing mode it was
|
The backend then reverts to the command-processing mode it was
|
||||||
in before the <command>COPY</> started, and sends CommandComplete.
|
in before the <command>COPY</> started, and sends CommandComplete.
|
||||||
The frontend cannot abort
|
The frontend cannot abort the transfer (short of closing the connection),
|
||||||
the transfer (short of closing the connection), but it can discard
|
but it can discard unwanted CopyData and CopyDone messages.
|
||||||
unwanted CopyDataRow, CopyBinaryRow, and CopyDone messages.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
In the event of a backend-detected error during copy-out mode,
|
In the event of a backend-detected error during copy-out mode,
|
||||||
the backend will issue an ErrorResponse message and revert to normal
|
the backend will issue an ErrorResponse message and revert to normal
|
||||||
processing. The frontend should treat receipt of ErrorResponse (or
|
processing. The frontend should treat receipt of ErrorResponse (or
|
||||||
indeed any message type other than CopyDataRow, CopyBinaryRow, or
|
indeed any message type other than CopyData or CopyDone) as terminating
|
||||||
CopyDone) as terminating the copy-out mode.
|
the copy-out mode.
|
||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
@ -1157,7 +1154,9 @@ indicate that it may be sent by a frontend (F), a backend (B), or both
|
|||||||
(F & B).
|
(F & B).
|
||||||
Notice that although each message includes a byte count at the beginning,
|
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
|
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>
|
</para>
|
||||||
|
|
||||||
<VariableList>
|
<VariableList>
|
||||||
@ -2002,83 +2001,7 @@ CommandComplete (B)
|
|||||||
|
|
||||||
<VarListEntry>
|
<VarListEntry>
|
||||||
<Term>
|
<Term>
|
||||||
CopyBinaryRow (F & B)
|
CopyData (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)
|
|
||||||
</Term>
|
</Term>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Para>
|
<Para>
|
||||||
@ -2089,7 +2012,7 @@ CopyDataRow (F & B)
|
|||||||
</Term>
|
</Term>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Para>
|
<Para>
|
||||||
Identifies the message as textual COPY data.
|
Identifies the message as COPY data.
|
||||||
</Para>
|
</Para>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</VarListEntry>
|
</VarListEntry>
|
||||||
@ -2105,12 +2028,14 @@ CopyDataRow (F & B)
|
|||||||
</VarListEntry>
|
</VarListEntry>
|
||||||
<VarListEntry>
|
<VarListEntry>
|
||||||
<Term>
|
<Term>
|
||||||
String
|
Byte<Replaceable>n</Replaceable>
|
||||||
</Term>
|
</Term>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Para>
|
<Para>
|
||||||
The textual representation of a single row of table data.
|
Data that forms part of a COPY datastream. Messages sent
|
||||||
It should end with a newline.
|
from the backend will always correspond to single data rows,
|
||||||
|
but messages sent by frontends may divide the datastream
|
||||||
|
arbitrarily.
|
||||||
</Para>
|
</Para>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</VarListEntry>
|
</VarListEntry>
|
||||||
@ -2236,8 +2161,7 @@ CopyInResponse (B)
|
|||||||
</Term>
|
</Term>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Para>
|
<Para>
|
||||||
0 for textual copy (CopyDataRow is expected), 1 for
|
0 for textual copy, 1 for binary copy.
|
||||||
binary copy (CopyBinaryRow is expected).
|
|
||||||
</Para>
|
</Para>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</VarListEntry>
|
</VarListEntry>
|
||||||
@ -2283,8 +2207,7 @@ CopyOutResponse (B)
|
|||||||
</Term>
|
</Term>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Para>
|
<Para>
|
||||||
0 for textual copy (CopyDataRow will follow), 1 for
|
0 for textual copy, 1 for binary copy.
|
||||||
binary copy (CopyBinaryRow will follow).
|
|
||||||
</Para>
|
</Para>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</VarListEntry>
|
</VarListEntry>
|
||||||
@ -3606,8 +3529,9 @@ StartupMessage (F)
|
|||||||
<ListItem>
|
<ListItem>
|
||||||
<Para>
|
<Para>
|
||||||
The protocol version number. The most significant 16 bits are
|
The protocol version number. The most significant 16 bits are
|
||||||
the major version number (3 for the format described here).
|
the major version number (3 or more for the format described
|
||||||
The least 16 significant bits are the minor version number.
|
here).
|
||||||
|
The least significant 16 bits are the minor version number.
|
||||||
</Para>
|
</Para>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</VarListEntry>
|
</VarListEntry>
|
||||||
@ -3654,17 +3578,18 @@ StartupMessage (F)
|
|||||||
<ListItem>
|
<ListItem>
|
||||||
<Para>
|
<Para>
|
||||||
Command-line arguments for the backend. (This is
|
Command-line arguments for the backend. (This is
|
||||||
deprecated in favor of setting individual GUC
|
deprecated in favor of setting individual run-time
|
||||||
parameters.)
|
parameters.)
|
||||||
</Para>
|
</Para>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</VarListEntry>
|
</VarListEntry>
|
||||||
</VariableList>
|
</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
|
set at backend start time may be listed. Such settings
|
||||||
will be applied during backend start (after parsing the
|
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>
|
</Para>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</VarListEntry>
|
</VarListEntry>
|
||||||
@ -3913,4 +3838,41 @@ not line breaks.
|
|||||||
</sect1>
|
</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>
|
</Chapter>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "commands/trigger.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "libpq/libpq.h"
|
#include "libpq/libpq.h"
|
||||||
|
#include "libpq/pqformat.h"
|
||||||
#include "mb/pg_wchar.h"
|
#include "mb/pg_wchar.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
@ -49,6 +50,17 @@
|
|||||||
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
|
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
|
||||||
#define OCTVALUE(c) ((c) - '0')
|
#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()
|
* Represents the type of data returned by CopyReadAttribute()
|
||||||
*/
|
*/
|
||||||
@ -61,13 +73,13 @@ typedef enum CopyReadResult
|
|||||||
|
|
||||||
/* non-export function prototypes */
|
/* non-export function prototypes */
|
||||||
static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
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,
|
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 GetInputFunction(Oid type);
|
||||||
static Oid GetTypeElement(Oid type);
|
static Oid GetTypeElement(Oid type);
|
||||||
static char *CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result);
|
static char *CopyReadAttribute(const char *delim, CopyReadResult *result);
|
||||||
static void CopyAttributeOut(FILE *fp, char *string, char *delim);
|
static void CopyAttributeOut(char *string, char *delim);
|
||||||
static List *CopyGetAttnums(Relation rel, List *attnamelist);
|
static List *CopyGetAttnums(Relation rel, List *attnamelist);
|
||||||
|
|
||||||
static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
|
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...
|
* never been reentrant...
|
||||||
*/
|
*/
|
||||||
int copy_lineno = 0; /* exported for use by elog() -- dz */
|
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
|
* These static variables are used to avoid incurring overhead for each
|
||||||
@ -96,98 +112,229 @@ static int server_encoding;
|
|||||||
/*
|
/*
|
||||||
* Internal communications functions
|
* Internal communications functions
|
||||||
*/
|
*/
|
||||||
static void CopySendData(void *databuf, int datasize, FILE *fp);
|
static void SendCopyBegin(bool binary);
|
||||||
static void CopySendString(const char *str, FILE *fp);
|
static void ReceiveCopyBegin(bool binary);
|
||||||
static void CopySendChar(char c, FILE *fp);
|
static void SendCopyEnd(bool binary);
|
||||||
static void CopyGetData(void *databuf, int datasize, FILE *fp);
|
static void CopySendData(void *databuf, int datasize);
|
||||||
static int CopyGetChar(FILE *fp);
|
static void CopySendString(const char *str);
|
||||||
static int CopyGetEof(FILE *fp);
|
static void CopySendChar(char c);
|
||||||
static int CopyPeekChar(FILE *fp);
|
static void CopyGetData(void *databuf, int datasize);
|
||||||
static void CopyDonePeek(FILE *fp, int c, bool pickup);
|
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
|
* Send copy start/stop messages for frontend copies. These have changed
|
||||||
* specified by fp or, if fp is NULL, using the standard
|
* in past protocol redesigns.
|
||||||
* backend->frontend functions
|
*/
|
||||||
*
|
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
|
* CopySendString does the same for null-terminated strings
|
||||||
* CopySendChar does the same for single characters
|
* CopySendChar does the same for single characters
|
||||||
*
|
*
|
||||||
* NB: no data conversion is applied by these functions
|
* NB: no data conversion is applied by these functions
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CopySendData(void *databuf, int datasize, FILE *fp)
|
CopySendData(void *databuf, int datasize)
|
||||||
{
|
{
|
||||||
if (!fp)
|
switch (copy_dest)
|
||||||
{
|
{
|
||||||
if (pq_putbytes((char *) databuf, datasize))
|
case COPY_FILE:
|
||||||
fe_eof = true;
|
fwrite(databuf, datasize, 1, copy_file);
|
||||||
}
|
if (ferror(copy_file))
|
||||||
else
|
elog(ERROR, "CopySendData: %m");
|
||||||
{
|
break;
|
||||||
fwrite(databuf, datasize, 1, fp);
|
case COPY_OLD_FE:
|
||||||
if (ferror(fp))
|
if (pq_putbytes((char *) databuf, datasize))
|
||||||
elog(ERROR, "CopySendData: %m");
|
fe_eof = true;
|
||||||
|
break;
|
||||||
|
case COPY_NEW_FE:
|
||||||
|
/* XXX fix later */
|
||||||
|
if (pq_putbytes((char *) databuf, datasize))
|
||||||
|
fe_eof = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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
|
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
|
* CopyGetData reads data from the source (file or frontend)
|
||||||
* specified by fp or, if fp is NULL, using the standard
|
|
||||||
* backend->frontend functions
|
|
||||||
*
|
|
||||||
* CopyGetChar does the same for single characters
|
* 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
|
* NB: no data conversion is applied by these functions
|
||||||
*/
|
*/
|
||||||
static void
|
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:
|
||||||
fe_eof = true;
|
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
|
static int
|
||||||
CopyGetChar(FILE *fp)
|
CopyGetChar(void)
|
||||||
{
|
{
|
||||||
if (!fp)
|
int ch;
|
||||||
|
|
||||||
|
switch (copy_dest)
|
||||||
{
|
{
|
||||||
int ch = pq_getbyte();
|
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;
|
||||||
|
|
||||||
if (ch == EOF)
|
CopyGetData(&cc, 1);
|
||||||
fe_eof = true;
|
if (fe_eof)
|
||||||
return ch;
|
ch = EOF;
|
||||||
|
else
|
||||||
|
ch = cc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ch = EOF;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
if (ch == EOF)
|
||||||
return getc(fp);
|
fe_eof = true;
|
||||||
}
|
return ch;
|
||||||
|
|
||||||
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).
|
* (if pickup is true) or leave it on the stream (if pickup is false).
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
CopyPeekChar(FILE *fp)
|
CopyPeekChar(void)
|
||||||
{
|
{
|
||||||
if (!fp)
|
int ch;
|
||||||
{
|
|
||||||
int ch = pq_peekbyte();
|
|
||||||
|
|
||||||
if (ch == EOF)
|
switch (copy_dest)
|
||||||
fe_eof = true;
|
{
|
||||||
return ch;
|
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;
|
||||||
}
|
}
|
||||||
else
|
if (ch == EOF)
|
||||||
return getc(fp);
|
fe_eof = true;
|
||||||
|
return ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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)
|
||||||
{
|
{
|
||||||
if (pickup)
|
case COPY_FILE:
|
||||||
{
|
if (!pickup)
|
||||||
/* We want to pick it up */
|
{
|
||||||
(void) pq_getbyte();
|
/* We don't want to pick it up - so put it back in there */
|
||||||
}
|
ungetc(c, copy_file);
|
||||||
/* If we didn't want to pick it up, just leave it where it sits */
|
}
|
||||||
}
|
/* If we wanted to pick it up, it's already done */
|
||||||
else
|
break;
|
||||||
{
|
case COPY_OLD_FE:
|
||||||
if (!pickup)
|
if (pickup)
|
||||||
{
|
{
|
||||||
/* We don't want to pick it up - so put it back in there */
|
/* We want to pick it up */
|
||||||
ungetc(c, fp);
|
(void) pq_getbyte();
|
||||||
}
|
}
|
||||||
/* If we wanted to pick it up, it's already done */
|
/* If we didn't want to pick it up, just leave it where it sits */
|
||||||
|
break;
|
||||||
|
case COPY_NEW_FE:
|
||||||
|
if (!pickup)
|
||||||
|
{
|
||||||
|
/* We don't want to pick it up - so put it back in there */
|
||||||
|
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;
|
bool oids = false;
|
||||||
char *delim = NULL;
|
char *delim = NULL;
|
||||||
char *null_print = NULL;
|
char *null_print = NULL;
|
||||||
FILE *fp;
|
|
||||||
Relation rel;
|
Relation rel;
|
||||||
AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT);
|
AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT);
|
||||||
AclResult aclresult;
|
AclResult aclresult;
|
||||||
@ -397,6 +577,11 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
client_encoding = pg_get_client_encoding();
|
client_encoding = pg_get_client_encoding();
|
||||||
server_encoding = GetDatabaseEncoding();
|
server_encoding = GetDatabaseEncoding();
|
||||||
|
|
||||||
|
copy_dest = COPY_FILE; /* default */
|
||||||
|
copy_file = NULL;
|
||||||
|
copy_msgbuf = NULL;
|
||||||
|
fe_eof = false;
|
||||||
|
|
||||||
if (is_from)
|
if (is_from)
|
||||||
{ /* copy from file to database */
|
{ /* copy from file to database */
|
||||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||||
@ -414,33 +599,30 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
if (pipe)
|
if (pipe)
|
||||||
{
|
{
|
||||||
if (IsUnderPostmaster)
|
if (IsUnderPostmaster)
|
||||||
{
|
ReceiveCopyBegin(binary);
|
||||||
ReceiveCopyBegin();
|
|
||||||
fp = NULL;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
fp = stdin;
|
copy_file = stdin;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct stat st;
|
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 "
|
elog(ERROR, "COPY command, running in backend with "
|
||||||
"effective uid %d, could not open file '%s' for "
|
"effective uid %d, could not open file '%s' for "
|
||||||
"reading. Errno = %s (%d).",
|
"reading. Errno = %s (%d).",
|
||||||
(int) geteuid(), filename, strerror(errno), errno);
|
(int) geteuid(), filename, strerror(errno), errno);
|
||||||
|
|
||||||
fstat(fileno(fp), &st);
|
fstat(fileno(copy_file), &st);
|
||||||
if (S_ISDIR(st.st_mode))
|
if (S_ISDIR(st.st_mode))
|
||||||
{
|
{
|
||||||
FreeFile(fp);
|
FreeFile(copy_file);
|
||||||
elog(ERROR, "COPY: %s is a directory", filename);
|
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
|
else
|
||||||
{ /* copy from database to file */
|
{ /* copy from database to file */
|
||||||
@ -459,13 +641,9 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
if (pipe)
|
if (pipe)
|
||||||
{
|
{
|
||||||
if (IsUnderPostmaster)
|
if (IsUnderPostmaster)
|
||||||
{
|
SendCopyBegin(binary);
|
||||||
SendCopyBegin();
|
|
||||||
pq_startcopyout();
|
|
||||||
fp = NULL;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
fp = stdout;
|
copy_file = stdout;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -481,33 +659,28 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
" COPY command");
|
" COPY command");
|
||||||
|
|
||||||
oumask = umask((mode_t) 022);
|
oumask = umask((mode_t) 022);
|
||||||
fp = AllocateFile(filename, PG_BINARY_W);
|
copy_file = AllocateFile(filename, PG_BINARY_W);
|
||||||
umask(oumask);
|
umask(oumask);
|
||||||
|
|
||||||
if (fp == NULL)
|
if (copy_file == NULL)
|
||||||
elog(ERROR, "COPY command, running in backend with "
|
elog(ERROR, "COPY command, running in backend with "
|
||||||
"effective uid %d, could not open file '%s' for "
|
"effective uid %d, could not open file '%s' for "
|
||||||
"writing. Errno = %s (%d).",
|
"writing. Errno = %s (%d).",
|
||||||
(int) geteuid(), filename, strerror(errno), errno);
|
(int) geteuid(), filename, strerror(errno), errno);
|
||||||
fstat(fileno(fp), &st);
|
fstat(fileno(copy_file), &st);
|
||||||
if (S_ISDIR(st.st_mode))
|
if (S_ISDIR(st.st_mode))
|
||||||
{
|
{
|
||||||
FreeFile(fp);
|
FreeFile(copy_file);
|
||||||
elog(ERROR, "COPY: %s is a directory", filename);
|
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)
|
if (!pipe)
|
||||||
FreeFile(fp);
|
FreeFile(copy_file);
|
||||||
else if (!is_from)
|
else if (IsUnderPostmaster && !is_from)
|
||||||
{
|
SendCopyEnd(binary);
|
||||||
if (!binary)
|
|
||||||
CopySendData("\\.\n", 3, fp);
|
|
||||||
if (IsUnderPostmaster)
|
|
||||||
pq_endcopyout(false);
|
|
||||||
}
|
|
||||||
pfree(attribute_buf.data);
|
pfree(attribute_buf.data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -525,7 +698,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||||
FILE *fp, char *delim, char *null_print)
|
char *delim, char *null_print)
|
||||||
{
|
{
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
@ -589,18 +762,18 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
int32 tmp;
|
int32 tmp;
|
||||||
|
|
||||||
/* Signature */
|
/* Signature */
|
||||||
CopySendData((char *) BinarySignature, 12, fp);
|
CopySendData((char *) BinarySignature, 12);
|
||||||
/* Integer layout field */
|
/* Integer layout field */
|
||||||
tmp = 0x01020304;
|
tmp = 0x01020304;
|
||||||
CopySendData(&tmp, sizeof(int32), fp);
|
CopySendData(&tmp, sizeof(int32));
|
||||||
/* Flags field */
|
/* Flags field */
|
||||||
tmp = 0;
|
tmp = 0;
|
||||||
if (oids)
|
if (oids)
|
||||||
tmp |= (1 << 16);
|
tmp |= (1 << 16);
|
||||||
CopySendData(&tmp, sizeof(int32), fp);
|
CopySendData(&tmp, sizeof(int32));
|
||||||
/* No header extension */
|
/* No header extension */
|
||||||
tmp = 0;
|
tmp = 0;
|
||||||
CopySendData(&tmp, sizeof(int32), fp);
|
CopySendData(&tmp, sizeof(int32));
|
||||||
}
|
}
|
||||||
|
|
||||||
mySnapshot = CopyQuerySnapshot();
|
mySnapshot = CopyQuerySnapshot();
|
||||||
@ -621,15 +794,15 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
/* Binary per-tuple header */
|
/* Binary per-tuple header */
|
||||||
int16 fld_count = attr_count;
|
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 */
|
/* Send OID if wanted --- note fld_count doesn't include it */
|
||||||
if (oids)
|
if (oids)
|
||||||
{
|
{
|
||||||
Oid oid = HeapTupleGetOid(tuple);
|
Oid oid = HeapTupleGetOid(tuple);
|
||||||
|
|
||||||
fld_size = sizeof(Oid);
|
fld_size = sizeof(Oid);
|
||||||
CopySendData(&fld_size, sizeof(int16), fp);
|
CopySendData(&fld_size, sizeof(int16));
|
||||||
CopySendData(&oid, sizeof(Oid), fp);
|
CopySendData(&oid, sizeof(Oid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -639,7 +812,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
{
|
{
|
||||||
string = DatumGetCString(DirectFunctionCall1(oidout,
|
string = DatumGetCString(DirectFunctionCall1(oidout,
|
||||||
ObjectIdGetDatum(HeapTupleGetOid(tuple))));
|
ObjectIdGetDatum(HeapTupleGetOid(tuple))));
|
||||||
CopySendString(string, fp);
|
CopySendString(string);
|
||||||
need_delim = true;
|
need_delim = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -655,7 +828,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
if (!binary)
|
if (!binary)
|
||||||
{
|
{
|
||||||
if (need_delim)
|
if (need_delim)
|
||||||
CopySendChar(delim[0], fp);
|
CopySendChar(delim[0]);
|
||||||
need_delim = true;
|
need_delim = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,12 +836,12 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
{
|
{
|
||||||
if (!binary)
|
if (!binary)
|
||||||
{
|
{
|
||||||
CopySendString(null_print, fp); /* null indicator */
|
CopySendString(null_print); /* null indicator */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fld_size = 0; /* null marker */
|
fld_size = 0; /* null marker */
|
||||||
CopySendData(&fld_size, sizeof(int16), fp);
|
CopySendData(&fld_size, sizeof(int16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -679,12 +852,12 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
value,
|
value,
|
||||||
ObjectIdGetDatum(elements[attnum - 1]),
|
ObjectIdGetDatum(elements[attnum - 1]),
|
||||||
Int32GetDatum(attr[attnum - 1]->atttypmod)));
|
Int32GetDatum(attr[attnum - 1]->atttypmod)));
|
||||||
CopyAttributeOut(fp, string, delim);
|
CopyAttributeOut(string, delim);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fld_size = attr[attnum - 1]->attlen;
|
fld_size = attr[attnum - 1]->attlen;
|
||||||
CopySendData(&fld_size, sizeof(int16), fp);
|
CopySendData(&fld_size, sizeof(int16));
|
||||||
if (isvarlena[attnum - 1])
|
if (isvarlena[attnum - 1])
|
||||||
{
|
{
|
||||||
/* varlena */
|
/* varlena */
|
||||||
@ -694,16 +867,14 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
value = PointerGetDatum(PG_DETOAST_DATUM(value));
|
value = PointerGetDatum(PG_DETOAST_DATUM(value));
|
||||||
|
|
||||||
CopySendData(DatumGetPointer(value),
|
CopySendData(DatumGetPointer(value),
|
||||||
VARSIZE(value),
|
VARSIZE(value));
|
||||||
fp);
|
|
||||||
}
|
}
|
||||||
else if (!attr[attnum - 1]->attbyval)
|
else if (!attr[attnum - 1]->attbyval)
|
||||||
{
|
{
|
||||||
/* fixed-length pass-by-reference */
|
/* fixed-length pass-by-reference */
|
||||||
Assert(fld_size > 0);
|
Assert(fld_size > 0);
|
||||||
CopySendData(DatumGetPointer(value),
|
CopySendData(DatumGetPointer(value),
|
||||||
fld_size,
|
fld_size);
|
||||||
fp);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -717,15 +888,14 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
*/
|
*/
|
||||||
store_att_byval(&datumBuf, value, fld_size);
|
store_att_byval(&datumBuf, value, fld_size);
|
||||||
CopySendData(&datumBuf,
|
CopySendData(&datumBuf,
|
||||||
fld_size,
|
fld_size);
|
||||||
fp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!binary)
|
if (!binary)
|
||||||
CopySendChar('\n', fp);
|
CopySendChar('\n');
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
}
|
}
|
||||||
@ -737,7 +907,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
/* Generate trailer for a binary copy */
|
/* Generate trailer for a binary copy */
|
||||||
int16 fld_count = -1;
|
int16 fld_count = -1;
|
||||||
|
|
||||||
CopySendData(&fld_count, sizeof(int16), fp);
|
CopySendData(&fld_count, sizeof(int16));
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryContextDelete(mycontext);
|
MemoryContextDelete(mycontext);
|
||||||
@ -753,7 +923,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||||
FILE *fp, char *delim, char *null_print)
|
char *delim, char *null_print)
|
||||||
{
|
{
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
@ -905,30 +1075,30 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
int32 tmp;
|
int32 tmp;
|
||||||
|
|
||||||
/* Signature */
|
/* Signature */
|
||||||
CopyGetData(readSig, 12, fp);
|
CopyGetData(readSig, 12);
|
||||||
if (CopyGetEof(fp) || memcmp(readSig, BinarySignature, 12) != 0)
|
if (CopyGetEof() || memcmp(readSig, BinarySignature, 12) != 0)
|
||||||
elog(ERROR, "COPY BINARY: file signature not recognized");
|
elog(ERROR, "COPY BINARY: file signature not recognized");
|
||||||
/* Integer layout field */
|
/* Integer layout field */
|
||||||
CopyGetData(&tmp, sizeof(int32), fp);
|
CopyGetData(&tmp, sizeof(int32));
|
||||||
if (CopyGetEof(fp) || tmp != 0x01020304)
|
if (CopyGetEof() || tmp != 0x01020304)
|
||||||
elog(ERROR, "COPY BINARY: incompatible integer layout");
|
elog(ERROR, "COPY BINARY: incompatible integer layout");
|
||||||
/* Flags field */
|
/* Flags field */
|
||||||
CopyGetData(&tmp, sizeof(int32), fp);
|
CopyGetData(&tmp, sizeof(int32));
|
||||||
if (CopyGetEof(fp))
|
if (CopyGetEof())
|
||||||
elog(ERROR, "COPY BINARY: bogus file header (missing flags)");
|
elog(ERROR, "COPY BINARY: bogus file header (missing flags)");
|
||||||
file_has_oids = (tmp & (1 << 16)) != 0;
|
file_has_oids = (tmp & (1 << 16)) != 0;
|
||||||
tmp &= ~(1 << 16);
|
tmp &= ~(1 << 16);
|
||||||
if ((tmp >> 16) != 0)
|
if ((tmp >> 16) != 0)
|
||||||
elog(ERROR, "COPY BINARY: unrecognized critical flags in header");
|
elog(ERROR, "COPY BINARY: unrecognized critical flags in header");
|
||||||
/* Header extension length */
|
/* Header extension length */
|
||||||
CopyGetData(&tmp, sizeof(int32), fp);
|
CopyGetData(&tmp, sizeof(int32));
|
||||||
if (CopyGetEof(fp) || tmp < 0)
|
if (CopyGetEof() || tmp < 0)
|
||||||
elog(ERROR, "COPY BINARY: bogus file header (missing length)");
|
elog(ERROR, "COPY BINARY: bogus file header (missing length)");
|
||||||
/* Skip extension header, if present */
|
/* Skip extension header, if present */
|
||||||
while (tmp-- > 0)
|
while (tmp-- > 0)
|
||||||
{
|
{
|
||||||
CopyGetData(readSig, 1, fp);
|
CopyGetData(readSig, 1);
|
||||||
if (CopyGetEof(fp))
|
if (CopyGetEof())
|
||||||
elog(ERROR, "COPY BINARY: bogus file header (wrong length)");
|
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));
|
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
|
||||||
nulls = (char *) palloc(num_phys_attrs * sizeof(char));
|
nulls = (char *) palloc(num_phys_attrs * sizeof(char));
|
||||||
|
|
||||||
|
/* Initialize static variables */
|
||||||
copy_lineno = 0;
|
copy_lineno = 0;
|
||||||
fe_eof = false;
|
fe_eof = false;
|
||||||
|
|
||||||
@ -970,7 +1141,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
|
|
||||||
if (file_has_oids)
|
if (file_has_oids)
|
||||||
{
|
{
|
||||||
string = CopyReadAttribute(fp, delim, &result);
|
string = CopyReadAttribute(delim, &result);
|
||||||
|
|
||||||
if (result == END_OF_FILE && *string == '\0')
|
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\"",
|
elog(ERROR, "Missing data for column \"%s\"",
|
||||||
NameStr(attr[m]->attname));
|
NameStr(attr[m]->attname));
|
||||||
|
|
||||||
string = CopyReadAttribute(fp, delim, &result);
|
string = CopyReadAttribute(delim, &result);
|
||||||
|
|
||||||
if (result == END_OF_FILE && *string == '\0' &&
|
if (result == END_OF_FILE && *string == '\0' &&
|
||||||
cur == attnumlist && !file_has_oids)
|
cur == attnumlist && !file_has_oids)
|
||||||
@ -1051,8 +1222,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
int16 fld_count,
|
int16 fld_count,
|
||||||
fld_size;
|
fld_size;
|
||||||
|
|
||||||
CopyGetData(&fld_count, sizeof(int16), fp);
|
CopyGetData(&fld_count, sizeof(int16));
|
||||||
if (CopyGetEof(fp) || fld_count == -1)
|
if (CopyGetEof() || fld_count == -1)
|
||||||
{
|
{
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
@ -1064,14 +1235,14 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
|
|
||||||
if (file_has_oids)
|
if (file_has_oids)
|
||||||
{
|
{
|
||||||
CopyGetData(&fld_size, sizeof(int16), fp);
|
CopyGetData(&fld_size, sizeof(int16));
|
||||||
if (CopyGetEof(fp))
|
if (CopyGetEof())
|
||||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||||
if (fld_size != (int16) sizeof(Oid))
|
if (fld_size != (int16) sizeof(Oid))
|
||||||
elog(ERROR, "COPY BINARY: sizeof(Oid) is %d, expected %d",
|
elog(ERROR, "COPY BINARY: sizeof(Oid) is %d, expected %d",
|
||||||
(int) fld_size, (int) sizeof(Oid));
|
(int) fld_size, (int) sizeof(Oid));
|
||||||
CopyGetData(&loaded_oid, sizeof(Oid), fp);
|
CopyGetData(&loaded_oid, sizeof(Oid));
|
||||||
if (CopyGetEof(fp))
|
if (CopyGetEof())
|
||||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||||
if (loaded_oid == InvalidOid)
|
if (loaded_oid == InvalidOid)
|
||||||
elog(ERROR, "COPY BINARY: Invalid Oid");
|
elog(ERROR, "COPY BINARY: Invalid Oid");
|
||||||
@ -1085,8 +1256,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
|
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
CopyGetData(&fld_size, sizeof(int16), fp);
|
CopyGetData(&fld_size, sizeof(int16));
|
||||||
if (CopyGetEof(fp))
|
if (CopyGetEof())
|
||||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||||
if (fld_size == 0)
|
if (fld_size == 0)
|
||||||
continue; /* it's NULL; nulls[attnum-1] already set */
|
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;
|
int32 varlena_size;
|
||||||
Pointer varlena_ptr;
|
Pointer varlena_ptr;
|
||||||
|
|
||||||
CopyGetData(&varlena_size, sizeof(int32), fp);
|
CopyGetData(&varlena_size, sizeof(int32));
|
||||||
if (CopyGetEof(fp))
|
if (CopyGetEof())
|
||||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||||
if (varlena_size < (int32) sizeof(int32))
|
if (varlena_size < (int32) sizeof(int32))
|
||||||
elog(ERROR, "COPY BINARY: bogus varlena length");
|
elog(ERROR, "COPY BINARY: bogus varlena length");
|
||||||
varlena_ptr = (Pointer) palloc(varlena_size);
|
varlena_ptr = (Pointer) palloc(varlena_size);
|
||||||
VARATT_SIZEP(varlena_ptr) = varlena_size;
|
VARATT_SIZEP(varlena_ptr) = varlena_size;
|
||||||
CopyGetData(VARDATA(varlena_ptr),
|
CopyGetData(VARDATA(varlena_ptr),
|
||||||
varlena_size - sizeof(int32),
|
varlena_size - sizeof(int32));
|
||||||
fp);
|
if (CopyGetEof())
|
||||||
if (CopyGetEof(fp))
|
|
||||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||||
values[m] = PointerGetDatum(varlena_ptr);
|
values[m] = PointerGetDatum(varlena_ptr);
|
||||||
}
|
}
|
||||||
@ -1120,8 +1290,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||||||
|
|
||||||
Assert(fld_size > 0);
|
Assert(fld_size > 0);
|
||||||
refval_ptr = (Pointer) palloc(fld_size);
|
refval_ptr = (Pointer) palloc(fld_size);
|
||||||
CopyGetData(refval_ptr, fld_size, fp);
|
CopyGetData(refval_ptr, fld_size);
|
||||||
if (CopyGetEof(fp))
|
if (CopyGetEof())
|
||||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||||
values[m] = PointerGetDatum(refval_ptr);
|
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.
|
* how shorter data values are aligned within a Datum.
|
||||||
*/
|
*/
|
||||||
Assert(fld_size > 0 && fld_size <= sizeof(Datum));
|
Assert(fld_size > 0 && fld_size <= sizeof(Datum));
|
||||||
CopyGetData(&datumBuf, fld_size, fp);
|
CopyGetData(&datumBuf, fld_size);
|
||||||
if (CopyGetEof(fp))
|
if (CopyGetEof())
|
||||||
elog(ERROR, "COPY BINARY: unexpected EOF");
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
||||||
values[m] = fetch_att(&datumBuf, true, fld_size);
|
values[m] = fetch_att(&datumBuf, true, fld_size);
|
||||||
}
|
}
|
||||||
@ -1324,7 +1494,7 @@ GetTypeElement(Oid type)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
CopyReadAttribute(const char *delim, CopyReadResult *result)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
int delimc = (unsigned char) delim[0];
|
int delimc = (unsigned char) delim[0];
|
||||||
@ -1344,7 +1514,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
|||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
c = CopyGetChar(fp);
|
c = CopyGetChar();
|
||||||
if (c == EOF)
|
if (c == EOF)
|
||||||
{
|
{
|
||||||
*result = END_OF_FILE;
|
*result = END_OF_FILE;
|
||||||
@ -1359,7 +1529,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
|||||||
break;
|
break;
|
||||||
if (c == '\\')
|
if (c == '\\')
|
||||||
{
|
{
|
||||||
c = CopyGetChar(fp);
|
c = CopyGetChar();
|
||||||
if (c == EOF)
|
if (c == EOF)
|
||||||
{
|
{
|
||||||
*result = END_OF_FILE;
|
*result = END_OF_FILE;
|
||||||
@ -1379,16 +1549,16 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
|||||||
int val;
|
int val;
|
||||||
|
|
||||||
val = OCTVALUE(c);
|
val = OCTVALUE(c);
|
||||||
c = CopyPeekChar(fp);
|
c = CopyPeekChar();
|
||||||
if (ISOCTAL(c))
|
if (ISOCTAL(c))
|
||||||
{
|
{
|
||||||
val = (val << 3) + OCTVALUE(c);
|
val = (val << 3) + OCTVALUE(c);
|
||||||
CopyDonePeek(fp, c, true /* pick up */ );
|
CopyDonePeek(c, true /* pick up */ );
|
||||||
c = CopyPeekChar(fp);
|
c = CopyPeekChar();
|
||||||
if (ISOCTAL(c))
|
if (ISOCTAL(c))
|
||||||
{
|
{
|
||||||
val = (val << 3) + OCTVALUE(c);
|
val = (val << 3) + OCTVALUE(c);
|
||||||
CopyDonePeek(fp, c, true /* pick up */ );
|
CopyDonePeek(c, true /* pick up */ );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1397,7 +1567,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
|||||||
*result = END_OF_FILE;
|
*result = END_OF_FILE;
|
||||||
goto copy_eof;
|
goto copy_eof;
|
||||||
}
|
}
|
||||||
CopyDonePeek(fp, c, false /* put back */ );
|
CopyDonePeek(c, false /* put back */ );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1407,7 +1577,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
|||||||
*result = END_OF_FILE;
|
*result = END_OF_FILE;
|
||||||
goto copy_eof;
|
goto copy_eof;
|
||||||
}
|
}
|
||||||
CopyDonePeek(fp, c, false /* put back */ );
|
CopyDonePeek(c, false /* put back */ );
|
||||||
}
|
}
|
||||||
c = val & 0377;
|
c = val & 0377;
|
||||||
}
|
}
|
||||||
@ -1441,9 +1611,21 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
|||||||
c = '\v';
|
c = '\v';
|
||||||
break;
|
break;
|
||||||
case '.':
|
case '.':
|
||||||
c = CopyGetChar(fp);
|
c = CopyGetChar();
|
||||||
if (c != '\n')
|
if (c != '\n')
|
||||||
elog(ERROR, "CopyReadAttribute: end of record marker corrupted");
|
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;
|
*result = END_OF_FILE;
|
||||||
goto copy_eof;
|
goto copy_eof;
|
||||||
}
|
}
|
||||||
@ -1458,7 +1640,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
|
|||||||
mblen = pg_encoding_mblen(client_encoding, s);
|
mblen = pg_encoding_mblen(client_encoding, s);
|
||||||
for (j = 1; j < mblen; j++)
|
for (j = 1; j < mblen; j++)
|
||||||
{
|
{
|
||||||
c = CopyGetChar(fp);
|
c = CopyGetChar();
|
||||||
if (c == EOF)
|
if (c == EOF)
|
||||||
{
|
{
|
||||||
*result = END_OF_FILE;
|
*result = END_OF_FILE;
|
||||||
@ -1488,7 +1670,7 @@ copy_eof:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
CopyAttributeOut(FILE *fp, char *server_string, char *delim)
|
CopyAttributeOut(char *server_string, char *delim)
|
||||||
{
|
{
|
||||||
char *string;
|
char *string;
|
||||||
char c;
|
char c;
|
||||||
@ -1511,30 +1693,30 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim)
|
|||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case '\b':
|
case '\b':
|
||||||
CopySendString("\\b", fp);
|
CopySendString("\\b");
|
||||||
break;
|
break;
|
||||||
case '\f':
|
case '\f':
|
||||||
CopySendString("\\f", fp);
|
CopySendString("\\f");
|
||||||
break;
|
break;
|
||||||
case '\n':
|
case '\n':
|
||||||
CopySendString("\\n", fp);
|
CopySendString("\\n");
|
||||||
break;
|
break;
|
||||||
case '\r':
|
case '\r':
|
||||||
CopySendString("\\r", fp);
|
CopySendString("\\r");
|
||||||
break;
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
CopySendString("\\t", fp);
|
CopySendString("\\t");
|
||||||
break;
|
break;
|
||||||
case '\v':
|
case '\v':
|
||||||
CopySendString("\\v", fp);
|
CopySendString("\\v");
|
||||||
break;
|
break;
|
||||||
case '\\':
|
case '\\':
|
||||||
CopySendString("\\\\", fp);
|
CopySendString("\\\\");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (c == delimc)
|
if (c == delimc)
|
||||||
CopySendChar('\\', fp);
|
CopySendChar('\\');
|
||||||
CopySendChar(c, fp);
|
CopySendChar(c);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can skip pg_encoding_mblen() overhead when encoding
|
* 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 */
|
/* send additional bytes of the char, if any */
|
||||||
mblen = pg_encoding_mblen(client_encoding, string);
|
mblen = pg_encoding_mblen(client_encoding, string);
|
||||||
for (i = 1; i < mblen; i++)
|
for (i = 1; i < mblen; i++)
|
||||||
CopySendChar(string[i], fp);
|
CopySendChar(string[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,15 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 "postgres.h"
|
||||||
|
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* makeStringInfo
|
* makeStringInfo
|
||||||
*
|
*
|
||||||
@ -50,41 +50,7 @@ initStringInfo(StringInfo str)
|
|||||||
str->maxlen = size;
|
str->maxlen = size;
|
||||||
str->len = 0;
|
str->len = 0;
|
||||||
str->data[0] = '\0';
|
str->data[0] = '\0';
|
||||||
}
|
str->cursor = 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -147,8 +113,9 @@ appendStringInfo(StringInfo str, const char *fmt,...)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*------------------------
|
/*
|
||||||
* appendStringInfoChar
|
* appendStringInfoChar
|
||||||
|
*
|
||||||
* Append a single byte to str.
|
* Append a single byte to str.
|
||||||
* Like appendStringInfo(str, "%c", ch) but much faster.
|
* 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';
|
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
|
* 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 sendAuthRequest(Port *port, AuthRequest areq);
|
||||||
static void auth_failed(Port *port, int status);
|
static void auth_failed(Port *port, int status);
|
||||||
|
static char *recv_password_packet(Port *port);
|
||||||
static int recv_and_check_password_packet(Port *port);
|
static int recv_and_check_password_packet(Port *port);
|
||||||
|
|
||||||
char *pg_krb_server_keyfile;
|
char *pg_krb_server_keyfile;
|
||||||
@ -539,11 +540,9 @@ sendAuthRequest(Port *port, AuthRequest areq)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
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)
|
if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
|
||||||
{
|
{
|
||||||
switch (msg[0]->msg_style)
|
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)
|
if (strlen(appdata_ptr) == 0)
|
||||||
{
|
{
|
||||||
|
char *passwd;
|
||||||
|
|
||||||
sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
|
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 */
|
return PAM_CONV_ERR; /* client didn't want to send password */
|
||||||
|
|
||||||
initStringInfo(&buf);
|
if (strlen(passwd) == 0)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
elog(LOG, "pam_passwd_conv_proc: no password");
|
elog(LOG, "pam_passwd_conv_proc: no password");
|
||||||
return PAM_CONV_ERR;
|
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)
|
if (!*resp)
|
||||||
{
|
{
|
||||||
elog(LOG, "pam_passwd_conv_proc: Out of memory!");
|
elog(LOG, "pam_passwd_conv_proc: Out of memory!");
|
||||||
if (buf.data)
|
|
||||||
pfree(buf.data);
|
|
||||||
return PAM_CONV_ERR;
|
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
|
static char *
|
||||||
recv_and_check_password_packet(Port *port)
|
recv_password_packet(Port *port)
|
||||||
{
|
{
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
int32 len;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
|
if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
|
||||||
return STATUS_EOF; /* client didn't want to send password */
|
{
|
||||||
|
/* 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);
|
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);
|
pfree(buf.data);
|
||||||
return STATUS_EOF;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't actually use the password packet length the frontend sent
|
* Apply sanity check: password packet length should agree with length
|
||||||
* us; however, it's a reasonable sanity check to ensure that we
|
* of contained string. Note it is safe to use strlen here because
|
||||||
* actually read as much data as we expected to.
|
* StringInfo is guaranteed to have an appended '\0'.
|
||||||
*
|
|
||||||
* The password packet size is the length of the buffer, plus the size
|
|
||||||
* field itself (4 bytes), plus a 1-byte terminator.
|
|
||||||
*/
|
*/
|
||||||
if (len != (buf.len + 4 + 1))
|
if (strlen(buf.data) + 1 != buf.len)
|
||||||
elog(LOG, "unexpected password packet size: read %d, expected %d",
|
elog(COMMERROR, "bogus password packet size");
|
||||||
buf.len + 4 + 1, len);
|
|
||||||
|
|
||||||
/* Do not echo password to logs, for security. */
|
/* Do not echo password to logs, for security. */
|
||||||
elog(DEBUG5, "received password packet");
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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)
|
* Since the server static private key ($DataDir/server.key)
|
||||||
* will normally be stored unencrypted so that the database
|
* will normally be stored unencrypted so that the database
|
||||||
@ -110,13 +110,6 @@
|
|||||||
extern void ExitPostmaster(int);
|
extern void ExitPostmaster(int);
|
||||||
extern void postmaster_error(const char *fmt,...);
|
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
|
#ifdef USE_SSL
|
||||||
static DH *load_dh_file(int keylength);
|
static DH *load_dh_file(int keylength);
|
||||||
static DH *load_dh_buffer(const char *, size_t);
|
static DH *load_dh_buffer(const char *, size_t);
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
* These routines handle the low-level details of communication between
|
* These routines handle the low-level details of communication between
|
||||||
* frontend and backend. They just shove data across the communication
|
* frontend and backend. They just shove data across the communication
|
||||||
* channel, and are ignorant of the semantics of the data --- or would be,
|
* 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.
|
* except for major brain damage in the design of the old COPY OUT protocol.
|
||||||
* Unfortunately, COPY OUT is designed to commandeer the communication
|
* Unfortunately, COPY OUT was designed to commandeer the communication
|
||||||
* channel (it just transfers data without wrapping it into messages).
|
* 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
|
* 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
|
* 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) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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:
|
* low-level I/O:
|
||||||
* pq_getbytes - get a known number of bytes from connection
|
* pq_getbytes - get a known number of bytes from connection
|
||||||
* pq_getstring - get a null terminated string 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_getbyte - get next byte from connection
|
||||||
* pq_peekbyte - peek at next byte from connection
|
* pq_peekbyte - peek at next byte from connection
|
||||||
* pq_putbytes - send bytes to connection (not flushed until pq_flush)
|
* pq_putbytes - send bytes to connection (not flushed until pq_flush)
|
||||||
* pq_flush - flush pending output
|
* 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_putmessage - send a normal message (suppressed in COPY OUT mode)
|
||||||
* pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
|
* pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
|
||||||
* pq_endcopyout - end a COPY OUT transfer
|
* pq_endcopyout - end a COPY OUT transfer
|
||||||
@ -85,9 +86,6 @@
|
|||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/ipc.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);
|
static void pq_close(void);
|
||||||
|
|
||||||
@ -562,8 +560,10 @@ pq_recvbuf(void)
|
|||||||
}
|
}
|
||||||
if (r == 0)
|
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;
|
return EOF;
|
||||||
}
|
}
|
||||||
/* r contains number of bytes read, so just incr length */
|
/* 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
|
* pq_getstring - get a null terminated string from connection
|
||||||
*
|
*
|
||||||
* The return value is placed in an expansible StringInfo.
|
* The return value is placed in an expansible StringInfo, which has
|
||||||
* Note that space allocation comes from the current memory context!
|
* already been initialized by the caller.
|
||||||
*
|
*
|
||||||
* If maxlen is not zero, it is an upper limit on the length of the
|
* This is used only for dealing with old-protocol clients. The idea
|
||||||
* string we are willing to accept. We abort the connection (by
|
* is to produce a StringInfo that looks the same as we would get from
|
||||||
* returning EOF) if client tries to send more than that. Note that
|
* pq_getmessage() with a newer client; we will then process it with
|
||||||
* since we test maxlen in the outer per-bufferload loop, the limit
|
* pq_getmsgstring. Therefore, no character set conversion is done here,
|
||||||
* is fuzzy: we might accept up to PQ_BUFFER_SIZE more bytes than
|
* even though this is presumably useful only for text.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* returns 0 if OK, EOF if trouble
|
* returns 0 if OK, EOF if trouble
|
||||||
* --------------------------------
|
* --------------------------------
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
pq_getstring(StringInfo s, int maxlen)
|
pq_getstring(StringInfo s)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Reset string to empty */
|
/* Reset string to empty */
|
||||||
s->len = 0;
|
s->len = 0;
|
||||||
s->data[0] = '\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 (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
while (PqRecvPointer >= PqRecvLength)
|
while (PqRecvPointer >= PqRecvLength)
|
||||||
@ -677,9 +671,9 @@ pq_getstring(StringInfo s, int maxlen)
|
|||||||
{
|
{
|
||||||
if (PqRecvBuffer[i] == '\0')
|
if (PqRecvBuffer[i] == '\0')
|
||||||
{
|
{
|
||||||
/* does not copy the \0 */
|
/* include the '\0' in the copy */
|
||||||
appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
|
appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
|
||||||
i - PqRecvPointer);
|
i - PqRecvPointer + 1);
|
||||||
PqRecvPointer = i + 1; /* advance past \0 */
|
PqRecvPointer = i + 1; /* advance past \0 */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -689,14 +683,73 @@ pq_getstring(StringInfo s, int maxlen)
|
|||||||
appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
|
appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
|
||||||
PqRecvLength - PqRecvPointer);
|
PqRecvLength - PqRecvPointer);
|
||||||
PqRecvPointer = PqRecvLength;
|
PqRecvPointer = PqRecvLength;
|
||||||
|
|
||||||
/* If maxlen is specified, check for overlength input. */
|
|
||||||
if (maxlen > 0 && s->len > maxlen)
|
|
||||||
return EOF;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
* pq_putbytes - send bytes to connection (not flushed until pq_flush)
|
* pq_putbytes - send bytes to connection (not flushed until pq_flush)
|
||||||
*
|
*
|
||||||
@ -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.
|
* 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
|
void
|
||||||
|
@ -8,15 +8,17 @@
|
|||||||
* formatting/conversion routines that are needed to produce valid messages.
|
* formatting/conversion routines that are needed to produce valid messages.
|
||||||
* Note in particular the distinction between "raw data" and "text"; raw data
|
* Note in particular the distinction between "raw data" and "text"; raw data
|
||||||
* is message protocol characters and binary values that are not subject to
|
* 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
|
* Incoming messages are similarly read into a StringInfo buffer, via
|
||||||
* are still data-conversion tasks to be performed.
|
* 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) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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:
|
* Special-case message output:
|
||||||
* pq_puttextmessage - generate a character set-converted message in one step
|
* pq_puttextmessage - generate a character set-converted message in one step
|
||||||
*
|
*
|
||||||
* Message input:
|
* Message parsing after input:
|
||||||
* pq_getint - get an integer from connection
|
* pq_getmsgbyte - get a raw byte from a message buffer
|
||||||
* pq_getstr_bounded - get a null terminated string from connection
|
* pq_getmsgint - get a binary integer from a message buffer
|
||||||
* pq_getstr_bounded performs character set conversion on the collected
|
* pq_getmsgbytes - get raw data from a message buffer
|
||||||
* string. Use the raw pqcomm.c routines pq_getstring or pq_getbytes
|
* pq_copymsgbytes - copy raw data from a message buffer
|
||||||
* to fetch data without conversion.
|
* pq_getmsgstring - get a null-terminated text string (with conversion)
|
||||||
|
* pq_getmsgend - verify message fully consumed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
@ -206,16 +209,29 @@ pq_puttextmessage(char msgtype, const char *str)
|
|||||||
return pq_putmessage(msgtype, str, slen + 1);
|
return pq_putmessage(msgtype, str, slen + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
* pq_getint - get an integer from connection
|
* pq_getmsgbyte - get a raw byte from a message buffer
|
||||||
*
|
|
||||||
* returns 0 if OK, EOF if trouble
|
|
||||||
* --------------------------------
|
* --------------------------------
|
||||||
*/
|
*/
|
||||||
int
|
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;
|
unsigned char n8;
|
||||||
uint16 n16;
|
uint16 n16;
|
||||||
uint32 n32;
|
uint32 n32;
|
||||||
@ -223,59 +239,93 @@ pq_getint(int *result, int b)
|
|||||||
switch (b)
|
switch (b)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
status = pq_getbytes((char *) &n8, 1);
|
pq_copymsgbytes(msg, (char *) &n8, 1);
|
||||||
*result = (int) n8;
|
result = n8;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
status = pq_getbytes((char *) &n16, 2);
|
pq_copymsgbytes(msg, (char *) &n16, 2);
|
||||||
*result = (int) (ntohs(n16));
|
result = ntohs(n16);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
status = pq_getbytes((char *) &n32, 4);
|
pq_copymsgbytes(msg, (char *) &n32, 4);
|
||||||
*result = (int) (ntohl(n32));
|
result = ntohl(n32);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
elog(ERROR, "pq_getmsgint: unsupported size %d", b);
|
||||||
/*
|
result = 0; /* keep compiler quiet */
|
||||||
* 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;
|
|
||||||
break;
|
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.
|
* Returns a pointer directly into the message buffer; note this
|
||||||
* Note that space allocation comes from the current memory context!
|
* may not have any particular alignment.
|
||||||
*
|
|
||||||
* The maxlen parameter is interpreted as per pq_getstring.
|
|
||||||
*
|
|
||||||
* returns 0 if OK, EOF if trouble
|
|
||||||
* --------------------------------
|
* --------------------------------
|
||||||
*/
|
*/
|
||||||
int
|
const char *
|
||||||
pq_getstr_bounded(StringInfo s, int maxlen)
|
pq_getmsgbytes(StringInfo msg, int datalen)
|
||||||
{
|
{
|
||||||
int result;
|
const char *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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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;
|
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
|
* 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
|
* NOTES
|
||||||
*
|
*
|
||||||
@ -1118,7 +1118,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
|
|||||||
|
|
||||||
if (pq_getbytes((char *) &len, 4) == EOF)
|
if (pq_getbytes((char *) &len, 4) == EOF)
|
||||||
{
|
{
|
||||||
elog(LOG, "incomplete startup packet");
|
elog(COMMERROR, "incomplete startup packet");
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1142,7 +1142,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
|
|||||||
|
|
||||||
if (pq_getbytes(buf, len) == EOF)
|
if (pq_getbytes(buf, len) == EOF)
|
||||||
{
|
{
|
||||||
elog(LOG, "incomplete startup packet");
|
elog(COMMERROR, "incomplete startup packet");
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1189,6 +1189,16 @@ ProcessStartupPacket(Port *port, bool SSLdone)
|
|||||||
/* Could add additional special packet types here */
|
/* 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. */
|
/* Check we can handle the protocol the frontend is using. */
|
||||||
|
|
||||||
if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
|
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_MAJOR(PG_PROTOCOL_LATEST),
|
||||||
PG_PROTOCOL_MINOR(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
|
* Now fetch parameters out of startup packet and save them into the
|
||||||
* Port structure. All data structures attached to the Port struct
|
* Port structure. All data structures attached to the Port struct
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NullCommand - tell dest that an empty query string was recognized
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* This cruft is the server side of PQfn.
|
* This cruft is the server side of PQfn.
|
||||||
@ -28,38 +28,13 @@
|
|||||||
* back to the frontend. If the return returns by reference,
|
* back to the frontend. If the return returns by reference,
|
||||||
* send down only the data portion and set the return size appropriately.
|
* 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 "postgres.h"
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "libpq/libpq.h"
|
#include "libpq/libpq.h"
|
||||||
@ -72,6 +47,67 @@
|
|||||||
#include "utils/tqual.h"
|
#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
|
* 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).
|
* Server side of PQfn (fastpath function calls from the frontend).
|
||||||
* This corresponds to the libpq protocol symbol "F".
|
* 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:
|
* RETURNS:
|
||||||
* 0 if successful completion, EOF if frontend connection lost.
|
* 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.
|
* control returns to PostgresMain.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
HandleFunctionRequest(void)
|
HandleFunctionRequest(StringInfo msgBuf)
|
||||||
{
|
{
|
||||||
Oid fid;
|
Oid fid;
|
||||||
int argsize;
|
|
||||||
int nargs;
|
int nargs;
|
||||||
int tmp;
|
|
||||||
AclResult aclresult;
|
AclResult aclresult;
|
||||||
FunctionCallInfoData fcinfo;
|
FunctionCallInfoData fcinfo;
|
||||||
Datum retval;
|
Datum retval;
|
||||||
int i;
|
int i;
|
||||||
char *p;
|
|
||||||
struct fp_info my_fp;
|
struct fp_info my_fp;
|
||||||
struct fp_info *fip;
|
struct fp_info *fip;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX FIXME: This protocol is misdesigned.
|
* Read message contents if not already done.
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
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;
|
* Now that we've eaten the input message, check to see if we actually
|
||||||
fid = (Oid) tmp;
|
* want to do the function call or not. It's now safe to elog(); we won't
|
||||||
if (pq_getint(&nargs, 4)) /* # of arguments */
|
* lose sync with the frontend.
|
||||||
return EOF;
|
*/
|
||||||
|
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
|
* There used to be a lame attempt at caching lookup info here. Now we
|
||||||
@ -274,11 +306,14 @@ HandleFunctionRequest(void)
|
|||||||
fip = &my_fp;
|
fip = &my_fp;
|
||||||
fetch_fp_info(fid, fip);
|
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)
|
if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
|
||||||
{
|
|
||||||
elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
|
elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
|
||||||
nargs, fip->flinfo.fn_nargs);
|
nargs, fip->flinfo.fn_nargs);
|
||||||
}
|
|
||||||
|
|
||||||
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
||||||
fcinfo.flinfo = &fip->flinfo;
|
fcinfo.flinfo = &fip->flinfo;
|
||||||
@ -286,21 +321,21 @@ HandleFunctionRequest(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy supplied arguments into arg vector. Note there is no way for
|
* 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)
|
for (i = 0; i < nargs; ++i)
|
||||||
{
|
{
|
||||||
if (pq_getint(&argsize, 4))
|
int argsize;
|
||||||
return EOF;
|
char *p;
|
||||||
|
|
||||||
|
argsize = pq_getmsgint(msgBuf, 4);
|
||||||
if (fip->argbyval[i])
|
if (fip->argbyval[i])
|
||||||
{ /* by-value */
|
{ /* by-value */
|
||||||
if (argsize < 1 || argsize > 4)
|
if (argsize < 1 || argsize > 4)
|
||||||
elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
|
elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
|
||||||
argsize);
|
argsize);
|
||||||
/* XXX should we demand argsize == fip->arglen[i] ? */
|
/* XXX should we demand argsize == fip->arglen[i] ? */
|
||||||
if (pq_getint(&tmp, argsize))
|
fcinfo.arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
|
||||||
return EOF;
|
|
||||||
fcinfo.arg[i] = (Datum) tmp;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* by-reference ... */
|
{ /* by-reference ... */
|
||||||
@ -309,13 +344,9 @@ HandleFunctionRequest(void)
|
|||||||
if (argsize < 0)
|
if (argsize < 0)
|
||||||
elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
|
elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
|
||||||
argsize);
|
argsize);
|
||||||
/* I suspect this +1 isn't really needed - tgl 5/2000 */
|
p = palloc(argsize + VARHDRSZ);
|
||||||
p = palloc(argsize + VARHDRSZ + 1); /* Added +1 to solve
|
|
||||||
* memory leak - Peter
|
|
||||||
* 98 Jan 6 */
|
|
||||||
VARATT_SIZEP(p) = argsize + VARHDRSZ;
|
VARATT_SIZEP(p) = argsize + VARHDRSZ;
|
||||||
if (pq_getbytes(VARDATA(p), argsize))
|
pq_copymsgbytes(msgBuf, VARDATA(p), argsize);
|
||||||
return EOF;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* ... fixed */
|
{ /* ... fixed */
|
||||||
@ -323,29 +354,12 @@ HandleFunctionRequest(void)
|
|||||||
elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d",
|
elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d",
|
||||||
argsize, fip->arglen[i]);
|
argsize, fip->arglen[i]);
|
||||||
p = palloc(argsize + 1); /* +1 in case argsize is 0 */
|
p = palloc(argsize + 1); /* +1 in case argsize is 0 */
|
||||||
if (pq_getbytes(p, argsize))
|
pq_copymsgbytes(msgBuf, p, argsize);
|
||||||
return EOF;
|
|
||||||
}
|
}
|
||||||
fcinfo.arg[i] = PointerGetDatum(p);
|
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
|
* 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
|
* to do this if we are in transaction-abort state, so we have to postpone
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* this is the "main" module of the postgres backend and
|
* 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
|
* 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.
|
* 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 */
|
/* Reset inBuf to empty */
|
||||||
inBuf->len = 0;
|
inBuf->len = 0;
|
||||||
inBuf->data[0] = '\0';
|
inBuf->data[0] = '\0';
|
||||||
|
inBuf->cursor = 0;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@ -214,6 +217,9 @@ InteractiveBackend(StringInfo inBuf)
|
|||||||
break;
|
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..
|
* if the query echo flag was given, print the query..
|
||||||
*/
|
*/
|
||||||
@ -227,66 +233,79 @@ InteractiveBackend(StringInfo inBuf)
|
|||||||
/* ----------------
|
/* ----------------
|
||||||
* SocketBackend() Is called for frontend-backend connections
|
* SocketBackend() Is called for frontend-backend connections
|
||||||
*
|
*
|
||||||
* If the input is a query (case 'Q') then the string entered by
|
* Returns the message type code, and loads message body data into inBuf.
|
||||||
* 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()).
|
|
||||||
*
|
*
|
||||||
* EOF is returned if the connection is lost.
|
* EOF is returned if the connection is lost.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
SocketBackend(StringInfo inBuf)
|
SocketBackend(StringInfo inBuf)
|
||||||
{
|
{
|
||||||
int qtype;
|
int qtype;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get input from the frontend
|
* Get message type code from the frontend.
|
||||||
*/
|
*/
|
||||||
qtype = pq_getbyte();
|
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)
|
switch (qtype)
|
||||||
{
|
{
|
||||||
case EOF:
|
case 'Q': /* simple query */
|
||||||
/* frontend disconnected */
|
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;
|
break;
|
||||||
|
|
||||||
/*
|
case 'F': /* fastpath function call */
|
||||||
* 'Q': user entered a query
|
|
||||||
*/
|
|
||||||
case 'Q':
|
|
||||||
if (pq_getstr(inBuf))
|
|
||||||
return EOF;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
case 'X': /* terminate */
|
||||||
* 'F': calling user/system functions
|
|
||||||
*/
|
|
||||||
case 'F':
|
|
||||||
if (pq_getstr(inBuf))
|
|
||||||
return EOF; /* ignore "string" at start of F message */
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
case 'd': /* copy data */
|
||||||
* 'X': frontend is exiting
|
case 'c': /* copy done */
|
||||||
*/
|
case 'f': /* copy fail */
|
||||||
case 'X':
|
/* Accept but ignore these messages, per protocol spec */
|
||||||
break;
|
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:
|
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);
|
elog(FATAL, "Socket command type %c unknown", qtype);
|
||||||
break;
|
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;
|
return qtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1220,19 +1239,17 @@ int
|
|||||||
PostgresMain(int argc, char *argv[], const char *username)
|
PostgresMain(int argc, char *argv[], const char *username)
|
||||||
{
|
{
|
||||||
int flag;
|
int flag;
|
||||||
|
|
||||||
const char *DBName = NULL;
|
const char *DBName = NULL;
|
||||||
|
char *potential_DataDir = NULL;
|
||||||
bool secure;
|
bool secure;
|
||||||
int errs = 0;
|
int errs = 0;
|
||||||
int debug_flag = 0;
|
int debug_flag = 0;
|
||||||
GucContext ctx;
|
GucContext ctx;
|
||||||
GucSource gucsource;
|
GucSource gucsource;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
|
|
||||||
int firstchar;
|
int firstchar;
|
||||||
StringInfo parser_input;
|
StringInfo parser_input;
|
||||||
|
bool send_rfq;
|
||||||
char *potential_DataDir = NULL;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Catch standard options before doing much else. This even works on
|
* 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)
|
if (!IsUnderPostmaster)
|
||||||
{
|
{
|
||||||
puts("\nPOSTGRES backend interactive interface ");
|
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);
|
PG_SETMASK(&UnBlockSig);
|
||||||
|
|
||||||
|
send_rfq = true; /* initially, or after error */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Non-error queries loop here.
|
* 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.
|
* Note: this includes fflush()'ing the last of the prior output.
|
||||||
*/
|
*/
|
||||||
ReadyForQuery(whereToSendOutput);
|
if (send_rfq)
|
||||||
|
{
|
||||||
|
ReadyForQuery(whereToSendOutput);
|
||||||
|
send_rfq = false;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* Tell the statistics collector what we've collected
|
* Tell the statistics collector what we've collected
|
||||||
@ -1986,43 +2009,9 @@ PostgresMain(int argc, char *argv[], const char *username)
|
|||||||
*/
|
*/
|
||||||
switch (firstchar)
|
switch (firstchar)
|
||||||
{
|
{
|
||||||
|
case 'Q': /* simple query */
|
||||||
/*
|
/*
|
||||||
* 'F' indicates a fastpath call.
|
* Process the query string.
|
||||||
*/
|
|
||||||
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.
|
|
||||||
*
|
*
|
||||||
* Note: transaction command start/end is now done within
|
* Note: transaction command start/end is now done within
|
||||||
* pg_exec_query_string(), not here.
|
* pg_exec_query_string(), not here.
|
||||||
@ -2038,6 +2027,35 @@ PostgresMain(int argc, char *argv[], const char *username)
|
|||||||
|
|
||||||
if (log_statement_stats)
|
if (log_statement_stats)
|
||||||
ShowUsage("QUERY STATISTICS");
|
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;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2064,8 +2082,18 @@ PostgresMain(int argc, char *argv[], const char *username)
|
|||||||
*/
|
*/
|
||||||
proc_exit(0);
|
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:
|
default:
|
||||||
elog(ERROR, "unknown frontend message was received");
|
elog(FATAL, "Socket command type %c unknown", firstchar);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MEMORY_CONTEXT_CHECKING
|
#ifdef MEMORY_CONTEXT_CHECKING
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* string size (including the terminating '\0' char) that we can
|
||||||
* currently store in 'data' without having to reallocate
|
* currently store in 'data' without having to reallocate
|
||||||
* more space. We must always have maxlen > len.
|
* 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
|
typedef struct StringInfoData
|
||||||
@ -34,6 +37,7 @@ typedef struct StringInfoData
|
|||||||
char *data;
|
char *data;
|
||||||
int len;
|
int len;
|
||||||
int maxlen;
|
int maxlen;
|
||||||
|
int cursor;
|
||||||
} StringInfoData;
|
} StringInfoData;
|
||||||
|
|
||||||
typedef StringInfoData *StringInfo;
|
typedef StringInfoData *StringInfo;
|
||||||
@ -111,4 +115,10 @@ extern void appendStringInfoChar(StringInfo str, char ch);
|
|||||||
extern void appendBinaryStringInfo(StringInfo str,
|
extern void appendBinaryStringInfo(StringInfo str,
|
||||||
const char *data, int datalen);
|
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 */
|
#endif /* STRINGINFO_H */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 TouchSocketFile(void);
|
||||||
extern void pq_init(void);
|
extern void pq_init(void);
|
||||||
extern int pq_getbytes(char *s, size_t len);
|
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_getbyte(void);
|
||||||
extern int pq_peekbyte(void);
|
extern int pq_peekbyte(void);
|
||||||
extern int pq_putbytes(const char *s, size_t len);
|
extern int pq_putbytes(const char *s, size_t len);
|
||||||
extern int pq_flush(void);
|
extern int pq_flush(void);
|
||||||
extern int pq_eof(void);
|
|
||||||
extern int pq_putmessage(char msgtype, const char *s, size_t len);
|
extern int pq_putmessage(char msgtype, const char *s, size_t len);
|
||||||
extern void pq_startcopyout(void);
|
extern void pq_startcopyout(void);
|
||||||
extern void pq_endcopyout(bool errorAbort);
|
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 */
|
#endif /* LIBPQ_H */
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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. */
|
/* The earliest and latest frontend/backend protocol version supported. */
|
||||||
|
|
||||||
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0)
|
#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 */
|
typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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_puttextmessage(char msgtype, const char *str);
|
||||||
|
|
||||||
extern int pq_getint(int *result, int b);
|
extern int pq_getmsgbyte(StringInfo msg);
|
||||||
extern int pq_getstr_bounded(StringInfo s, int maxlen);
|
extern unsigned int pq_getmsgint(StringInfo msg, int b);
|
||||||
|
extern const char *pq_getmsgbytes(StringInfo msg, int datalen);
|
||||||
#define pq_getstr(s) pq_getstr_bounded(s, 0)
|
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 */
|
#endif /* PQFORMAT_H */
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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. */
|
/* 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 NullCommand(CommandDest dest);
|
||||||
extern void ReadyForQuery(CommandDest dest);
|
extern void ReadyForQuery(CommandDest dest);
|
||||||
|
|
||||||
|
@ -6,13 +6,15 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
#ifndef FASTPATH_H
|
||||||
#define FASTPATH_H
|
#define FASTPATH_H
|
||||||
|
|
||||||
extern int HandleFunctionRequest(void);
|
#include "lib/stringinfo.h"
|
||||||
|
|
||||||
|
extern int HandleFunctionRequest(StringInfo msgBuf);
|
||||||
|
|
||||||
#endif /* FASTPATH_H */
|
#endif /* FASTPATH_H */
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
|
* exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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:
|
default:
|
||||||
return STATUS_ERROR;
|
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)
|
if (areq == AUTH_REQ_MD5)
|
||||||
free(crypt_pwd);
|
free(crypt_pwd);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The output buffer size is set to 8K, which is the usual size of
|
* We try to send at least 8K at a time, which is the usual size
|
||||||
* pipe buffers on Unix systems. That way, when we are sending a
|
* of pipe buffers on Unix systems. That way, when we are sending a
|
||||||
* large amount of data, we avoid incurring extra kernel context swaps
|
* large amount of data, we avoid incurring extra kernel context swaps
|
||||||
* for partial bufferloads. Note that we currently don't ever enlarge
|
* for partial bufferloads. The output buffer is initially made 16K
|
||||||
* the output buffer.
|
* in size, and we try to dump it after accumulating 8K.
|
||||||
*
|
*
|
||||||
* With the same goal of minimizing context swaps, the input buffer will
|
* With the same goal of minimizing context swaps, the input buffer will
|
||||||
* be enlarged anytime it has less than 8K free, so we initially
|
* be enlarged anytime it has less than 8K free, so we initially
|
||||||
@ -1832,7 +1832,7 @@ makeEmptyPGconn(void)
|
|||||||
*/
|
*/
|
||||||
conn->inBufSize = 16 * 1024;
|
conn->inBufSize = 16 * 1024;
|
||||||
conn->inBuffer = (char *) malloc(conn->inBufSize);
|
conn->inBuffer = (char *) malloc(conn->inBufSize);
|
||||||
conn->outBufSize = 8 * 1024;
|
conn->outBufSize = 16 * 1024;
|
||||||
conn->outBuffer = (char *) malloc(conn->outBufSize);
|
conn->outBuffer = (char *) malloc(conn->outBufSize);
|
||||||
conn->nonblocking = FALSE;
|
conn->nonblocking = FALSE;
|
||||||
initPQExpBuffer(&conn->errorMessage);
|
initPQExpBuffer(&conn->errorMessage);
|
||||||
@ -1918,11 +1918,10 @@ closePGconn(PGconn *conn)
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Try to send "close connection" message to backend. Ignore any
|
* Try to send "close connection" message to backend. Ignore any
|
||||||
* error. Note: this routine used to go to substantial lengths to
|
* error.
|
||||||
* avoid getting SIGPIPE'd if the connection were already closed.
|
|
||||||
* Now we rely on pqFlush to avoid the signal.
|
|
||||||
*/
|
*/
|
||||||
pqPutc('X', conn);
|
pqPutMsgStart('X', conn);
|
||||||
|
pqPutMsgEnd(conn);
|
||||||
pqFlush(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
|
* pack_type: the single-byte message type code. (Pass zero for startup
|
||||||
* packets, which have no message type code.)
|
* packets, which have no message type code.)
|
||||||
@ -2167,19 +2166,18 @@ int
|
|||||||
pqPacketSend(PGconn *conn, char pack_type,
|
pqPacketSend(PGconn *conn, char pack_type,
|
||||||
const void *buf, size_t buf_len)
|
const void *buf, size_t buf_len)
|
||||||
{
|
{
|
||||||
/* Send the message type. */
|
/* Start the message. */
|
||||||
if (pack_type != 0)
|
if (pqPutMsgStart(pack_type, conn))
|
||||||
if (pqPutc(pack_type, conn))
|
|
||||||
return STATUS_ERROR;
|
|
||||||
|
|
||||||
/* Send the (self-inclusive) message length word. */
|
|
||||||
if (pqPutInt(buf_len + 4, 4, conn))
|
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
|
|
||||||
/* Send the message body. */
|
/* Send the message body. */
|
||||||
if (pqPutnchar(buf, buf_len, conn))
|
if (pqPutnchar(buf, buf_len, conn))
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
|
|
||||||
|
/* Finish the message. */
|
||||||
|
if (pqPutMsgEnd(conn))
|
||||||
|
return STATUS_ERROR;
|
||||||
|
|
||||||
/* Flush to ensure backend gets it. */
|
/* Flush to ensure backend gets it. */
|
||||||
if (pqFlush(conn))
|
if (pqFlush(conn))
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
@ -2624,7 +2622,7 @@ build_startup_packet(const PGconn *conn, char *packet)
|
|||||||
packet_len += sizeof(ProtocolVersion);
|
packet_len += sizeof(ProtocolVersion);
|
||||||
|
|
||||||
/* Add user name, database name, options */
|
/* Add user name, database name, options */
|
||||||
if (conn->pguser)
|
if (conn->pguser && conn->pguser[0])
|
||||||
{
|
{
|
||||||
if (packet)
|
if (packet)
|
||||||
strcpy(packet + packet_len, "user");
|
strcpy(packet + packet_len, "user");
|
||||||
@ -2633,7 +2631,7 @@ build_startup_packet(const PGconn *conn, char *packet)
|
|||||||
strcpy(packet + packet_len, conn->pguser);
|
strcpy(packet + packet_len, conn->pguser);
|
||||||
packet_len += strlen(conn->pguser) + 1;
|
packet_len += strlen(conn->pguser) + 1;
|
||||||
}
|
}
|
||||||
if (conn->dbName)
|
if (conn->dbName && conn->dbName[0])
|
||||||
{
|
{
|
||||||
if (packet)
|
if (packet)
|
||||||
strcpy(packet + packet_len, "database");
|
strcpy(packet + packet_len, "database");
|
||||||
@ -2642,7 +2640,7 @@ build_startup_packet(const PGconn *conn, char *packet)
|
|||||||
strcpy(packet + packet_len, conn->dbName);
|
strcpy(packet + packet_len, conn->dbName);
|
||||||
packet_len += strlen(conn->dbName) + 1;
|
packet_len += strlen(conn->dbName) + 1;
|
||||||
}
|
}
|
||||||
if (conn->pgoptions)
|
if (conn->pgoptions && conn->pgoptions[0])
|
||||||
{
|
{
|
||||||
if (packet)
|
if (packet)
|
||||||
strcpy(packet + packet_len, "options");
|
strcpy(packet + packet_len, "options");
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* Returns: 1 if successfully submitted
|
||||||
* 0 if error (conn->errorMessage is set)
|
* 0 if error (conn->errorMessage is set)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
int
|
||||||
PQsendQuery(PGconn *conn, const char *query)
|
PQsendQuery(PGconn *conn, const char *query)
|
||||||
{
|
{
|
||||||
@ -770,51 +769,24 @@ PQsendQuery(PGconn *conn, const char *query)
|
|||||||
conn->result = NULL;
|
conn->result = NULL;
|
||||||
conn->curTuple = NULL;
|
conn->curTuple = NULL;
|
||||||
|
|
||||||
/* send the query to the backend; */
|
/* construct the outgoing Query message */
|
||||||
|
if (pqPutMsgStart('Q', conn) < 0 ||
|
||||||
|
pqPuts(query, conn) < 0 ||
|
||||||
|
pqPutMsgEnd(conn) < 0)
|
||||||
|
{
|
||||||
|
handleSendFailure(conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* in order to guarantee that we don't send a partial query where we
|
* Give the data a push. In nonblock mode, don't complain if we're
|
||||||
* would become out of sync with the backend and/or block during a
|
* unable to send it all; PQconsumeInput() will do any additional flushing
|
||||||
* non-blocking connection we must first flush the send buffer before
|
* needed.
|
||||||
* 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))
|
if (pqFlush(conn) < 0)
|
||||||
{
|
{
|
||||||
/*
|
handleSendFailure(conn);
|
||||||
* the buffer must have emptied completely before we allow a new
|
return 0;
|
||||||
* query to be buffered
|
|
||||||
*/
|
|
||||||
if (pqFlush(conn))
|
|
||||||
return 0;
|
|
||||||
/* 'Q' == queries */
|
|
||||||
/* XXX: if we fail here we really ought to not block */
|
|
||||||
if (pqPutc('Q', conn) != 0 || pqPuts(query, conn) != 0)
|
|
||||||
{
|
|
||||||
handleSendFailure(conn);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* give the data a push, ignore the return value as ConsumeInput()
|
|
||||||
* will do any additional flushing if needed
|
|
||||||
*/
|
|
||||||
pqFlush(conn);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* the frontend-backend protocol uses 'Q' to designate queries
|
|
||||||
*/
|
|
||||||
if (pqPutc('Q', conn) != 0 || pqPuts(query, conn) != 0 ||
|
|
||||||
pqFlush(conn) != 0)
|
|
||||||
{
|
|
||||||
handleSendFailure(conn);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OK, it's launched! */
|
/* OK, it's launched! */
|
||||||
@ -830,7 +802,6 @@ PQsendQuery(PGconn *conn, const char *query)
|
|||||||
*
|
*
|
||||||
* NOTE: this routine should only be called in PGASYNC_IDLE state.
|
* NOTE: this routine should only be called in PGASYNC_IDLE state.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handleSendFailure(PGconn *conn)
|
handleSendFailure(PGconn *conn)
|
||||||
{
|
{
|
||||||
@ -854,13 +825,23 @@ handleSendFailure(PGconn *conn)
|
|||||||
* 0 return: some kind of trouble
|
* 0 return: some kind of trouble
|
||||||
* 1 return: no problem
|
* 1 return: no problem
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
int
|
||||||
PQconsumeInput(PGconn *conn)
|
PQconsumeInput(PGconn *conn)
|
||||||
{
|
{
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return 0;
|
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
|
* Load more data, if available. We do this no matter what state we
|
||||||
* are in, since we are probably getting called because the
|
* 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.
|
* we will NOT block waiting for more input.
|
||||||
*/
|
*/
|
||||||
if (pqReadData(conn) < 0)
|
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;
|
return 0;
|
||||||
}
|
|
||||||
/* Parsing of the data waits till later. */
|
/* Parsing of the data waits till later. */
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1733,14 +1706,13 @@ PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
|
|||||||
* PQputline -- sends a string to the backend.
|
* PQputline -- sends a string to the backend.
|
||||||
* Returns 0 if OK, EOF if not.
|
* 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
|
int
|
||||||
PQputline(PGconn *conn, const char *s)
|
PQputline(PGconn *conn, const char *s)
|
||||||
{
|
{
|
||||||
if (!conn || conn->sock < 0)
|
return PQputnbytes(conn, s, strlen(s));
|
||||||
return EOF;
|
|
||||||
return pqPutnchar(s, strlen(s), conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1752,7 +1724,14 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
|
|||||||
{
|
{
|
||||||
if (!conn || conn->sock < 0)
|
if (!conn || conn->sock < 0)
|
||||||
return EOF;
|
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;
|
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
|
* make sure no data is waiting to be sent, abort if we are
|
||||||
* non-blocking and the flush fails
|
* non-blocking and the flush fails
|
||||||
@ -1884,9 +1871,10 @@ PQfn(PGconn *conn,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pqPuts("F ", conn) != 0 || /* function */
|
if (pqPutMsgStart('F', conn) < 0 || /* function call msg */
|
||||||
pqPutInt(fnid, 4, conn) != 0 || /* function id */
|
pqPuts("", conn) < 0 || /* useless string */
|
||||||
pqPutInt(nargs, 4, conn) != 0) /* # of args */
|
pqPutInt(fnid, 4, conn) < 0 || /* function id */
|
||||||
|
pqPutInt(nargs, 4, conn) < 0) /* # of args */
|
||||||
{
|
{
|
||||||
handleSendFailure(conn);
|
handleSendFailure(conn);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1917,7 +1905,9 @@ PQfn(PGconn *conn,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pqFlush(conn))
|
|
||||||
|
if (pqPutMsgEnd(conn) < 0 ||
|
||||||
|
pqFlush(conn))
|
||||||
{
|
{
|
||||||
handleSendFailure(conn);
|
handleSendFailure(conn);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -2409,7 +2399,6 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
|
|||||||
int
|
int
|
||||||
PQsetnonblocking(PGconn *conn, int arg)
|
PQsetnonblocking(PGconn *conn, int arg)
|
||||||
{
|
{
|
||||||
|
|
||||||
arg = (arg == TRUE) ? 1 : 0;
|
arg = (arg == TRUE) ? 1 : 0;
|
||||||
/* early out if the socket is already in the state requested */
|
/* early out if the socket is already in the state requested */
|
||||||
if (arg == conn->nonblocking)
|
if (arg == conn->nonblocking)
|
||||||
@ -2437,7 +2426,6 @@ PQsetnonblocking(PGconn *conn, int arg)
|
|||||||
int
|
int
|
||||||
PQisnonblocking(const PGconn *conn)
|
PQisnonblocking(const PGconn *conn)
|
||||||
{
|
{
|
||||||
|
|
||||||
return (pqIsnonblocking(conn));
|
return (pqIsnonblocking(conn));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2445,18 +2433,9 @@ PQisnonblocking(const PGconn *conn)
|
|||||||
int
|
int
|
||||||
PQflush(PGconn *conn)
|
PQflush(PGconn *conn)
|
||||||
{
|
{
|
||||||
|
|
||||||
return (pqFlush(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
|
* PQfreeNotify - free's the memory associated with a PGnotify
|
||||||
*
|
*
|
||||||
@ -2473,5 +2452,3 @@ PQfreeNotify(PGnotify *notify)
|
|||||||
{
|
{
|
||||||
PQfreemem(notify);
|
PQfreemem(notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,16 +16,14 @@
|
|||||||
* will cause repeat printouts.
|
* will cause repeat printouts.
|
||||||
*
|
*
|
||||||
* We must speak the same transmitted data representations as the backend
|
* We must speak the same transmitted data representations as the backend
|
||||||
* routines. Note that this module supports *only* network byte order
|
* routines.
|
||||||
* for transmitted ints, whereas the backend modules (as of this writing)
|
*
|
||||||
* still handle either network or little-endian byte order.
|
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* IDENTIFICATION
|
* 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) \
|
#define DONOTICE(conn,message) \
|
||||||
((*(conn)->noticeHook) ((conn)->noticeArg, (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,
|
static int pqSocketCheck(PGconn *conn, int forRead, int forWrite,
|
||||||
time_t end_time);
|
time_t end_time);
|
||||||
static int pqSocketPoll(int sock, 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:
|
* pqGetc: get 1 character from the connection
|
||||||
* get a character from the connection
|
|
||||||
*
|
*
|
||||||
* All these routines return 0 on success, EOF on error.
|
* All these routines return 0 on success, EOF on error.
|
||||||
* Note that for the Get routines, EOF only means there is not enough
|
* 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
|
int
|
||||||
pqPutc(char c, PGconn *conn)
|
pqPutc(char c, PGconn *conn)
|
||||||
{
|
{
|
||||||
if (pqPutBytes(&c, 1, conn) == EOF)
|
if (pqPutMsgBytes(&c, 1, conn))
|
||||||
return EOF;
|
return EOF;
|
||||||
|
|
||||||
if (conn->Pfdebug)
|
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:
|
* pqGets:
|
||||||
* get a null-terminated string from the connection,
|
* 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
|
int
|
||||||
pqPuts(const char *s, PGconn *conn)
|
pqPuts(const char *s, PGconn *conn)
|
||||||
{
|
{
|
||||||
if (pqPutBytes(s, strlen(s) + 1, conn))
|
if (pqPutMsgBytes(s, strlen(s) + 1, conn))
|
||||||
return EOF;
|
return EOF;
|
||||||
|
|
||||||
if (conn->Pfdebug)
|
if (conn->Pfdebug)
|
||||||
fprintf(conn->Pfdebug, "To backend> %s\n", s);
|
fprintf(conn->Pfdebug, "To backend> '%s'\n", s);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -267,12 +181,12 @@ pqGetnchar(char *s, size_t len, PGconn *conn)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* pqPutnchar:
|
* pqPutnchar:
|
||||||
* send a string of exactly len bytes, no null termination needed
|
* write exactly len bytes to the current message
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
pqPutnchar(const char *s, size_t len, PGconn *conn)
|
pqPutnchar(const char *s, size_t len, PGconn *conn)
|
||||||
{
|
{
|
||||||
if (pqPutBytes(s, len, conn))
|
if (pqPutMsgBytes(s, len, conn))
|
||||||
return EOF;
|
return EOF;
|
||||||
|
|
||||||
if (conn->Pfdebug)
|
if (conn->Pfdebug)
|
||||||
@ -325,7 +239,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* pgPutInt
|
* 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.
|
* to network byte order.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
@ -339,12 +253,12 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
|
|||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
tmp2 = htons((uint16) value);
|
tmp2 = htons((uint16) value);
|
||||||
if (pqPutBytes((const char *) &tmp2, 2, conn))
|
if (pqPutMsgBytes((const char *) &tmp2, 2, conn))
|
||||||
return EOF;
|
return EOF;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
tmp4 = htonl((uint32) value);
|
tmp4 = htonl((uint32) value);
|
||||||
if (pqPutBytes((const char *) &tmp4, 4, conn))
|
if (pqPutMsgBytes((const char *) &tmp4, 4, conn))
|
||||||
return EOF;
|
return EOF;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -362,24 +276,162 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pqReadReady: is select() saying the file is ready to read?
|
* Make sure conn's output buffer can hold bytes_needed bytes (caller must
|
||||||
* JAB: -or- if SSL is enabled and used, is it buffering bytes?
|
* include existing outCount into the value!)
|
||||||
* Returns -1 on failure, 0 if not ready, 1 if ready.
|
*
|
||||||
|
* Returns 0 on success, EOF on error
|
||||||
*/
|
*/
|
||||||
int
|
static int
|
||||||
pqReadReady(PGconn *conn)
|
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?
|
* pqPutMsgStart: begin construction of a message to the server
|
||||||
* Returns -1 on failure, 0 if not ready, 1 if ready.
|
*
|
||||||
|
* 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
|
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
|
* len is how much to try to send (typically equal to outCount, but may
|
||||||
* socket would block and the connection is non-blocking.
|
* 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
|
static int
|
||||||
pqSendSome(PGconn *conn)
|
pqSendSome(PGconn *conn, int len)
|
||||||
{
|
{
|
||||||
char *ptr = conn->outBuffer;
|
char *ptr = conn->outBuffer;
|
||||||
int len = conn->outCount;
|
int remaining = conn->outCount;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
if (conn->sock < 0)
|
if (conn->sock < 0)
|
||||||
{
|
{
|
||||||
@ -598,13 +654,6 @@ pqSendSome(PGconn *conn)
|
|||||||
return -1;
|
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 there's still data to send */
|
||||||
while (len > 0)
|
while (len > 0)
|
||||||
{
|
{
|
||||||
@ -648,8 +697,9 @@ pqSendSome(PGconn *conn)
|
|||||||
* (typically, a NOTICE message from the backend
|
* (typically, a NOTICE message from the backend
|
||||||
* telling us it's committing hara-kiri...). Leave
|
* telling us it's committing hara-kiri...). Leave
|
||||||
* the socket open until pqReadData finds no more data
|
* 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;
|
return -1;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -657,6 +707,7 @@ pqSendSome(PGconn *conn)
|
|||||||
libpq_gettext("could not send data to server: %s\n"),
|
libpq_gettext("could not send data to server: %s\n"),
|
||||||
SOCK_STRERROR(SOCK_ERRNO));
|
SOCK_STRERROR(SOCK_ERRNO));
|
||||||
/* We don't assume it's a fatal error... */
|
/* We don't assume it's a fatal error... */
|
||||||
|
conn->outCount = 0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -664,6 +715,7 @@ pqSendSome(PGconn *conn)
|
|||||||
{
|
{
|
||||||
ptr += sent;
|
ptr += sent;
|
||||||
len -= sent;
|
len -= sent;
|
||||||
|
remaining -= sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
@ -681,46 +733,49 @@ pqSendSome(PGconn *conn)
|
|||||||
#endif
|
#endif
|
||||||
if (pqIsnonblocking(conn))
|
if (pqIsnonblocking(conn))
|
||||||
{
|
{
|
||||||
/* shift the contents of the buffer */
|
result = 1;
|
||||||
memmove(conn->outBuffer, ptr, len);
|
break;
|
||||||
conn->outCount = len;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (pqWait(FALSE, TRUE, conn))
|
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)
|
return result;
|
||||||
fflush(conn->Pfdebug);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pqFlush: send any data waiting in the output buffer
|
* pqFlush: send any data waiting in the output buffer
|
||||||
*
|
*
|
||||||
* Implemented in terms of pqSendSome to recreate the old behavior which
|
* Return 0 on success, -1 on failure and 1 when not all data could be sent
|
||||||
* returned 0 if all data was sent or EOF. EOF was sent regardless of
|
* because the socket would block and the connection is non-blocking.
|
||||||
* whether an error occurred or not all data was sent on a non-blocking
|
|
||||||
* socket.
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
pqFlush(PGconn *conn)
|
pqFlush(PGconn *conn)
|
||||||
{
|
{
|
||||||
if (pqSendSome(conn))
|
if (conn->Pfdebug)
|
||||||
return EOF;
|
fflush(conn->Pfdebug);
|
||||||
|
|
||||||
|
if (conn->outCount > 0)
|
||||||
|
return pqSendSome(conn, conn->outCount);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pqWait: wait until we can read or write the connection socket
|
* 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;
|
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,
|
* 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
|
* or both. Returns >0 if one or more conditions are met, 0 if it timed
|
||||||
* out, -1 if an error occurred.
|
* out, -1 if an error occurred.
|
||||||
|
*
|
||||||
* If SSL is in use, the SSL buffer is checked prior to checking the socket
|
* If SSL is in use, the SSL buffer is checked prior to checking the socket
|
||||||
* for read data directly.
|
* for read data directly.
|
||||||
*/
|
*/
|
||||||
@ -787,8 +863,8 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* JAB: Check for SSL library buffering read bytes */
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
|
/* Check for SSL library buffering read bytes */
|
||||||
if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
|
if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
|
||||||
{
|
{
|
||||||
/* short-circuit the select */
|
/* 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
|
* If neither forRead nor forWrite are set, immediately return a timeout
|
||||||
* condition (without waiting). Return >0 if condition is met, 0
|
* condition (without waiting). Return >0 if condition is met, 0
|
||||||
* if a timeout occurred, -1 if an error or interrupt occurred.
|
* if a timeout occurred, -1 if an error or interrupt occurred.
|
||||||
|
*
|
||||||
* Timeout is infinite if end_time is -1. Timeout is immediate (no blocking)
|
* Timeout is infinite if end_time is -1. Timeout is immediate (no blocking)
|
||||||
* if end_time is 0 (or indeed, any time before now).
|
* 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;
|
struct pollfd input_fd;
|
||||||
int timeout_ms;
|
int timeout_ms;
|
||||||
|
|
||||||
|
if (!forRead && !forWrite)
|
||||||
|
return 0;
|
||||||
|
|
||||||
input_fd.fd = sock;
|
input_fd.fd = sock;
|
||||||
input_fd.events = 0;
|
input_fd.events = POLLERR;
|
||||||
input_fd.revents = 0;
|
input_fd.revents = 0;
|
||||||
|
|
||||||
if (forRead)
|
if (forRead)
|
||||||
input_fd.events |= POLLIN;
|
input_fd.events |= POLLIN;
|
||||||
if (forWrite)
|
if (forWrite)
|
||||||
input_fd.events |= POLLOUT;
|
input_fd.events |= POLLOUT;
|
||||||
if (!input_fd.events)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Compute appropriate timeout interval */
|
/* Compute appropriate timeout interval */
|
||||||
if (end_time == ((time_t) -1))
|
if (end_time == ((time_t) -1))
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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) */
|
/* Force the write buffer to be written (or at least try) */
|
||||||
extern int PQflush(PGconn *conn);
|
extern int PQflush(PGconn *conn);
|
||||||
extern int PQsendSome(PGconn *conn);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "Fast path" interface --- not really recommended for application
|
* "Fast path" interface --- not really recommended for application
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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.
|
* 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.
|
* POSTGRES backend dependent Constants.
|
||||||
@ -266,6 +266,10 @@ struct pg_conn
|
|||||||
int outBufSize; /* allocated size of buffer */
|
int outBufSize; /* allocated size of buffer */
|
||||||
int outCount; /* number of chars waiting in 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 */
|
/* Status for asynchronous result construction */
|
||||||
PGresult *result; /* result being constructed */
|
PGresult *result; /* result being constructed */
|
||||||
PGresAttValue *curTuple; /* tuple currently being read */
|
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 pqPutnchar(const char *s, size_t len, PGconn *conn);
|
||||||
extern int pqGetInt(int *result, size_t bytes, PGconn *conn);
|
extern int pqGetInt(int *result, size_t bytes, PGconn *conn);
|
||||||
extern int pqPutInt(int value, 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 pqReadData(PGconn *conn);
|
||||||
extern int pqFlush(PGconn *conn);
|
extern int pqFlush(PGconn *conn);
|
||||||
extern int pqSendSome(PGconn *conn);
|
|
||||||
extern int pqWait(int forRead, int forWrite, PGconn *conn);
|
extern int pqWait(int forRead, int forWrite, PGconn *conn);
|
||||||
extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn,
|
extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn,
|
||||||
time_t finish_time);
|
time_t finish_time);
|
||||||
|
Reference in New Issue
Block a user