Commit 5ed27e35 authored by Tom Lane's avatar Tom Lane

Another round of protocol changes. Backend-to-frontend messages now all

have length words.  COPY OUT reimplemented per new protocol: it doesn't
need \. anymore, thank goodness.  COPY BINARY to/from frontend works,
at least as far as the backend is concerned --- libpq's PQgetline API
is not up to snuff, and will have to be replaced with something that is
null-safe.  libpq uses message length words for performance improvement
(no cycles wasted rescanning long messages), but not yet for error
recovery.
parent ca944bd2
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.119 2003/04/19 00:02:29 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.120 2003/04/22 00:08:06 tgl Exp $
--> -->
<chapter id="libpq"> <chapter id="libpq">
...@@ -175,7 +175,8 @@ PGconn *PQconnectdb(const char *conninfo); ...@@ -175,7 +175,8 @@ PGconn *PQconnectdb(const char *conninfo);
<term><literal>connect_timeout</literal></term> <term><literal>connect_timeout</literal></term>
<listitem> <listitem>
<para> <para>
Time space in seconds given to connection function. Zero or not set means infinite. Maximum wait for connection, in seconds (write as a decimal integer
string). Zero or not specified means infinite.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -184,7 +185,7 @@ PGconn *PQconnectdb(const char *conninfo); ...@@ -184,7 +185,7 @@ PGconn *PQconnectdb(const char *conninfo);
<term><literal>options</literal></term> <term><literal>options</literal></term>
<listitem> <listitem>
<para> <para>
Configuration options to be sent to the server. Command-line options to be sent to the server.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -252,8 +253,9 @@ PGconn *PQsetdbLogin(const char *pghost, ...@@ -252,8 +253,9 @@ PGconn *PQsetdbLogin(const char *pghost,
</para> </para>
<para> <para>
This is the predecessor of <function>PQconnectdb</function> with a fixed number This is the predecessor of <function>PQconnectdb</function> with a fixed
of parameters but the same functionality. number of parameters. It has the same functionality except that the
missing parameters cannot be specified in the call.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -274,8 +276,8 @@ PGconn *PQsetdb(char *pghost, ...@@ -274,8 +276,8 @@ PGconn *PQsetdb(char *pghost,
<para> <para>
This is a macro that calls <function>PQsetdbLogin</function> with null pointers This is a macro that calls <function>PQsetdbLogin</function> with null pointers
for the <parameter>login</> and <parameter>pwd</> parameters. It is provided primarily for the <parameter>login</> and <parameter>pwd</> parameters. It is provided
for backward compatibility with old programs. for backward compatibility with very old programs.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -454,7 +456,7 @@ switch(PQstatus(conn)) ...@@ -454,7 +456,7 @@ switch(PQstatus(conn))
</para> </para>
<para> <para>
Finally, these functions leave the socket in a nonblocking state as if Finally, these functions leave the connection in a nonblocking state as if
<function>PQsetnonblocking</function> had been called. <function>PQsetnonblocking</function> had been called.
</para> </para>
</listitem> </listitem>
...@@ -486,8 +488,6 @@ typedef struct ...@@ -486,8 +488,6 @@ typedef struct
</para> </para>
<para> <para>
converts an escaped string representation of binary data into binary
data --- the reverse of <function>PQescapeBytea</function>.
Returns a connection options array. This may Returns a connection options array. This may
be used to determine all possible <function>PQconnectdb</function> options and their be used to determine all possible <function>PQconnectdb</function> options and their
current default values. The return value points to an array of current default values. The return value points to an array of
...@@ -683,7 +683,7 @@ char *PQtty(const PGconn *conn); ...@@ -683,7 +683,7 @@ char *PQtty(const PGconn *conn);
<term><function>PQoptions</function></term> <term><function>PQoptions</function></term>
<listitem> <listitem>
<para> <para>
Returns the configuration options passed in the connection request. Returns the command-line options passed in the connection request.
<synopsis> <synopsis>
char *PQoptions(const PGconn *conn); char *PQoptions(const PGconn *conn);
</synopsis> </synopsis>
...@@ -2047,13 +2047,13 @@ contains example functions that correctly handle the <command>COPY</command> pro ...@@ -2047,13 +2047,13 @@ contains example functions that correctly handle the <command>COPY</command> pro
<term><function>PQgetlineAsync</function></term> <term><function>PQgetlineAsync</function></term>
<listitem> <listitem>
<para> <para>
Reads a newline-terminated line of characters Reads a row of COPY data
(transmitted by the server) into a buffer (transmitted by the server) into a buffer
without blocking. without blocking.
<synopsis> <synopsis>
int PQgetlineAsync(PGconn *conn, int PQgetlineAsync(PGconn *conn,
char *buffer, char *buffer,
int length); int bufsize);
</synopsis> </synopsis>
</para> </para>
...@@ -2070,24 +2070,27 @@ end-of-data signal is detected. ...@@ -2070,24 +2070,27 @@ end-of-data signal is detected.
<para> <para>
Unlike <function>PQgetline</function>, this function takes Unlike <function>PQgetline</function>, this function takes
responsibility for detecting end-of-data. responsibility for detecting end-of-data.
On each call, <function>PQgetlineAsync</function> will return data if a complete newline- </para>
terminated data line is available in <application>libpq</>'s input buffer, or if the <para>
incoming data line is too long to fit in the buffer offered by the caller. On each call, <function>PQgetlineAsync</function> will return data if a
Otherwise, no data is returned until the rest of the line arrives. complete data row is available in <application>libpq</>'s input buffer.
Otherwise, no data is returned until the rest of the row arrives.
The function returns -1 if the end-of-copy-data marker has been recognized, The function returns -1 if the end-of-copy-data marker has been recognized,
or 0 if no data is available, or a positive number giving the number of or 0 if no data is available, or a positive number giving the number of
bytes of data returned. If -1 is returned, the caller must next call bytes of data returned. If -1 is returned, the caller must next call
<function>PQendcopy</function>, and then return to normal processing. <function>PQendcopy</function>, and then return to normal processing.
</para> </para>
<para> <para>
The data returned will not extend beyond a newline character. If possible The data returned will not extend beyond a data-row boundary. If possible
a whole line will be returned at one time. But if the buffer offered by a whole row will be returned at one time. But if the buffer offered by
the caller is too small to hold a line sent by the server, then a partial the caller is too small to hold a row sent by the server, then a partial
data line will be returned. This can be detected by testing whether the data row will be returned. With textual data this can be detected by testing
last returned byte is <literal>\n</literal> or not. whether the last returned byte is <literal>\n</literal> or not. (In a binary
COPY, actual parsing of the COPY data format will be needed to make the
equivalent determination.)
The returned string is not null-terminated. (If you want to add a The returned string is not null-terminated. (If you want to add a
terminating null, be sure to pass a <parameter>length</parameter> one smaller than the room terminating null, be sure to pass a <parameter>bufsize</parameter> one smaller
actually available.) than the room actually available.)
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -2105,10 +2108,24 @@ int PQputline(PGconn *conn, ...@@ -2105,10 +2108,24 @@ int PQputline(PGconn *conn,
</para> </para>
<para> <para>
Note the application must explicitly send the two The COPY datastream sent by a series of calls to
characters <literal>\.</literal> on a final line to indicate to <function>PQputline</function> has the same format as that returned by
the server that it has finished sending its data. <function>PQgetlineAsync</function>, except that applications are not
obliged to send exactly one data row per <function>PQputline</function>
call; it is okay to send a partial line or multiple lines per call.
</para> </para>
<note>
<para>
Before <productname>PostgreSQL</productname> 7.4, it was necessary for the
application to explicitly send the two characters <literal>\.</literal> as a
final line to indicate to the server that it had finished sending COPY data.
While this still works, it is deprecated and the special meaning of
<literal>\.</literal> can be expected to be removed in a future release.
It is sufficient to call <function>PQendcopy</function> after having sent the
actual data.
</para>
</note>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -2126,9 +2143,9 @@ int PQputnbytes(PGconn *conn, ...@@ -2126,9 +2143,9 @@ int PQputnbytes(PGconn *conn,
</para> </para>
<para> <para>
This is exactly like <function>PQputline</function>, except that the data buffer need This is exactly like <function>PQputline</function>, except that the data
not be null-terminated since the number of bytes to send is buffer need not be null-terminated since the number of bytes to send is
specified directly. specified directly. Use this procedure when sending binary data.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -2147,11 +2164,12 @@ int PQendcopy(PGconn *conn); ...@@ -2147,11 +2164,12 @@ int PQendcopy(PGconn *conn);
sent to the server using <function>PQputline</function> or when the sent to the server using <function>PQputline</function> or when the
last string has been received from the server last string has been received from the server
using <function>PGgetline</function>. It must be issued or the server using <function>PGgetline</function>. It must be issued or the server
may get <quote>out of sync</quote> with the client. Upon will get <quote>out of sync</quote> with the client. Upon
return from this function, the server is ready to return from this function, the server is ready to
receive the next SQL command. receive the next SQL command.
The return value is 0 on successful completion, The return value is 0 on successful completion,
nonzero otherwise. nonzero otherwise. (Use <function>PQerrorMessage</function> to retrieve
details if the return value is nonzero.)
</para> </para>
<para> <para>
...@@ -2187,7 +2205,6 @@ PQexec(conn, "COPY foo FROM STDIN;"); ...@@ -2187,7 +2205,6 @@ PQexec(conn, "COPY foo FROM STDIN;");
PQputline(conn, "3\thello world\t4.5\n"); PQputline(conn, "3\thello world\t4.5\n");
PQputline(conn, "4\tgoodbye world\t7.11\n"); PQputline(conn, "4\tgoodbye world\t7.11\n");
... ...
PQputline(conn, "\\.\n");
PQendcopy(conn); PQendcopy(conn);
</programlisting> </programlisting>
</para> </para>
......
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.28 2003/04/19 00:02:29 tgl Exp $ --> <!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.29 2003/04/22 00:08:06 tgl Exp $ -->
<chapter id="protocol"> <chapter id="protocol">
<title>Frontend/Backend Protocol</title> <title>Frontend/Backend Protocol</title>
...@@ -3691,7 +3691,8 @@ Terminate (F) ...@@ -3691,7 +3691,8 @@ Terminate (F)
<para> <para>
This section describes the fields that may appear in ErrorResponse and This section describes the fields that may appear in ErrorResponse and
NoticeResponse messages. Each field type has a single-byte identification NoticeResponse messages. Each field type has a single-byte identification
token. token. Note that any given field type should appear at most once per
message.
</para> </para>
<VariableList> <VariableList>
...@@ -3863,7 +3864,29 @@ PasswordMessage now has a type byte. ...@@ -3863,7 +3864,29 @@ PasswordMessage now has a type byte.
<para> <para>
COPY data is now encapsulated into CopyData and CopyDone messages. There COPY data is now encapsulated into CopyData and CopyDone messages. There
is a well-defined way to recover from errors during COPY. is a well-defined way to recover from errors during COPY. The special
<quote><literal>\.</></quote> last line is not needed anymore, and is not sent
during COPY OUT.
(It is still recognized as a terminator during COPY IN, but its use is
deprecated and will eventually be removed.) Binary COPY is supported.
The CopyInResponse and CopyOutResponse messages carry a field indicating
whether the COPY operation is text or binary.
</para>
<para>
The CursorResponse ('<literal>P</>') message is no longer generated by
the backend.
</para>
<para>
The NotificationResponse ('<literal>A</>') message has an additional string
field, which is presently empty but may someday carry additional data passed
from the NOTIFY event sender.
</para>
<para>
The EmptyQueryResponse ('<literal>I</>') message used to include an empty
string parameter; this has been removed.
</para> </para>
<note> <note>
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,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/access/common/printtup.c,v 1.65 2002/09/04 20:31:08 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.66 2003/04/22 00:08:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -77,15 +77,18 @@ static void ...@@ -77,15 +77,18 @@ static void
printtup_setup(DestReceiver *self, int operation, printtup_setup(DestReceiver *self, int operation,
const char *portalName, TupleDesc typeinfo) const char *portalName, TupleDesc typeinfo)
{ {
/* if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
* Send portal name to frontend. {
* /*
* If portal name not specified, use "blank" portal. * Send portal name to frontend (obsolete cruft, gone in proto 3.0)
*/ *
if (portalName == NULL) * If portal name not specified, use "blank" portal.
portalName = "blank"; */
if (portalName == NULL)
pq_puttextmessage('P', portalName); portalName = "blank";
pq_puttextmessage('P', portalName);
}
/* /*
* if this is a retrieve, then we send back the tuple descriptor of * if this is a retrieve, then we send back the tuple descriptor of
...@@ -98,8 +101,7 @@ printtup_setup(DestReceiver *self, int operation, ...@@ -98,8 +101,7 @@ printtup_setup(DestReceiver *self, int operation,
int i; int i;
StringInfoData buf; StringInfoData buf;
pq_beginmessage(&buf); pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
pq_sendbyte(&buf, 'T'); /* tuple descriptor message type */
pq_sendint(&buf, natts, 2); /* # of attrs in tuples */ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
...@@ -174,8 +176,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -174,8 +176,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
/* /*
* tell the frontend to expect new tuple data (in ASCII style) * tell the frontend to expect new tuple data (in ASCII style)
*/ */
pq_beginmessage(&buf); pq_beginmessage(&buf, 'D');
pq_sendbyte(&buf, 'D');
/* /*
* send a bitmap of which attributes are not null * send a bitmap of which attributes are not null
...@@ -388,8 +389,7 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -388,8 +389,7 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
/* /*
* tell the frontend to expect new tuple data (in binary style) * tell the frontend to expect new tuple data (in binary style)
*/ */
pq_beginmessage(&buf); pq_beginmessage(&buf, 'B');
pq_sendbyte(&buf, 'B');
/* /*
* send a bitmap of which attributes are not null * send a bitmap of which attributes are not null
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,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/commands/async.c,v 1.92 2003/02/18 02:53:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.93 2003/04/22 00:08:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -847,10 +847,14 @@ NotifyMyFrontEnd(char *relname, int32 listenerPID) ...@@ -847,10 +847,14 @@ NotifyMyFrontEnd(char *relname, int32 listenerPID)
{ {
StringInfoData buf; StringInfoData buf;
pq_beginmessage(&buf); pq_beginmessage(&buf, 'A');
pq_sendbyte(&buf, 'A');
pq_sendint(&buf, listenerPID, sizeof(int32)); pq_sendint(&buf, listenerPID, sizeof(int32));
pq_sendstring(&buf, relname); pq_sendstring(&buf, relname);
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
/* XXX Add parameter string here later */
pq_sendstring(&buf, "");
}
pq_endmessage(&buf); pq_endmessage(&buf);
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.194 2003/04/19 20:36:03 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.195 2003/04/22 00:08:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -50,13 +50,6 @@ ...@@ -50,13 +50,6 @@
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7')) #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#define OCTVALUE(c) ((c) - '0') #define OCTVALUE(c) ((c) - '0')
/* Default line termination */
#ifndef WIN32
#define PGEOL "\n"
#else
#define PGEOL "\r\n"
#endif
/* /*
* Represents the different source/dest cases we need to worry about at * Represents the different source/dest cases we need to worry about at
* the bottom level * the bottom level
...@@ -92,7 +85,7 @@ typedef enum EolType ...@@ -92,7 +85,7 @@ typedef enum EolType
/* 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,
bool pipe, 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,
char *delim, char *null_print); char *delim, char *null_print);
static Oid GetInputFunction(Oid type); static Oid GetInputFunction(Oid type);
...@@ -101,8 +94,7 @@ static char *CopyReadAttribute(const char *delim, CopyReadResult *result); ...@@ -101,8 +94,7 @@ static char *CopyReadAttribute(const char *delim, CopyReadResult *result);
static void CopyAttributeOut(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);
/* The trailing null is part of the signature */ static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
static const char BinarySignature[] = "PGBCOPY\n\377\r\n";
/* /*
* Static communication variables ... pretty grotty, but COPY has * Static communication variables ... pretty grotty, but COPY has
...@@ -135,10 +127,11 @@ static int server_encoding; ...@@ -135,10 +127,11 @@ static int server_encoding;
*/ */
static void SendCopyBegin(bool binary); static void SendCopyBegin(bool binary);
static void ReceiveCopyBegin(bool binary); static void ReceiveCopyBegin(bool binary);
static void SendCopyEnd(bool binary, bool pipe); static void SendCopyEnd(bool binary);
static void CopySendData(void *databuf, int datasize); static void CopySendData(void *databuf, int datasize);
static void CopySendString(const char *str); static void CopySendString(const char *str);
static void CopySendChar(char c); static void CopySendChar(char c);
static void CopySendEndOfRow(bool binary);
static void CopyGetData(void *databuf, int datasize); static void CopyGetData(void *databuf, int datasize);
static int CopyGetChar(void); static int CopyGetChar(void);
#define CopyGetEof() (fe_eof) #define CopyGetEof() (fe_eof)
...@@ -154,22 +147,32 @@ SendCopyBegin(bool binary) ...@@ -154,22 +147,32 @@ SendCopyBegin(bool binary)
{ {
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{ {
pq_putbytes("H", 1); /* new way */ /* new way */
/* XXX grottiness needed for old protocol */ StringInfoData buf;
pq_startcopyout();
pq_beginmessage(&buf, 'H');
pq_sendbyte(&buf, binary ? 1 : 0);
pq_endmessage(&buf);
copy_dest = COPY_NEW_FE; copy_dest = COPY_NEW_FE;
copy_msgbuf = makeStringInfo();
} }
else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
{ {
pq_putbytes("H", 1); /* old way */ /* old way */
/* grottiness needed for old protocol */ if (binary)
elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
pq_putemptymessage('H');
/* grottiness needed for old COPY OUT protocol */
pq_startcopyout(); pq_startcopyout();
copy_dest = COPY_OLD_FE; copy_dest = COPY_OLD_FE;
} }
else else
{ {
pq_putbytes("B", 1); /* very old way */ /* very old way */
/* grottiness needed for old protocol */ if (binary)
elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
pq_putemptymessage('B');
/* grottiness needed for old COPY OUT protocol */
pq_startcopyout(); pq_startcopyout();
copy_dest = COPY_OLD_FE; copy_dest = COPY_OLD_FE;
} }
...@@ -180,18 +183,29 @@ ReceiveCopyBegin(bool binary) ...@@ -180,18 +183,29 @@ ReceiveCopyBegin(bool binary)
{ {
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{ {
pq_putbytes("G", 1); /* new way */ /* new way */
StringInfoData buf;
pq_beginmessage(&buf, 'G');
pq_sendbyte(&buf, binary ? 1 : 0);
pq_endmessage(&buf);
copy_dest = COPY_NEW_FE; copy_dest = COPY_NEW_FE;
copy_msgbuf = makeStringInfo(); copy_msgbuf = makeStringInfo();
} }
else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
{ {
pq_putbytes("G", 1); /* old way */ /* old way */
if (binary)
elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
pq_putemptymessage('G');
copy_dest = COPY_OLD_FE; copy_dest = COPY_OLD_FE;
} }
else else
{ {
pq_putbytes("D", 1); /* very old way */ /* very old way */
if (binary)
elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
pq_putemptymessage('D');
copy_dest = COPY_OLD_FE; copy_dest = COPY_OLD_FE;
} }
/* We *must* flush here to ensure FE knows it can send. */ /* We *must* flush here to ensure FE knows it can send. */
...@@ -199,22 +213,39 @@ ReceiveCopyBegin(bool binary) ...@@ -199,22 +213,39 @@ ReceiveCopyBegin(bool binary)
} }
static void static void
SendCopyEnd(bool binary, bool pipe) SendCopyEnd(bool binary)
{ {
if (!binary) if (copy_dest == COPY_NEW_FE)
{ {
CopySendString("\\."); if (binary)
CopySendString(!pipe ? PGEOL : "\n"); {
/* Need to flush out file trailer word */
CopySendEndOfRow(true);
}
else
{
/* Shouldn't have any unsent data */
Assert(copy_msgbuf->len == 0);
}
/* Send Copy Done message */
pq_putemptymessage('c');
}
else
{
/* The FE/BE protocol uses \n as newline for all platforms */
CopySendData("\\.\n", 3);
pq_endcopyout(false);
} }
pq_endcopyout(false);
} }
/* /*----------
* CopySendData sends output data to the destination (file or frontend) * 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
* CopySendEndOfRow does the appropriate thing at end of each data row
* *
* 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) CopySendData(void *databuf, int datasize)
...@@ -228,12 +259,13 @@ CopySendData(void *databuf, int datasize) ...@@ -228,12 +259,13 @@ CopySendData(void *databuf, int datasize)
break; break;
case COPY_OLD_FE: case COPY_OLD_FE:
if (pq_putbytes((char *) databuf, datasize)) if (pq_putbytes((char *) databuf, datasize))
fe_eof = true; {
/* no hope of recovering connection sync, so FATAL */
elog(FATAL, "CopySendData: connection lost");
}
break; break;
case COPY_NEW_FE: case COPY_NEW_FE:
/* XXX fix later */ appendBinaryStringInfo(copy_msgbuf, (char *) databuf, datasize);
if (pq_putbytes((char *) databuf, datasize))
fe_eof = true;
break; break;
} }
} }
...@@ -250,6 +282,40 @@ CopySendChar(char c) ...@@ -250,6 +282,40 @@ CopySendChar(char c)
CopySendData(&c, 1); CopySendData(&c, 1);
} }
static void
CopySendEndOfRow(bool binary)
{
switch (copy_dest)
{
case COPY_FILE:
if (!binary)
{
/* Default line termination depends on platform */
#ifndef WIN32
CopySendChar('\n');
#else
CopySendString("\r\n");
#endif
}
break;
case COPY_OLD_FE:
/* The FE/BE protocol uses \n as newline for all platforms */
if (!binary)
CopySendChar('\n');
break;
case COPY_NEW_FE:
/* The FE/BE protocol uses \n as newline for all platforms */
if (!binary)
CopySendChar('\n');
/* Dump the accumulated row as one CopyData message */
(void) pq_putmessage('d', copy_msgbuf->data, copy_msgbuf->len);
/* Reset copy_msgbuf to empty */
copy_msgbuf->len = 0;
copy_msgbuf->data[0] = '\0';
break;
}
}
/* /*
* CopyGetData reads data from the source (file or frontend) * CopyGetData reads data from the source (file or frontend)
* CopyGetChar does the same for single characters * CopyGetChar does the same for single characters
...@@ -568,13 +634,6 @@ DoCopy(const CopyStmt *stmt) ...@@ -568,13 +634,6 @@ DoCopy(const CopyStmt *stmt)
"directly to or from a file. Anyone can COPY to stdout or " "directly to or from a file. Anyone can COPY to stdout or "
"from stdin. Psql's \\copy command also works for anyone."); "from stdin. Psql's \\copy command also works for anyone.");
/*
* This restriction is unfortunate, but necessary until the frontend
* COPY protocol is redesigned to be binary-safe...
*/
if (pipe && binary)
elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
/* /*
* Presently, only single-character delimiter strings are supported. * Presently, only single-character delimiter strings are supported.
*/ */
...@@ -698,13 +757,13 @@ DoCopy(const CopyStmt *stmt) ...@@ -698,13 +757,13 @@ DoCopy(const CopyStmt *stmt)
elog(ERROR, "COPY: %s is a directory", filename); elog(ERROR, "COPY: %s is a directory", filename);
} }
} }
CopyTo(rel, attnumlist, binary, oids, pipe, delim, null_print); CopyTo(rel, attnumlist, binary, oids, delim, null_print);
} }
if (!pipe) if (!pipe)
FreeFile(copy_file); FreeFile(copy_file);
else if (IsUnderPostmaster && !is_from) else if (IsUnderPostmaster && !is_from)
SendCopyEnd(binary, pipe); SendCopyEnd(binary);
pfree(attribute_buf.data); pfree(attribute_buf.data);
/* /*
...@@ -721,7 +780,7 @@ DoCopy(const CopyStmt *stmt) ...@@ -721,7 +780,7 @@ DoCopy(const CopyStmt *stmt)
* Copy from relation TO file. * Copy from relation TO file.
*/ */
static void static void
CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, bool pipe, CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print) char *delim, char *null_print)
{ {
HeapTuple tuple; HeapTuple tuple;
...@@ -786,7 +845,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, bool pipe, ...@@ -786,7 +845,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, bool pipe,
int32 tmp; int32 tmp;
/* Signature */ /* Signature */
CopySendData((char *) BinarySignature, sizeof(BinarySignature)); CopySendData((char *) BinarySignature, 12);
/* Integer layout field */ /* Integer layout field */
tmp = 0x01020304; tmp = 0x01020304;
CopySendData(&tmp, sizeof(int32)); CopySendData(&tmp, sizeof(int32));
...@@ -918,8 +977,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, bool pipe, ...@@ -918,8 +977,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, bool pipe,
} }
} }
if (!binary) CopySendEndOfRow(binary);
CopySendString(!pipe ? PGEOL : "\n");
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
} }
...@@ -1100,8 +1158,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, ...@@ -1100,8 +1158,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
/* Signature */ /* Signature */
CopyGetData(readSig, 12); CopyGetData(readSig, 12);
if (CopyGetEof() || memcmp(readSig, BinarySignature, if (CopyGetEof() || memcmp(readSig, BinarySignature, 12) != 0)
sizeof(BinarySignature)) != 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)); CopyGetData(&tmp, sizeof(int32));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.99 2003/04/19 00:02:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.100 2003/04/22 00:08:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -512,8 +512,7 @@ sendAuthRequest(Port *port, AuthRequest areq) ...@@ -512,8 +512,7 @@ sendAuthRequest(Port *port, AuthRequest areq)
{ {
StringInfoData buf; StringInfoData buf;
pq_beginmessage(&buf); pq_beginmessage(&buf, 'R');
pq_sendbyte(&buf, 'R');
pq_sendint(&buf, (int32) areq, sizeof(int32)); pq_sendint(&buf, (int32) areq, sizeof(int32));
/* Add the salt for encrypted passwords. */ /* Add the salt for encrypted passwords. */
......
...@@ -12,15 +12,16 @@ ...@@ -12,15 +12,16 @@
* 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
* the frontend gets back into sync. Therefore, these routines have to be * the frontend gets back into sync. Therefore, these routines have to be
* aware of COPY OUT state. * aware of COPY OUT state. (New COPY-OUT is message-based and does *not*
* set the DoingCopyOut flag.)
* *
* NOTE: generally, it's a bad idea to emit outgoing messages directly with * NOTE: generally, it's a bad idea to emit outgoing messages directly with
* pq_putbytes(), especially if the message would require multiple calls * pq_putbytes(), especially if the message would require multiple calls
* to send. Instead, use the routines in pqformat.c to construct the message * to send. Instead, use the routines in pqformat.c to construct the message
* in a buffer and then emit it in one call to pq_putmessage. This helps * in a buffer and then emit it in one call to pq_putmessage. This ensures
* ensure that the channel will not be clogged by an incomplete message * that the channel will not be clogged by an incomplete message if execution
* if execution is aborted by elog(ERROR) partway through the message. * is aborted by elog(ERROR) partway through the message. The only non-libpq
* The only non-libpq code that should call pq_putbytes directly is COPY OUT. * code that should call pq_putbytes directly is old-style COPY OUT.
* *
* At one time, libpq was shared between frontend and backend, but now * At one time, libpq was shared between frontend and backend, but now
* the backend's "backend/libpq" is quite separate from "interfaces/libpq". * the backend's "backend/libpq" is quite separate from "interfaces/libpq".
...@@ -29,7 +30,7 @@ ...@@ -29,7 +30,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
* *
* $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.150 2003/04/19 00:02:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.151 2003/04/22 00:08:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -846,13 +847,17 @@ pq_flush(void) ...@@ -846,13 +847,17 @@ pq_flush(void)
* pq_putmessage - send a normal message (suppressed in COPY OUT mode) * pq_putmessage - send a normal message (suppressed in COPY OUT mode)
* *
* If msgtype is not '\0', it is a message type code to place before * If msgtype is not '\0', it is a message type code to place before
* the message body (len counts only the body size!). * the message body. If msgtype is '\0', then the message has no type
* If msgtype is '\0', then the buffer already includes the type code. * code (this is only valid in pre-3.0 protocols).
* *
* All normal messages are suppressed while COPY OUT is in progress. * len is the length of the message body data at *s. In protocol 3.0
* (In practice only a few messages might get emitted then; dropping * and later, a message length word (equal to len+4 because it counts
* them is annoying, but at least they will still appear in the * itself too) is inserted by this routine.
* postmaster log.) *
* All normal messages are suppressed while old-style COPY OUT is in
* progress. (In practice only a few notice messages might get emitted
* then; dropping them is annoying, but at least they will still appear
* in the postmaster log.)
* *
* returns 0 if OK, EOF if trouble * returns 0 if OK, EOF if trouble
* -------------------------------- * --------------------------------
...@@ -865,6 +870,14 @@ pq_putmessage(char msgtype, const char *s, size_t len) ...@@ -865,6 +870,14 @@ pq_putmessage(char msgtype, const char *s, size_t len)
if (msgtype) if (msgtype)
if (pq_putbytes(&msgtype, 1)) if (pq_putbytes(&msgtype, 1))
return EOF; return EOF;
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
uint32 n32;
n32 = htonl((uint32) (len + 4));
if (pq_putbytes((char *) &n32, 4))
return EOF;
}
return pq_putbytes(s, len); return pq_putbytes(s, len);
} }
...@@ -880,12 +893,13 @@ pq_startcopyout(void) ...@@ -880,12 +893,13 @@ pq_startcopyout(void)
} }
/* -------------------------------- /* --------------------------------
* pq_endcopyout - end a COPY OUT transfer * pq_endcopyout - end an old-style COPY OUT transfer
* *
* If errorAbort is indicated, we are aborting a COPY OUT due to an error, * If errorAbort is indicated, we are aborting a COPY OUT due to an error,
* and must send a terminator line. Since a partial data line might have * and must send a terminator line. Since a partial data line might have
* been emitted, send a couple of newlines first (the first one could * been emitted, send a couple of newlines first (the first one could
* get absorbed by a backslash...) * get absorbed by a backslash...) Note that old-style COPY OUT does
* not allow binary transfers, so a textual terminator is always correct.
* -------------------------------- * --------------------------------
*/ */
void void
...@@ -893,8 +907,8 @@ pq_endcopyout(bool errorAbort) ...@@ -893,8 +907,8 @@ pq_endcopyout(bool errorAbort)
{ {
if (!DoingCopyOut) if (!DoingCopyOut)
return; return;
DoingCopyOut = false;
if (errorAbort) if (errorAbort)
pq_putbytes("\n\n\\.\n", 5); pq_putbytes("\n\n\\.\n", 5);
/* in non-error case, copy.c will have emitted the terminator line */ /* in non-error case, copy.c will have emitted the terminator line */
DoingCopyOut = false;
} }
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,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
* *
* $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.27 2003/04/19 00:02:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.28 2003/04/22 00:08:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
* *
* 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
* pq_putemptymessage - convenience routine for message with empty body
* *
* Message parsing after input: * Message parsing after input:
* pq_getmsgbyte - get a raw byte from a message buffer * pq_getmsgbyte - get a raw byte from a message buffer
...@@ -63,6 +64,22 @@ ...@@ -63,6 +64,22 @@
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
/* --------------------------------
* pq_beginmessage - initialize for sending a message
* --------------------------------
*/
void
pq_beginmessage(StringInfo buf, char msgtype)
{
initStringInfo(buf);
/*
* We stash the message type into the buffer's cursor field, expecting
* that the pq_sendXXX routines won't touch it. We could alternatively
* make it the first byte of the buffer contents, but this seems easier.
*/
buf->cursor = msgtype;
}
/* -------------------------------- /* --------------------------------
* pq_sendbyte - append a raw byte to a StringInfo buffer * pq_sendbyte - append a raw byte to a StringInfo buffer
* -------------------------------- * --------------------------------
...@@ -176,7 +193,8 @@ pq_sendint(StringInfo buf, int i, int b) ...@@ -176,7 +193,8 @@ pq_sendint(StringInfo buf, int i, int b)
void void
pq_endmessage(StringInfo buf) pq_endmessage(StringInfo buf)
{ {
(void) pq_putmessage('\0', buf->data, buf->len); /* msgtype was saved in cursor field */
(void) pq_putmessage(buf->cursor, buf->data, buf->len);
/* no need to complain about any failure, since pqcomm.c already did */ /* no need to complain about any failure, since pqcomm.c already did */
pfree(buf->data); pfree(buf->data);
buf->data = NULL; buf->data = NULL;
...@@ -188,11 +206,9 @@ pq_endmessage(StringInfo buf) ...@@ -188,11 +206,9 @@ pq_endmessage(StringInfo buf)
* This is the same as the pqcomm.c routine pq_putmessage, except that * This is the same as the pqcomm.c routine pq_putmessage, except that
* the message body is a null-terminated string to which encoding * the message body is a null-terminated string to which encoding
* conversion applies. * conversion applies.
*
* returns 0 if OK, EOF if trouble
* -------------------------------- * --------------------------------
*/ */
int void
pq_puttextmessage(char msgtype, const char *str) pq_puttextmessage(char msgtype, const char *str)
{ {
int slen = strlen(str); int slen = strlen(str);
...@@ -201,12 +217,22 @@ pq_puttextmessage(char msgtype, const char *str) ...@@ -201,12 +217,22 @@ pq_puttextmessage(char msgtype, const char *str)
p = (char *) pg_server_to_client((unsigned char *) str, slen); p = (char *) pg_server_to_client((unsigned char *) str, slen);
if (p != str) /* actual conversion has been done? */ if (p != str) /* actual conversion has been done? */
{ {
int result = pq_putmessage(msgtype, p, strlen(p) + 1); (void) pq_putmessage(msgtype, p, strlen(p) + 1);
pfree(p); pfree(p);
return result; return;
} }
return pq_putmessage(msgtype, str, slen + 1); (void) pq_putmessage(msgtype, str, slen + 1);
}
/* --------------------------------
* pq_putemptymessage - convenience routine for message with empty body
* --------------------------------
*/
void
pq_putemptymessage(char msgtype)
{
(void) pq_putmessage(msgtype, NULL, 0);
} }
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.313 2003/04/19 00:02:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.314 2003/04/22 00:08:06 tgl Exp $
* *
* NOTES * NOTES
* *
...@@ -1118,7 +1118,13 @@ ProcessStartupPacket(Port *port, bool SSLdone) ...@@ -1118,7 +1118,13 @@ ProcessStartupPacket(Port *port, bool SSLdone)
if (pq_getbytes((char *) &len, 4) == EOF) if (pq_getbytes((char *) &len, 4) == EOF)
{ {
elog(COMMERROR, "incomplete startup packet"); /*
* EOF after SSLdone probably means the client didn't like our
* response to NEGOTIATE_SSL_CODE. That's not an error condition,
* so don't clutter the log with a complaint.
*/
if (!SSLdone)
elog(COMMERROR, "incomplete startup packet");
return STATUS_ERROR; return STATUS_ERROR;
} }
...@@ -1127,7 +1133,10 @@ ProcessStartupPacket(Port *port, bool SSLdone) ...@@ -1127,7 +1133,10 @@ ProcessStartupPacket(Port *port, bool SSLdone)
if (len < (int32) sizeof(ProtocolVersion) || if (len < (int32) sizeof(ProtocolVersion) ||
len > MAX_STARTUP_PACKET_LENGTH) len > MAX_STARTUP_PACKET_LENGTH)
elog(FATAL, "invalid length of startup packet"); {
elog(COMMERROR, "invalid length of startup packet");
return STATUS_ERROR;
}
/* /*
* Allocate at least the size of an old-style startup packet, plus one * Allocate at least the size of an old-style startup packet, plus one
...@@ -1173,7 +1182,7 @@ ProcessStartupPacket(Port *port, bool SSLdone) ...@@ -1173,7 +1182,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#endif #endif
if (send(port->sock, &SSLok, 1, 0) != 1) if (send(port->sock, &SSLok, 1, 0) != 1)
{ {
elog(LOG, "failed to send SSL negotiation response: %m"); elog(COMMERROR, "failed to send SSL negotiation response: %m");
return STATUS_ERROR; /* close the connection */ return STATUS_ERROR; /* close the connection */
} }
...@@ -1188,6 +1197,11 @@ ProcessStartupPacket(Port *port, bool SSLdone) ...@@ -1188,6 +1197,11 @@ ProcessStartupPacket(Port *port, bool SSLdone)
/* Could add additional special packet types here */ /* Could add additional special packet types here */
/*
* Set FrontendProtocol now so that elog() knows what format to send
* if we fail during startup.
*/
FrontendProtocol = proto;
/* /*
* XXX temporary for 3.0 protocol development: we are using the minor * XXX temporary for 3.0 protocol development: we are using the minor
......
...@@ -8,7 +8,7 @@ ...@@ -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.52 2003/04/19 00:02:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.53 2003/04/22 00:08:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -141,7 +141,9 @@ EndCommand(const char *commandTag, CommandDest dest) ...@@ -141,7 +141,9 @@ EndCommand(const char *commandTag, CommandDest dest)
* libpq's crufty way of determining whether a multiple-command * libpq's crufty way of determining whether a multiple-command
* query string is done. In protocol 2.0 it's probably not really * query string is done. In protocol 2.0 it's probably not really
* necessary to distinguish empty queries anymore, but we still do it * necessary to distinguish empty queries anymore, but we still do it
* for backwards compatibility with 1.0. * for backwards compatibility with 1.0. In protocol 3.0 it has some
* use again, since it ensures that there will be a recognizable end
* to the response to an Execute message.
* ---------------- * ----------------
*/ */
void void
...@@ -153,9 +155,13 @@ NullCommand(CommandDest dest) ...@@ -153,9 +155,13 @@ NullCommand(CommandDest dest)
case Remote: case Remote:
/* /*
* tell the fe that we saw an empty query string * tell the fe that we saw an empty query string. In protocols
* before 3.0 this has a useless empty-string message body.
*/ */
pq_putbytes("I", 2); /* note we send I and \0 */ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
pq_putemptymessage('I');
else
pq_puttextmessage('I', "");
break; break;
case Debug: case Debug:
...@@ -184,7 +190,7 @@ ReadyForQuery(CommandDest dest) ...@@ -184,7 +190,7 @@ ReadyForQuery(CommandDest dest)
case RemoteInternal: case RemoteInternal:
case Remote: case Remote:
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
pq_putbytes("Z", 1); pq_putemptymessage('Z');
/* Flush output at end of cycle in any case. */ /* Flush output at end of cycle in any case. */
pq_flush(); pq_flush();
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.58 2003/04/19 00:02:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.59 2003/04/22 00:08:07 tgl Exp $
* *
* NOTES * NOTES
* This cruft is the server side of PQfn. * This cruft is the server side of PQfn.
...@@ -119,8 +119,7 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen) ...@@ -119,8 +119,7 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen)
{ {
StringInfoData buf; StringInfoData buf;
pq_beginmessage(&buf); pq_beginmessage(&buf, 'V');
pq_sendbyte(&buf, 'V');
if (retlen != 0) if (retlen != 0)
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.322 2003/04/19 00:02:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.323 2003/04/22 00:08:07 tgl Exp $
* *
* NOTES * NOTES
* this is the "main" module of the postgres backend and * this is the "main" module of the postgres backend and
...@@ -1821,8 +1821,7 @@ PostgresMain(int argc, char *argv[], const char *username) ...@@ -1821,8 +1821,7 @@ PostgresMain(int argc, char *argv[], const char *username)
{ {
StringInfoData buf; StringInfoData buf;
pq_beginmessage(&buf); pq_beginmessage(&buf, 'K');
pq_sendbyte(&buf, 'K');
pq_sendint(&buf, (int32) MyProcPid, sizeof(int32)); pq_sendint(&buf, (int32) MyProcPid, sizeof(int32));
pq_sendint(&buf, (int32) MyCancelKey, sizeof(int32)); pq_sendint(&buf, (int32) MyCancelKey, sizeof(int32));
pq_endmessage(&buf); pq_endmessage(&buf);
...@@ -1832,7 +1831,7 @@ PostgresMain(int argc, char *argv[], const char *username) ...@@ -1832,7 +1831,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.322 $ $Date: 2003/04/19 00:02:29 $\n"); puts("$Revision: 1.323 $ $Date: 2003/04/22 00:08:07 $\n");
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.107 2003/03/20 03:34:56 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.108 2003/04/22 00:08:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -406,20 +406,19 @@ elog(int lev, const char *fmt,...) ...@@ -406,20 +406,19 @@ elog(int lev, const char *fmt,...)
*/ */
oldcxt = MemoryContextSwitchTo(ErrorContext); oldcxt = MemoryContextSwitchTo(ErrorContext);
if (lev <= WARNING) if (lev >= ERROR)
/* exclude the timestamp from msg sent to frontend */
send_message_to_frontend(lev, msg_buf + timestamp_size);
else
{ {
/* /*
* Abort any COPY OUT in progress when an error is detected. * Abort any COPY OUT in progress when an error is detected.
* This hack is necessary because of poor design of copy * This hack is necessary because of poor design of old-style
* protocol. * copy protocol.
*/ */
pq_endcopyout(true); pq_endcopyout(true);
send_message_to_frontend(ERROR, msg_buf + timestamp_size);
} }
/* Exclude the timestamp from msg sent to frontend */
send_message_to_frontend(lev, msg_buf + timestamp_size);
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
} }
...@@ -745,11 +744,9 @@ send_message_to_frontend(int type, const char *msg) ...@@ -745,11 +744,9 @@ send_message_to_frontend(int type, const char *msg)
{ {
StringInfoData buf; StringInfoData buf;
AssertArg(type <= ERROR);
pq_beginmessage(&buf);
/* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */ /* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
pq_sendbyte(&buf, type < ERROR ? 'N' : 'E'); pq_beginmessage(&buf, (type < ERROR) ? 'N' : 'E');
/* XXX more to do here */
pq_sendstring(&buf, msg); pq_sendstring(&buf, msg);
pq_endmessage(&buf); pq_endmessage(&buf);
......
...@@ -9,7 +9,7 @@ ...@@ -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.77 2003/04/19 00:02:29 tgl Exp $ * $Id: pqcomm.h,v 1.78 2003/04/22 00:08:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -106,7 +106,7 @@ typedef union SockAddr ...@@ -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,101) /* XXX temporary value */ #define PG_PROTOCOL_LATEST PG_PROTOCOL(3,102) /* XXX temporary value */
typedef uint32 ProtocolVersion; /* FE/BE protocol version number */ typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
......
...@@ -6,7 +6,7 @@ ...@@ -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.14 2003/04/19 00:02:29 tgl Exp $ * $Id: pqformat.h,v 1.15 2003/04/22 00:08:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -15,8 +15,7 @@ ...@@ -15,8 +15,7 @@
#include "lib/stringinfo.h" #include "lib/stringinfo.h"
#define pq_beginmessage(buf) initStringInfo(buf) extern void pq_beginmessage(StringInfo buf, char msgtype);
extern void pq_sendbyte(StringInfo buf, int byt); extern void pq_sendbyte(StringInfo buf, int byt);
extern void pq_sendbytes(StringInfo buf, const char *data, int datalen); extern void pq_sendbytes(StringInfo buf, const char *data, int datalen);
extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen); extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen);
...@@ -24,7 +23,8 @@ extern void pq_sendstring(StringInfo buf, const char *str); ...@@ -24,7 +23,8 @@ extern void pq_sendstring(StringInfo buf, const char *str);
extern void pq_sendint(StringInfo buf, int i, int b); extern void pq_sendint(StringInfo buf, int i, int b);
extern void pq_endmessage(StringInfo buf); extern void pq_endmessage(StringInfo buf);
extern int pq_puttextmessage(char msgtype, const char *str); extern void pq_puttextmessage(char msgtype, const char *str);
extern void pq_putemptymessage(char msgtype);
extern int pq_getmsgbyte(StringInfo msg); extern int pq_getmsgbyte(StringInfo msg);
extern unsigned int pq_getmsgint(StringInfo msg, int b); extern unsigned int pq_getmsgint(StringInfo msg, int b);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.233 2003/04/19 00:02:30 tgl Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.234 2003/04/22 00:08:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1328,6 +1328,8 @@ keep_going: /* We will come back to here until there ...@@ -1328,6 +1328,8 @@ keep_going: /* We will come back to here until there
case CONNECTION_AWAITING_RESPONSE: case CONNECTION_AWAITING_RESPONSE:
{ {
char beresp; char beresp;
int msgLength;
int avail;
AuthRequest areq; AuthRequest areq;
/* /*
...@@ -1337,15 +1339,58 @@ keep_going: /* We will come back to here until there ...@@ -1337,15 +1339,58 @@ keep_going: /* We will come back to here until there
*/ */
conn->inCursor = conn->inStart; conn->inCursor = conn->inStart;
/* Read type byte */
if (pqGetc(&beresp, conn)) if (pqGetc(&beresp, conn))
{ {
/* We'll come back when there is more data */ /* We'll come back when there is more data */
return PGRES_POLLING_READING; return PGRES_POLLING_READING;
} }
/* Handle errors. */ /*
if (beresp == 'E') * Validate message type: we expect only an authentication
* request or an error here. Anything else probably means
* it's not Postgres on the other end at all.
*/
if (!(beresp == 'R' || beresp == 'E'))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext(
"expected authentication request from "
"server, but received %c\n"
),
beresp);
goto error_return;
}
/* Read message length word */
if (pqGetInt(&msgLength, 4, conn))
{
/* We'll come back when there is more data */
return PGRES_POLLING_READING;
}
/*
* Try to validate message length before using it.
* Authentication requests can't be very large. Errors
* can be a little larger, but not huge. If we see a large
* apparent length in an error, it means we're really talking
* to a pre-3.0-protocol server; cope.
*/
if (beresp == 'R' && (msgLength < 8 || msgLength > 100))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext(
"expected authentication request from "
"server, but received %c\n"
),
beresp);
goto error_return;
}
if (beresp == 'E' && (msgLength < 8 || msgLength > 30000))
{ {
/* Handle error from a pre-3.0 server */
conn->inCursor = conn->inStart + 1; /* reread data */
if (pqGets(&conn->errorMessage, conn)) if (pqGets(&conn->errorMessage, conn))
{ {
/* We'll come back when there is more data */ /* We'll come back when there is more data */
...@@ -1363,18 +1408,45 @@ keep_going: /* We will come back to here until there ...@@ -1363,18 +1408,45 @@ keep_going: /* We will come back to here until there
goto error_return; goto error_return;
} }
/* Otherwise it should be an authentication request. */ /*
if (beresp != 'R') * Can't process if message body isn't all here yet.
*/
msgLength -= 4;
avail = conn->inEnd - conn->inCursor;
if (avail < msgLength)
{ {
printfPQExpBuffer(&conn->errorMessage, /*
libpq_gettext( * Before returning, try to enlarge the input buffer if
"expected authentication request from " * needed to hold the whole message; see notes in
"server, but received %c\n" * parseInput.
), */
beresp); if (pqCheckInBufferSpace(conn->inCursor + msgLength, conn))
goto error_return;
/* We'll come back when there is more data */
return PGRES_POLLING_READING;
}
/* Handle errors. */
if (beresp == 'E')
{
if (pqGets(&conn->errorMessage, conn))
{
/* We'll come back when there is more data */
return PGRES_POLLING_READING;
}
/* OK, we read the message; mark data consumed */
conn->inStart = conn->inCursor;
/*
* The postmaster typically won't end its message with
* a newline, so add one to conform to libpq
* conventions.
*/
appendPQExpBufferChar(&conn->errorMessage, '\n');
goto error_return; goto error_return;
} }
/* It is an authentication request. */
/* Get the type of request. */ /* Get the type of request. */
if (pqGetInt((int *) &areq, 4, conn)) if (pqGetInt((int *) &areq, 4, conn))
{ {
......
This diff is collapsed.
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,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/interfaces/libpq/fe-misc.c,v 1.89 2003/04/19 00:02:30 tgl Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.90 2003/04/22 00:08:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -277,12 +277,12 @@ pqPutInt(int value, size_t bytes, PGconn *conn) ...@@ -277,12 +277,12 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
/* /*
* Make sure conn's output buffer can hold bytes_needed bytes (caller must * Make sure conn's output buffer can hold bytes_needed bytes (caller must
* include existing outCount into the value!) * include already-stored data into the value!)
* *
* Returns 0 on success, EOF on error * Returns 0 on success, EOF if failed to enlarge buffer
*/ */
static int static int
checkOutBufferSpace(int bytes_needed, PGconn *conn) pqCheckOutBufferSpace(int bytes_needed, PGconn *conn)
{ {
int newsize = conn->outBufSize; int newsize = conn->outBufSize;
char *newbuf; char *newbuf;
...@@ -335,6 +335,66 @@ checkOutBufferSpace(int bytes_needed, PGconn *conn) ...@@ -335,6 +335,66 @@ checkOutBufferSpace(int bytes_needed, PGconn *conn)
return EOF; return EOF;
} }
/*
* Make sure conn's input buffer can hold bytes_needed bytes (caller must
* include already-stored data into the value!)
*
* Returns 0 on success, EOF if failed to enlarge buffer
*/
int
pqCheckInBufferSpace(int bytes_needed, PGconn *conn)
{
int newsize = conn->inBufSize;
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->inBuffer, newsize);
if (newbuf)
{
/* realloc succeeded */
conn->inBuffer = newbuf;
conn->inBufSize = newsize;
return 0;
}
}
newsize = conn->inBufSize;
do {
newsize += 8192;
} while (bytes_needed > newsize && newsize > 0);
if (bytes_needed <= newsize)
{
newbuf = realloc(conn->inBuffer, newsize);
if (newbuf)
{
/* realloc succeeded */
conn->inBuffer = newbuf;
conn->inBufSize = newsize;
return 0;
}
}
/* realloc failed. Probably out of memory */
printfPQExpBuffer(&conn->errorMessage,
"cannot allocate memory for input buffer\n");
return EOF;
}
/* /*
* pqPutMsgStart: begin construction of a message to the server * pqPutMsgStart: begin construction of a message to the server
* *
...@@ -364,7 +424,7 @@ pqPutMsgStart(char msg_type, PGconn *conn) ...@@ -364,7 +424,7 @@ pqPutMsgStart(char msg_type, PGconn *conn)
else else
lenPos = conn->outCount; lenPos = conn->outCount;
/* make sure there is room for it */ /* make sure there is room for it */
if (checkOutBufferSpace(lenPos + 4, conn)) if (pqCheckOutBufferSpace(lenPos + 4, conn))
return EOF; return EOF;
/* okay, save the message type byte if any */ /* okay, save the message type byte if any */
if (msg_type) if (msg_type)
...@@ -390,7 +450,7 @@ static int ...@@ -390,7 +450,7 @@ static int
pqPutMsgBytes(const void *buf, size_t len, PGconn *conn) pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
{ {
/* make sure there is room for it */ /* make sure there is room for it */
if (checkOutBufferSpace(conn->outMsgEnd + len, conn)) if (pqCheckOutBufferSpace(conn->outMsgEnd + len, conn))
return EOF; return EOF;
/* okay, save the data */ /* okay, save the data */
memcpy(conn->outBuffer + conn->outMsgEnd, buf, len); memcpy(conn->outBuffer + conn->outMsgEnd, buf, len);
...@@ -486,13 +546,13 @@ pqReadData(PGconn *conn) ...@@ -486,13 +546,13 @@ pqReadData(PGconn *conn)
*/ */
if (conn->inBufSize - conn->inEnd < 8192) if (conn->inBufSize - conn->inEnd < 8192)
{ {
int newSize = conn->inBufSize * 2; if (pqCheckInBufferSpace(conn->inEnd + 8192, conn))
char *newBuf = (char *) realloc(conn->inBuffer, newSize);
if (newBuf)
{ {
conn->inBuffer = newBuf; /*
conn->inBufSize = newSize; * We don't insist that the enlarge worked, but we need some room
*/
if (conn->inBufSize - conn->inEnd < 100)
return -1; /* errorMessage already set */
} }
} }
......
...@@ -12,7 +12,7 @@ ...@@ -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.62 2003/04/19 00:02:30 tgl Exp $ * $Id: libpq-int.h,v 1.63 2003/04/22 00:08:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -56,7 +56,7 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast ...@@ -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,101) /* XXX temporary value */ #define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,102) /* XXX temporary value */
/* /*
* POSTGRES backend dependent Constants. * POSTGRES backend dependent Constants.
...@@ -216,7 +216,8 @@ struct pg_conn ...@@ -216,7 +216,8 @@ struct pg_conn
* is listening on; if NULL, uses a * is listening on; if NULL, uses a
* default constructed from pgport */ * default constructed from pgport */
char *pgtty; /* tty on which the backend messages is char *pgtty; /* tty on which the backend messages is
* displayed (NOT ACTUALLY USED???) */ * displayed (OBSOLETE, NOT USED) */
char *connect_timeout; /* connection timeout (numeric string) */
char *pgoptions; /* options to start the backend with */ char *pgoptions; /* options to start the backend with */
char *dbName; /* database name */ char *dbName; /* database name */
char *pguser; /* Postgres username and password, if any */ char *pguser; /* Postgres username and password, if any */
...@@ -232,6 +233,10 @@ struct pg_conn ...@@ -232,6 +233,10 @@ struct pg_conn
/* Status indicators */ /* Status indicators */
ConnStatusType status; ConnStatusType status;
PGAsyncStatusType asyncStatus; PGAsyncStatusType asyncStatus;
char copy_is_binary; /* 1 = copy binary, 0 = copy text */
int copy_already_done; /* # bytes already returned in COPY OUT */
int nonblocking; /* whether this connection is using a
* blocking socket to the backend or not */
Dllist *notifyList; /* Notify msgs not yet handed to Dllist *notifyList; /* Notify msgs not yet handed to
* application */ * application */
...@@ -246,6 +251,7 @@ struct pg_conn ...@@ -246,6 +251,7 @@ struct pg_conn
int be_key; /* key of backend --- needed for cancels */ int be_key; /* key of backend --- needed for cancels */
char md5Salt[4]; /* password salt received from backend */ char md5Salt[4]; /* password salt received from backend */
char cryptSalt[2]; /* password salt received from backend */ char cryptSalt[2]; /* password salt received from backend */
int client_encoding; /* encoding id */
PGlobjfuncs *lobjfuncs; /* private state for large-object access PGlobjfuncs *lobjfuncs; /* private state for large-object access
* fns */ * fns */
...@@ -258,9 +264,6 @@ struct pg_conn ...@@ -258,9 +264,6 @@ struct pg_conn
int inEnd; /* offset to first position after avail int inEnd; /* offset to first position after avail
* data */ * data */
int nonblocking; /* whether this connection is using a
* blocking socket to the backend or not */
/* Buffer for data not yet sent to backend */ /* Buffer for data not yet sent to backend */
char *outBuffer; /* currently allocated buffer */ char *outBuffer; /* currently allocated buffer */
int outBufSize; /* allocated size of buffer */ int outBufSize; /* allocated size of buffer */
...@@ -291,10 +294,6 @@ struct pg_conn ...@@ -291,10 +294,6 @@ struct pg_conn
/* Buffer for receiving various parts of messages */ /* Buffer for receiving various parts of messages */
PQExpBufferData workBuffer; /* expansible string */ PQExpBufferData workBuffer; /* expansible string */
int client_encoding; /* encoding id */
char *connect_timeout;
}; };
/* String descriptions of the ExecStatusTypes. /* String descriptions of the ExecStatusTypes.
...@@ -330,6 +329,7 @@ extern void pqClearAsyncResult(PGconn *conn); ...@@ -330,6 +329,7 @@ extern void pqClearAsyncResult(PGconn *conn);
* for Get, EOF merely means the buffer is exhausted, not that there is * for Get, EOF merely means the buffer is exhausted, not that there is
* necessarily any error. * necessarily any error.
*/ */
extern int pqCheckInBufferSpace(int bytes_needed, PGconn *conn);
extern int pqGetc(char *result, PGconn *conn); extern int pqGetc(char *result, PGconn *conn);
extern int pqPutc(char c, PGconn *conn); extern int pqPutc(char c, PGconn *conn);
extern int pqGets(PQExpBuffer buf, PGconn *conn); extern int pqGets(PQExpBuffer buf, PGconn *conn);
......
...@@ -995,7 +995,6 @@ copy test("........pg.dropped.1........") to stdout; ...@@ -995,7 +995,6 @@ copy test("........pg.dropped.1........") to stdout;
ERROR: Relation "test" has no column "........pg.dropped.1........" ERROR: Relation "test" has no column "........pg.dropped.1........"
copy test from stdin; copy test from stdin;
ERROR: copy: line 1, Extra data after last expected column ERROR: copy: line 1, Extra data after last expected column
lost synchronization with server, resetting connection
SET autocommit TO 'on'; SET autocommit TO 'on';
select * from test; select * from test;
b | c b | c
......
...@@ -35,17 +35,13 @@ ERROR: Attribute "d" specified more than once ...@@ -35,17 +35,13 @@ ERROR: Attribute "d" specified more than once
-- missing data: should fail -- missing data: should fail
COPY x from stdin; COPY x from stdin;
ERROR: copy: line 1, pg_atoi: zero-length string ERROR: copy: line 1, pg_atoi: zero-length string
lost synchronization with server, resetting connection
COPY x from stdin; COPY x from stdin;
ERROR: copy: line 1, Missing data for column "e" ERROR: copy: line 1, Missing data for column "e"
lost synchronization with server, resetting connection
COPY x from stdin; COPY x from stdin;
ERROR: copy: line 1, Missing data for column "e" ERROR: copy: line 1, Missing data for column "e"
lost synchronization with server, resetting connection
-- extra data: should fail -- extra data: should fail
COPY x from stdin; COPY x from stdin;
ERROR: copy: line 1, Extra data after last expected column ERROR: copy: line 1, Extra data after last expected column
lost synchronization with server, resetting connection
SET autocommit TO 'on'; SET autocommit TO 'on';
-- various COPY options: delimiters, oids, NULL string -- various COPY options: delimiters, oids, NULL string
COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x'; COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x';
......
...@@ -40,7 +40,6 @@ INSERT INTO basictest values ('88', 'haha', 'short', '123.1212'); -- Truncate ...@@ -40,7 +40,6 @@ INSERT INTO basictest values ('88', 'haha', 'short', '123.1212'); -- Truncate
-- Test copy -- Test copy
COPY basictest (testvarchar) FROM stdin; -- fail COPY basictest (testvarchar) FROM stdin; -- fail
ERROR: copy: line 1, value too long for type character varying(5) ERROR: copy: line 1, value too long for type character varying(5)
lost synchronization with server, resetting connection
SET autocommit TO 'on'; SET autocommit TO 'on';
COPY basictest (testvarchar) FROM stdin; COPY basictest (testvarchar) FROM stdin;
select * from basictest; select * from basictest;
...@@ -128,12 +127,10 @@ INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good ...@@ -128,12 +127,10 @@ INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
-- Test copy -- Test copy
COPY nulltest FROM stdin; --fail COPY nulltest FROM stdin; --fail
ERROR: copy: line 1, Domain dcheck does not allow NULL values ERROR: copy: line 1, Domain dcheck does not allow NULL values
lost synchronization with server, resetting connection
SET autocommit TO 'on'; SET autocommit TO 'on';
-- Last row is bad -- Last row is bad
COPY nulltest FROM stdin; COPY nulltest FROM stdin;
ERROR: copy: line 3, CopyFrom: rejected due to CHECK constraint "nulltest_col5" on "nulltest" ERROR: copy: line 3, CopyFrom: rejected due to CHECK constraint "nulltest_col5" on "nulltest"
lost synchronization with server, resetting connection
select * from nulltest; select * from nulltest;
col1 | col2 | col3 | col4 | col5 col1 | col2 | col3 | col4 | col5
------+------+------+------+------ ------+------+------+------+------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment