diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index 9833a8d4d0..5ce4830ebd 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -8,23 +8,25 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.42 1999/02/13 23:14:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.43 1999/04/25 03:19:23 tgl Exp $ * *------------------------------------------------------------------------- */ #include -#include -#include -#include -#include -#include -#include -#include +#include "postgres.h" + +#include "fmgr.h" +#include "access/heapam.h" +#include "access/printtup.h" +#include "catalog/pg_type.h" +#include "libpq/libpq.h" +#include "libpq/pqformat.h" +#include "utils/syscache.h" #ifdef MULTIBYTE -#include +#include "mb/pg_wchar.h" #endif static void printtup_setup(DestReceiver* self, TupleDesc typeinfo); @@ -152,6 +154,7 @@ static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) { DR_printtup *myState = (DR_printtup*) self; + StringInfoData buf; int i, j, k, @@ -172,7 +175,8 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) * tell the frontend to expect new tuple data (in ASCII style) * ---------------- */ - pq_putnchar("D", 1); + pq_beginmessage(&buf); + pq_sendbyte(&buf, 'D'); /* ---------------- * send a bitmap of which attributes are not null @@ -187,13 +191,13 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) k >>= 1; if (k == 0) /* end of byte? */ { - pq_putint(j, 1); + pq_sendint(&buf, j, 1); j = 0; k = 1 << 7; } } if (k != (1 << 7)) /* flush last partial byte */ - pq_putint(j, 1); + pq_sendint(&buf, j, 1); /* ---------------- * send the attributes of this tuple @@ -212,12 +216,12 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) #ifdef MULTIBYTE p = pg_server_to_client(outputstr, strlen(outputstr)); outputlen = strlen(p); - pq_putint(outputlen + VARHDRSZ, VARHDRSZ); - pq_putnchar(p, outputlen); + pq_sendint(&buf, outputlen + VARHDRSZ, VARHDRSZ); + pq_sendbytes(&buf, p, outputlen); #else outputlen = strlen(outputstr); - pq_putint(outputlen + VARHDRSZ, VARHDRSZ); - pq_putnchar(outputstr, outputlen); + pq_sendint(&buf, outputlen + VARHDRSZ, VARHDRSZ); + pq_sendbytes(&buf, outputstr, outputlen); #endif pfree(outputstr); } @@ -225,10 +229,12 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) { outputstr = ""; outputlen = strlen(outputstr); - pq_putint(outputlen + VARHDRSZ, VARHDRSZ); - pq_putnchar(outputstr, outputlen); + pq_sendint(&buf, outputlen + VARHDRSZ, VARHDRSZ); + pq_sendbytes(&buf, outputstr, outputlen); } } + + pq_endmessage(&buf); } /* ---------------- @@ -325,6 +331,7 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) { + StringInfoData buf; int i, j, k; @@ -335,7 +342,8 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) * tell the frontend to expect new tuple data (in binary style) * ---------------- */ - pq_putnchar("B", 1); + pq_beginmessage(&buf); + pq_sendbyte(&buf, 'B'); /* ---------------- * send a bitmap of which attributes are not null @@ -350,13 +358,13 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) k >>= 1; if (k == 0) /* end of byte? */ { - pq_putint(j, 1); + pq_sendint(&buf, j, 1); j = 0; k = 1 << 7; } } if (k != (1 << 7)) /* flush last partial byte */ - pq_putint(j, 1); + pq_sendint(&buf, j, 1); /* ---------------- * send the attributes of this tuple @@ -378,8 +386,8 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) /* variable length, assume a varlena structure */ len = VARSIZE(attr) - VARHDRSZ; - pq_putint(len, VARHDRSZ); - pq_putnchar(VARDATA(attr), len); + pq_sendint(&buf, len, VARHDRSZ); + pq_sendbytes(&buf, VARDATA(attr), len); #ifdef IPORTAL_DEBUG { @@ -399,20 +407,20 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) int16 i16; int32 i32; - pq_putint(len, sizeof(int32)); + pq_sendint(&buf, len, sizeof(int32)); switch (len) { case sizeof(int8): i8 = DatumGetChar(attr); - pq_putnchar((char *) &i8, len); + pq_sendbytes(&buf, (char *) &i8, len); break; case sizeof(int16): i16 = DatumGetInt16(attr); - pq_putnchar((char *) &i16, len); + pq_sendbytes(&buf, (char *) &i16, len); break; case sizeof(int32): i32 = DatumGetInt32(attr); - pq_putnchar((char *) &i32, len); + pq_sendbytes(&buf, (char *) &i32, len); break; } #ifdef IPORTAL_DEBUG @@ -421,8 +429,8 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) } else { - pq_putint(len, sizeof(int32)); - pq_putnchar(DatumGetPointer(attr), len); + pq_sendint(&buf, len, sizeof(int32)); + pq_sendbytes(&buf, DatumGetPointer(attr), len); #ifdef IPORTAL_DEBUG fprintf(stderr, "byref length %d data %x\n", len, DatumGetPointer(attr)); @@ -431,4 +439,6 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self) } } } + + pq_endmessage(&buf); } diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 453019871e..3d5cf92f7d 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -6,7 +6,7 @@ * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.44 1999/02/13 23:15:00 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.45 1999/04/25 03:19:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -94,6 +94,7 @@ #include "fmgr.h" #include "lib/dllist.h" #include "libpq/libpq.h" +#include "libpq/pqformat.h" #include "miscadmin.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" @@ -798,9 +799,12 @@ NotifyMyFrontEnd(char *relname, int32 listenerPID) { if (whereToSendOutput == Remote) { - pq_putnchar("A", 1); - pq_putint(listenerPID, sizeof(int32)); - pq_putstr(relname); + StringInfoData buf; + pq_beginmessage(&buf); + pq_sendbyte(&buf, 'A'); + pq_sendint(&buf, listenerPID, sizeof(int32)); + pq_sendstring(&buf, relname, strlen(relname)); + pq_endmessage(&buf); /* NOTE: we do not do pq_flush() here. For a self-notify, it will * happen at the end of the transaction, and for incoming notifies * ProcessIncomingNotify will do it after finding all the notifies. diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 8b75f5b3cd..4efa13635a 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.73 1999/02/13 23:15:04 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.74 1999/04/25 03:19:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -89,12 +89,14 @@ inline void CopyDonePeek(FILE *fp, int c, int pickup); * * CopySendString does the same for null-terminated strings * CopySendChar does the same for single characters + * + * NB: no data conversion is applied by these functions */ inline void CopySendData(void *databuf, int datasize, FILE *fp) { if (!fp) - pq_putnchar(databuf, datasize); + pq_putbytes((char*) databuf, datasize); else - fwrite(databuf, datasize, 1, fp); + fwrite(databuf, datasize, 1, fp); } inline void CopySendString(char *str, FILE *fp) { @@ -112,17 +114,24 @@ inline void CopySendChar(char c, FILE *fp) { * * CopyGetChar does the same for single characters * CopyGetEof checks if it's EOF on the input + * + * NB: no data conversion is applied by these functions */ inline void CopyGetData(void *databuf, int datasize, FILE *fp) { if (!fp) - pq_getnchar(databuf, 0, datasize); + pq_getbytes((char*) databuf, datasize); else fread(databuf, datasize, 1, fp); } inline int CopyGetChar(FILE *fp) { if (!fp) - return pq_getchar(); + { + unsigned char ch; + if (pq_getbytes((char*) &ch, 1)) + return EOF; + return ch; + } else return getc(fp); } @@ -143,7 +152,7 @@ inline int CopyGetEof(FILE *fp) { */ inline int CopyPeekChar(FILE *fp) { if (!fp) - return pq_peekchar(); + return pq_peekbyte(); else return getc(fp); } @@ -153,7 +162,7 @@ inline void CopyDonePeek(FILE *fp, int c, int pickup) { if (pickup) { /* We want to pick it up - just receive again into dummy buffer */ char c; - pq_getnchar(&c, 0, 1); + pq_getbytes(&c, 1); } /* If we didn't want to pick it up, just leave it where it sits */ } @@ -216,7 +225,10 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, * descriptor leak. bjm 1998/08/29 */ if (file_opened) + { FreeFile(fp); + file_opened = false; + } rel = heap_openr(relname); if (rel == NULL) @@ -271,6 +283,7 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, if (IsUnderPostmaster) { SendCopyBegin(); + pq_startcopyout(); fp = NULL; } else @@ -301,9 +314,12 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, FreeFile(fp); file_opened = false; } - else if (!from && !binary) + else if (!from) { - CopySendData("\\.\n",3,fp); + if (!binary) + CopySendData("\\.\n",3,fp); + if (IsUnderPostmaster) + pq_endcopyout(false); } } } diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 6745b4f1f3..a26579270c 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -4,7 +4,7 @@ * * Copyright (c) 1994-5, Regents of the University of California * - * $Id: explain.c,v 1.34 1999/04/23 21:23:48 momjian Exp $ + * $Id: explain.c,v 1.35 1999/04/25 03:19:09 tgl Exp $ * */ #include @@ -350,18 +350,13 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) static char * Explain_PlanToString(Plan *plan, ExplainState *es) { - StringInfo str; - char *s; + StringInfoData str; - if (plan == NULL) - return ""; - Assert(plan != NULL); - str = makeStringInfo(); - explain_outNode(str, plan, 0, es); - s = str->data; - pfree(str); - - return s; + /* see stringinfo.h for an explanation of this maneuver */ + initStringInfo(&str); + if (plan != NULL) + explain_outNode(&str, plan, 0, es); + return str.data; } /* diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c index 2d129d12b2..3ded779974 100644 --- a/src/backend/lib/stringinfo.c +++ b/src/backend/lib/stringinfo.c @@ -1,125 +1,171 @@ -/* +/*------------------------------------------------------------------------- + * * stringinfo.c - * These are routines that can be used to write informations to a string, - * without having to worry about string lengths, space allocation etc. - * Ideally the interface should look like the file i/o interface, + * + * StringInfo provides an indefinitely-extensible string data type. + * It can be used to buffer either ordinary C strings (null-terminated text) + * or arbitrary binary data. All storage is allocated with palloc(). * * Copyright (c) 1994, Regents of the University of California * - * $Id: stringinfo.c,v 1.14 1999/02/13 23:15:36 momjian Exp $ + * $Id: stringinfo.c,v 1.15 1999/04/25 03:19:25 tgl Exp $ + * + *------------------------------------------------------------------------- */ #include #include -#include - -#include - -#include -#include +#include "postgres.h" +#include "lib/stringinfo.h" /* * makeStringInfo * - * Create a StringInfoData & return a pointer to it. - * + * Create an empty 'StringInfoData' & return a pointer to it. */ StringInfo -makeStringInfo() +makeStringInfo(void) { StringInfo res; - int size; res = (StringInfo) palloc(sizeof(StringInfoData)); if (res == NULL) - elog(ERROR, "makeStringInfo: Out of memory!"); + elog(ERROR, "makeStringInfo: Out of memory"); - size = 256; /* initial default size */ - res->data = palloc(size); - if (res->data == NULL) - { - elog(ERROR, - "makeStringInfo: Out of memory! (%d bytes requested)", size); - } - res->maxlen = size; - res->len = 0; - /* Make sure the string is empty initially. */ - res->data[0] = '\0'; + initStringInfo(res); return res; } /* - * appendStringInfo - * - * append to the current 'StringInfo' a new string. - * If there is not enough space in the current 'data', then reallocate - * some more... - * - * NOTE: if we reallocate space, we pfree the old one! + * initStringInfo * + * Initialize a StringInfoData struct (with previously undefined contents) + * to describe an empty string. */ void -appendStringInfo(StringInfo str, const char *fmt,...) +initStringInfo(StringInfo str) { - int buflen, - newlen, - needed; - char *s, - buffer[512]; + int size = 256; /* initial default buffer size */ - va_list args; - va_start(args, fmt); - buflen = vsnprintf(buffer, 512, fmt, args); - va_end(args); + str->data = palloc(size); + if (str->data == NULL) + elog(ERROR, + "initStringInfo: Out of memory (%d bytes requested)", size); + str->maxlen = size; + str->len = 0; + str->data[0] = '\0'; +} + +/* + * enlargeStringInfo + * + * Internal routine: make sure there is enough space for 'needed' more bytes + * ('needed' does not include the terminating null). + */ +static void +enlargeStringInfo(StringInfo str, int needed) +{ + int newlen; + char *newdata; + + 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; + + newdata = palloc(newlen); + if (newdata == NULL) + elog(ERROR, + "enlargeStringInfo: Out of memory (%d bytes requested)", newlen); + + /* OK, transfer data into new buffer, and release old buffer */ + memcpy(newdata, str->data, str->len + 1); + pfree(str->data); + str->data = newdata; + str->maxlen = newlen; +} + +/* + * appendStringInfo + * + * Format text data under the control of fmt (an sprintf-like format string) + * and append it to whatever is already in str. More space is allocated + * to str if necessary. This is sort of like a combination of sprintf and + * strcat. + * + * CAUTION: the current implementation has a 1K limit on the amount of text + * generated in a single call (not on the total string length). + */ +void +appendStringInfo(StringInfo str, const char *fmt, ...) +{ + va_list args; + char buffer[1024]; + int buflen; Assert(str != NULL); - if (buflen == 0) - strcpy(buffer, "<>"); - /* - * do we have enough space to append the new string? (don't forget to - * count the null string terminating char!) If no, then reallocate - * some more. - */ - needed = str->len + buflen + 1; - if (needed > str->maxlen) - { + va_start(args, fmt); + buflen = vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); - /* - * how much more space to allocate ? Let's say double the current - * space... However we must check if this is enough! - */ - newlen = 2 * str->maxlen; - while (needed > newlen) - newlen = 2 * newlen; + /* Make more room if needed */ + enlargeStringInfo(str, buflen); - /* - * allocate enough space. - */ - s = palloc(newlen); - if (s == NULL) - { - elog(ERROR, - "appendStringInfo: Out of memory (%d bytes requested)", newlen); - } - /* - * transfer the data. strcpy() would work, but is probably a tad - * slower than memcpy(), and since we know the string length... - */ - memcpy(s, str->data, str->len + 1); - pfree(str->data); - str->maxlen = newlen; - str->data = s; - } - - /* - * OK, we have enough space now, append 'buffer' at the end of the - * string & update the string length. NOTE: strcat() would work, - * but is certainly slower than just memcpy'ing the data to the right - * place. - */ + /* OK, append the data, including the trailing null */ memcpy(str->data + str->len, buffer, buflen + 1); str->len += buflen; } + +/*------------------------ + * appendStringInfoChar + * Append a single byte to str. + * Like appendStringInfo(str, "%c", ch) but much faster. + */ +void +appendStringInfoChar(StringInfo str, char ch) +{ + Assert(str != NULL); + + /* Make more room if needed */ + enlargeStringInfo(str, 1); + + /* OK, append the character */ + str->data[str->len] = ch; + str->len++; + str->data[str->len] = '\0'; +} + +/* + * appendBinaryStringInfo + * + * Append arbitrary binary data to a StringInfo, allocating more space + * if necessary. + */ +void +appendBinaryStringInfo(StringInfo str, const char *data, int datalen) +{ + Assert(str != NULL); + + /* Make more room if needed */ + enlargeStringInfo(str, datalen); + + /* OK, append the data */ + memcpy(str->data + str->len, data, datalen); + str->len += datalen; + + /* Keep a trailing null in place, even though it's probably useless + * for binary data... + */ + str->data[str->len] = '\0'; +} diff --git a/src/backend/libpq/portal.c b/src/backend/libpq/portal.c index a297387c68..05970a5cc3 100644 --- a/src/backend/libpq/portal.c +++ b/src/backend/libpq/portal.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: portal.c,v 1.20 1999/02/13 23:15:45 momjian Exp $ + * $Id: portal.c,v 1.21 1999/04/25 03:19:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,9 +37,6 @@ * PQgetvalue - Return an attribute (field) value * PQgetlength - Return an attribute (field) length * PQclear - free storage claimed by named portal - * PQnotifies - Return a list of relations on which notification - * has occurred. - * PQremoveNotify - Remove this notification from the list. * * NOTES * These functions may be used by both frontend routines which @@ -647,85 +644,3 @@ PQclear(char *pname) return; pbuf_close(pname); } - -/* - * async notification. - * This is going away with pending rewrite of comm. code... - */ -/* static SLList pqNotifyList;*/ -static Dllist *pqNotifyList = NULL; - -/* remove invalid notifies before returning */ -void -PQcleanNotify() -{ - Dlelem *e, - *next; - PQNotifyList *p; - - e = DLGetHead(pqNotifyList); - - while (e) - { - next = DLGetSucc(e); - p = (PQNotifyList *) DLE_VAL(e); - if (p->valid == 0) - { - DLRemove(e); - DLFreeElem(e); - pfree(p); - } - e = next; - } -} - -void -PQnotifies_init() -{ - Dlelem *e; - PQNotifyList *p; - - if (pqNotifyList == NULL) - pqNotifyList = DLNewList(); - else - { - /* clean all notifies */ - for (e = DLGetHead(pqNotifyList); e != NULL; e = DLGetSucc(e)) - { - p = (PQNotifyList *) DLE_VAL(e); - p->valid = 0; - } - PQcleanNotify(); - } -} - -PQNotifyList * -PQnotifies() -{ - Dlelem *e; - - PQcleanNotify(); - e = DLGetHead(pqNotifyList); - return e ? (PQNotifyList *) DLE_VAL(e) : NULL; -} - -void -PQremoveNotify(PQNotifyList *nPtr) -{ - nPtr->valid = 0; /* remove later */ -} - -void -PQappendNotify(char *relname, int pid) -{ - PQNotifyList *p; - - if (pqNotifyList == NULL) - pqNotifyList = DLNewList(); - - p = (PQNotifyList *) pbuf_alloc(sizeof(PQNotifyList)); - StrNCpy(p->relname, relname, NAMEDATALEN); - p->be_pid = pid; - p->valid = 1; - DLAddTail(pqNotifyList, DLNewElem(p)); -} diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index a4843fb3f6..7f890569d3 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -3,43 +3,60 @@ * pqcomm.c * Communication functions between the Frontend and the Backend * + * These routines handle the low-level details of communication between + * frontend and backend. They just shove data across the communication + * channel, and are ignorant of the semantics of the data --- or would be, + * except for major brain damage in the design of the COPY OUT protocol. + * Unfortunately, COPY OUT is designed to commandeer the communication + * channel (it just transfers data without wrapping it into messages). + * No other messages can be sent while COPY OUT is in progress; and if the + * copy is aborted by an elog(ERROR), we need to close out the copy so that + * the frontend gets back into sync. Therefore, these routines have to be + * aware of COPY OUT state. + * + * NOTE: generally, it's a bad idea to emit outgoing messages directly with + * pq_putbytes(), especially if the message would require multiple calls + * 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 + * ensure that the channel will not be clogged by an incomplete message + * if execution is aborted by elog(ERROR) partway through the message. + * The only non-libpq code that should call pq_putbytes directly is COPY OUT. + * + * At one time, libpq was shared between frontend and backend, but now + * the backend's "backend/libpq" is quite separate from "interfaces/libpq". + * All that remains is similarities of names to trap the unwary... + * * Copyright (c) 1994, Regents of the University of California * - * $Id: pqcomm.c,v 1.67 1999/02/18 01:13:26 tgl Exp $ + * $Id: pqcomm.c,v 1.68 1999/04/25 03:19:21 tgl Exp $ * *------------------------------------------------------------------------- */ -/* + +/*------------------------ * INTERFACE ROUTINES - * pq_init - initialize libpq - * pq_getport - return the PGPORT setting - * pq_close - close input / output connections - * pq_flush - flush pending output - * pq_recvbuf - load some bytes into the input buffer - * pq_getstr - get a null terminated string from connection - * pq_getchar - get 1 character from connection - * pq_peekchar - peek at next character from connection - * pq_getnchar - get n characters from connection, and null-terminate - * pq_getint - get an integer from connection - * pq_putchar - send 1 character to connection - * pq_putstr - send a null terminated string to connection - * pq_putnchar - send n characters to connection - * pq_putint - send an integer to connection - * pq_putncharlen - send n characters to connection - * (also send an int header indicating - * the length) - * pq_getinaddr - initialize address from host and port number - * pq_getinserv - initialize address from host and service name * - * StreamDoUnlink - Shutdown UNIX socket connection - * StreamServerPort - Open socket stream + * setup/teardown: + * StreamServerPort - Open postmaster's server port * StreamConnection - Create new connection with client * StreamClose - Close a client/backend connection - * - * NOTES - * Frontend is now completely in interfaces/libpq, and no - * functions from this file are used there. + * pq_getport - return the PGPORT setting + * pq_init - initialize libpq at backend startup + * pq_close - shutdown libpq at backend exit * + * low-level I/O: + * pq_getbytes - get a known number of bytes from connection + * pq_getstring - get a null terminated string from connection + * pq_peekbyte - peek at next byte from connection + * pq_putbytes - send bytes to connection (not flushed until pq_flush) + * pq_flush - flush pending output + * + * message-level I/O (and COPY OUT cruft): + * pq_putmessage - send a normal message (suppressed in COPY OUT mode) + * pq_startcopyout - inform libpq that a COPY OUT transfer is beginning + * pq_endcopyout - end a COPY OUT transfer + * + *------------------------ */ #include "postgres.h" @@ -62,89 +79,57 @@ #include #include -#if defined(linux) -#ifndef SOMAXCONN -#define SOMAXCONN 5 /* from Linux listen(2) man page */ -#endif /* SOMAXCONN */ -#endif /* linux */ - +#include "libpq/libpq.h" /* where my declarations go */ #include "miscadmin.h" #include "libpq/pqsignal.h" #include "libpq/auth.h" -#include "libpq/libpq.h" /* where the declarations go */ #include "storage/ipc.h" -#ifdef MULTIBYTE -#include "mb/pg_wchar.h" -#endif #include "utils/trace.h" +#ifndef SOMAXCONN +#define SOMAXCONN 5 /* from Linux listen(2) man page */ +#endif /* SOMAXCONN */ + extern FILE * debug_port; /* in util.c */ /* - * Buffers + * Buffers for low-level I/O */ -unsigned char PqSendBuffer[PQ_BUFFER_SIZE]; -unsigned char PqRecvBuffer[PQ_BUFFER_SIZE]; -int PqSendPointer,PqRecvPointer,PqRecvLength; + +#define PQ_BUFFER_SIZE 8192 + +static unsigned char PqSendBuffer[PQ_BUFFER_SIZE]; +static int PqSendPointer; /* Next index to store a byte in PqSendBuffer */ + +static unsigned char PqRecvBuffer[PQ_BUFFER_SIZE]; +static int PqRecvPointer; /* Next index to read a byte from PqRecvBuffer */ +static int PqRecvLength; /* End of data available in PqRecvBuffer */ + +/* + * Message status + */ +static bool DoingCopyOut; /* -------------------------------- - * pq_init - open portal file descriptors + * pq_init - initialize libpq at backend startup * -------------------------------- */ void -pq_init(int fd) +pq_init(void) { PqSendPointer = PqRecvPointer = PqRecvLength = 0; - PQnotifies_init(); + DoingCopyOut = false; if (getenv("LIBPQ_DEBUG")) debug_port = stderr; } -/* ------------------------- - * pq_getchar() - * - * get a character from the input file, or EOF if trouble - * -------------------------------- - */ - -int -pq_getchar(void) -{ - while (PqRecvPointer >= PqRecvLength) - { - if (pq_recvbuf()) /* If nothing in buffer, then recv some */ - return EOF; /* Failed to recv data */ - } - return PqRecvBuffer[PqRecvPointer++]; -} - -/* ------------------------- - * pq_peekchar() - * - * get a character from the connection, but leave it in the buffer - * to be read again - * -------------------------------- - */ - -int -pq_peekchar(void) -{ - while (PqRecvPointer >= PqRecvLength) - { - if (pq_recvbuf()) /* If nothing in buffer, then recv some */ - return EOF; /* Failed to recv data */ - } - /* Note we don't bump the pointer... */ - return PqRecvBuffer[PqRecvPointer]; -} - /* -------------------------------- * pq_getport - return the PGPORT setting * -------------------------------- */ int -pq_getport() +pq_getport(void) { char *envport = getenv("PGPORT"); @@ -154,327 +139,16 @@ pq_getport() } /* -------------------------------- - * pq_close - close input / output connections + * pq_close - shutdown libpq at backend exit * -------------------------------- */ void -pq_close() +pq_close(void) { close(MyProcPort->sock); - PQnotifies_init(); } -/* -------------------------------- - * pq_flush - flush pending output - * - * returns 0 if OK, EOF if trouble - * -------------------------------- - */ -int -pq_flush() -{ - unsigned char *bufptr = PqSendBuffer; - unsigned char *bufend = PqSendBuffer + PqSendPointer; - while (bufptr < bufend) - { - int r = send(MyProcPort->sock, bufptr, bufend - bufptr, 0); - if (r <= 0) - { - if (errno == EINTR) - continue; /* Ok if we were interrupted */ - /* We would like to use elog() here, but cannot because elog - * tries to write to the client, which would cause a recursive - * flush attempt! So just write it out to the postmaster log. - */ - fprintf(stderr, "pq_flush: send() failed, errno %d\n", errno); - /* We drop the buffered data anyway so that processing - * can continue, even though we'll probably quit soon. - */ - PqSendPointer = 0; - return EOF; - } - bufptr += r; - } - PqSendPointer = 0; - return 0; -} - -/* -------------------------------- - * pq_recvbuf - load some bytes into the input buffer - * - * returns 0 if OK, EOF if trouble - * -------------------------------- - */ - -int -pq_recvbuf() -{ - if (PqRecvPointer > 0) - { - if (PqRecvLength > PqRecvPointer) - { - /* still some unread data, left-justify it in the buffer */ - memmove(PqRecvBuffer, PqRecvBuffer+PqRecvPointer, - PqRecvLength-PqRecvPointer); - PqRecvLength -= PqRecvPointer; - PqRecvPointer = 0; - } - else - PqRecvLength = PqRecvPointer = 0; - } - - /* Can fill buffer from PqRecvLength and upwards */ - for (;;) - { - int r = recv(MyProcPort->sock, PqRecvBuffer + PqRecvLength, - PQ_BUFFER_SIZE - PqRecvLength, 0); - if (r < 0) - { - if (errno == EINTR) - continue; /* Ok if interrupted */ - /* We would like to use elog() here, but dare not because elog - * tries to write to the client, which will cause problems - * if we have a hard communications failure ... - * So just write the message to the postmaster log. - */ - fprintf(stderr, "pq_recvbuf: recv() failed, errno=%d\n", errno); - return EOF; - } - if (r == 0) - { - /* as above, elog not safe */ - fprintf(stderr, "pq_recvbuf: unexpected EOF on client connection\n"); - return EOF; - } - /* r contains number of bytes read, so just incr length */ - PqRecvLength += r; - return 0; - } -} - -/* -------------------------------- - * pq_getstr - get a null terminated string from connection - * -------------------------------- - */ -int -pq_getstr(char *s, int maxlen) -{ - int c; -#ifdef MULTIBYTE - char *p; -#endif - - c = pqGetString(s, maxlen); - -#ifdef MULTIBYTE - p = (char*) pg_client_to_server((unsigned char *) s, maxlen); - if (s != p) /* actual conversion has been done? */ - strcpy(s, p); -#endif - - return c; -} - -/* -------------------------------- - * pq_getnchar - get n characters from connection, and null terminate - * -------------------------------- - */ -int -pq_getnchar(char *s, int off, int maxlen) -{ - int r = pqGetNBytes(s + off, maxlen); - s[off+maxlen] = '\0'; - return r; -} - -/* -------------------------------- - * pq_getint - get an integer from connection - * we receive an integer a byte at a type and reconstruct it so that - * machines with different ENDIAN representations can talk to each - * other - * -------------------------------- - */ -int -pq_getint(int b) -{ - int n, - status = 1; - - /* - * mjl: Seems inconsisten w/ return value of pq_putint (void). Also, - * EOF is a valid return value for an int! XXX - */ - - switch (b) - { - case 1: - status = ((n = pq_getchar()) == EOF); - break; - case 2: - status = pqGetShort(&n); - break; - case 4: - status = pqGetLong(&n); - break; - default: - fprintf(stderr, "** Unsupported size %d\n", b); - } - - if (status) - { - snprintf(PQerrormsg, ERROR_MSG_LENGTH, - "FATAL: pq_getint failed: errno=%d\n", errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - n = 0; - } - - return n; -} - -/* -------------------------------- - * pq_putstr - send a null terminated string to connection - * -------------------------------- - */ -void -pq_putstr(char *s) -{ -#ifdef MULTIBYTE - unsigned char *p; - - p = pg_server_to_client(s, strlen(s)); - if (pqPutString(p)) -#else - if (pqPutString(s)) -#endif - { - snprintf(PQerrormsg, ERROR_MSG_LENGTH, - "FATAL: pq_putstr: fputs() failed: errno=%d\n", errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } -} - -/* -------------------------------- - * pq_putnchar - send n characters to connection - * -------------------------------- - */ -void -pq_putnchar(char *s, int n) -{ - if (pqPutNBytes(s, n)) - { - snprintf(PQerrormsg, ERROR_MSG_LENGTH, - "FATAL: pq_putnchar: pqPutNBytes() failed: errno=%d\n", - errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } -} - -/* -------------------------------- - * pq_putint - send an integer to connection - * we chop an integer into bytes and send individual bytes - * machines with different ENDIAN representations can still talk to each - * other - * -------------------------------- - */ -void -pq_putint(int i, int b) -{ - int status; - - status = 1; - switch (b) - { - case 1: - status = (pq_putchar(i) == EOF); - break; - case 2: - status = pqPutShort(i); - break; - case 4: - status = pqPutLong(i); - break; - default: - fprintf(stderr, "** Unsupported size %d\n", b); - } - - if (status) - { - snprintf(PQerrormsg, ERROR_MSG_LENGTH, - "FATAL: pq_putint failed: errno=%d\n", errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } -} - -/* -------------------------------- - * pq_getinaddr - initialize address from host and port number - * -------------------------------- - */ -int -pq_getinaddr(struct sockaddr_in * sin, - char *host, - int port) -{ - struct hostent *hs; - - MemSet((char *) sin, 0, sizeof(*sin)); - - if (host) - { - if (*host >= '0' && *host <= '9') - sin->sin_addr.s_addr = inet_addr(host); - else - { - if (!(hs = gethostbyname(host))) - { - perror(host); - return 1; - } - if (hs->h_addrtype != AF_INET) - { - snprintf(PQerrormsg, ERROR_MSG_LENGTH, - "FATAL: pq_getinaddr: %s not on Internet\n", - host); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return 1; - } - memmove((char *) &sin->sin_addr, - hs->h_addr, - hs->h_length); - } - } - sin->sin_family = AF_INET; - sin->sin_port = htons(port); - return 0; -} - -/* -------------------------------- - * pq_getinserv - initialize address from host and servive name - * -------------------------------- - */ -int -pq_getinserv(struct sockaddr_in * sin, char *host, char *serv) -{ - struct servent *ss; - - if (*serv >= '0' && *serv <= '9') - return pq_getinaddr(sin, host, atoi(serv)); - if (!(ss = getservbyname(serv, NULL))) - { - snprintf(PQerrormsg, ERROR_MSG_LENGTH, - "FATAL: pq_getinserv: unknown service: %s\n", - serv); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return 1; - } - return pq_getinaddr(sin, host, ntohs(ss->s_port)); -} /* * Streams -- wrapper around Unix socket system calls @@ -489,7 +163,7 @@ static char sock_path[MAXPGPATH + 1] = ""; * Shutdown routine for backend connection * If a Unix socket is used for communication, explicitly close it. */ -void +static void StreamDoUnlink() { Assert(sock_path[0]); @@ -499,12 +173,7 @@ StreamDoUnlink() /* * StreamServerPort -- open a sock stream "listening" port. * - * This initializes the Postmaster's connection - * accepting port. - * - * ASSUME: that this doesn't need to be non-blocking because - * the Postmaster uses select() to tell when the socket - * is ready. + * This initializes the Postmaster's connection-accepting port. * * RETURNS: STATUS_OK or STATUS_ERROR */ @@ -648,7 +317,9 @@ StreamServerPort(char *hostName, short portName, int *fdP) * StreamConnection -- create a new connection with client using * server port. * - * This one should be non-blocking. + * ASSUME: that this doesn't need to be non-blocking because + * the Postmaster uses select() to tell when the server master + * socket is ready for accept(). * * RETURNS: STATUS_OK or STATUS_ERROR */ @@ -711,31 +382,295 @@ StreamClose(int sock) close(sock); } -#ifdef MULTIBYTE -void -pq_putncharlen(char *s, int n) -{ - unsigned char *p; - int len; - p = pg_server_to_client(s, n); - len = strlen(p); - pq_putint(len, sizeof(int)); - pq_putnchar(p, len); -} - -#endif - - -/* - * Act like the stdio putc() function. Write one character - * to the stream. Return this character, or EOF on error. +/* -------------------------------- + * Low-level I/O routines begin here. + * + * These routines communicate with a frontend client across a connection + * already established by the preceding routines. + * -------------------------------- */ -int pq_putchar(unsigned char c) + + +/* -------------------------------- + * pq_recvbuf - load some bytes into the input buffer + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +static int +pq_recvbuf(void) { - if (PqSendPointer >= PQ_BUFFER_SIZE) - if (pq_flush()) /* If buffer is full, then flush it out */ + if (PqRecvPointer > 0) + { + if (PqRecvLength > PqRecvPointer) + { + /* still some unread data, left-justify it in the buffer */ + memmove(PqRecvBuffer, PqRecvBuffer+PqRecvPointer, + PqRecvLength-PqRecvPointer); + PqRecvLength -= PqRecvPointer; + PqRecvPointer = 0; + } + else + PqRecvLength = PqRecvPointer = 0; + } + + /* Can fill buffer from PqRecvLength and upwards */ + for (;;) + { + int r = recv(MyProcPort->sock, PqRecvBuffer + PqRecvLength, + PQ_BUFFER_SIZE - PqRecvLength, 0); + if (r < 0) + { + if (errno == EINTR) + continue; /* Ok if interrupted */ + /* We would like to use elog() here, but dare not because elog + * tries to write to the client, which will cause problems + * if we have a hard communications failure ... + * So just write the message to the postmaster log. + */ + fprintf(stderr, "pq_recvbuf: recv() failed, errno=%d\n", errno); return EOF; - PqSendBuffer[PqSendPointer++] = c; /* Put in buffer */ - return c; + } + if (r == 0) + { + /* as above, elog not safe */ + fprintf(stderr, "pq_recvbuf: unexpected EOF on client connection\n"); + return EOF; + } + /* r contains number of bytes read, so just incr length */ + PqRecvLength += r; + return 0; + } +} + +/* -------------------------------- + * pq_getbyte - get a single byte from connection, or return EOF + * -------------------------------- + */ +static int +pq_getbyte(void) +{ + while (PqRecvPointer >= PqRecvLength) + { + if (pq_recvbuf()) /* If nothing in buffer, then recv some */ + return EOF; /* Failed to recv data */ + } + return PqRecvBuffer[PqRecvPointer++]; +} + +/* -------------------------------- + * pq_peekbyte - peek at next byte from connection + * + * Same as pq_getbyte() except we don't advance the pointer. + * -------------------------------- + */ +int +pq_peekbyte(void) +{ + while (PqRecvPointer >= PqRecvLength) + { + if (pq_recvbuf()) /* If nothing in buffer, then recv some */ + return EOF; /* Failed to recv data */ + } + return PqRecvBuffer[PqRecvPointer]; +} + +/* -------------------------------- + * pq_getbytes - get a known number of bytes from connection + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +pq_getbytes(char *s, size_t len) +{ + size_t amount; + + while (len > 0) + { + while (PqRecvPointer >= PqRecvLength) + { + if (pq_recvbuf()) /* If nothing in buffer, then recv some */ + return EOF; /* Failed to recv data */ + } + amount = PqRecvLength - PqRecvPointer; + if (amount > len) + amount = len; + memcpy(s, PqRecvBuffer + PqRecvPointer, amount); + PqRecvPointer += amount; + s += amount; + len -= amount; + } + return 0; +} + +/* -------------------------------- + * pq_getstring - get a null terminated string from connection + * + * NOTE: this routine does not do any MULTIBYTE conversion, + * even though it is presumably useful only for text, because + * no code in this module should depend on MULTIBYTE mode. + * See pq_getstr in pqformat.c for that. + * + * FIXME: we ought to use an expansible StringInfo buffer, + * rather than dropping data if the message is too long. + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +pq_getstring(char *s, size_t len) +{ + int c; + + /* + * Keep on reading until we get the terminating '\0', + * discarding any bytes we don't have room for. + */ + + while ((c = pq_getbyte()) != EOF && c != '\0') + { + if (len > 1) + { + *s++ = c; + len--; + } + } + + *s = '\0'; + + if (c == EOF) + return EOF; + + return 0; +} + + +/* -------------------------------- + * pq_putbytes - send bytes to connection (not flushed until pq_flush) + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +pq_putbytes(const char *s, size_t len) +{ + size_t amount; + + while (len > 0) + { + if (PqSendPointer >= PQ_BUFFER_SIZE) + if (pq_flush()) /* If buffer is full, then flush it out */ + return EOF; + amount = PQ_BUFFER_SIZE - PqSendPointer; + if (amount > len) + amount = len; + memcpy(PqSendBuffer + PqSendPointer, s, amount); + PqSendPointer += amount; + s += amount; + len -= amount; + } + return 0; +} + +/* -------------------------------- + * pq_flush - flush pending output + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +pq_flush(void) +{ + unsigned char *bufptr = PqSendBuffer; + unsigned char *bufend = PqSendBuffer + PqSendPointer; + + while (bufptr < bufend) + { + int r = send(MyProcPort->sock, bufptr, bufend - bufptr, 0); + if (r <= 0) + { + if (errno == EINTR) + continue; /* Ok if we were interrupted */ + /* We would like to use elog() here, but cannot because elog + * tries to write to the client, which would cause a recursive + * flush attempt! So just write it out to the postmaster log. + */ + fprintf(stderr, "pq_flush: send() failed, errno %d\n", errno); + /* We drop the buffered data anyway so that processing + * can continue, even though we'll probably quit soon. + */ + PqSendPointer = 0; + return EOF; + } + bufptr += r; + } + PqSendPointer = 0; + return 0; +} + + +/* -------------------------------- + * Message-level I/O routines begin here. + * + * These routines understand about COPY OUT protocol. + * -------------------------------- + */ + + +/* -------------------------------- + * 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 + * the message body (len counts only the body size!). + * If msgtype is '\0', then the buffer already includes the type code. + * + * All normal messages are suppressed while COPY OUT is in progress. + * (In practice only 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 + * -------------------------------- + */ +int +pq_putmessage(char msgtype, const char *s, size_t len) +{ + if (DoingCopyOut) + return 0; + if (msgtype) + if (pq_putbytes(&msgtype, 1)) + return EOF; + return pq_putbytes(s, len); +} + +/* -------------------------------- + * pq_startcopyout - inform libpq that a COPY OUT transfer is beginning + * -------------------------------- + */ +void +pq_startcopyout(void) +{ + DoingCopyOut = true; +} + +/* -------------------------------- + * pq_endcopyout - end a COPY OUT transfer + * + * 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 + * been emitted, send a couple of newlines first (the first one could + * get absorbed by a backslash...) + * -------------------------------- + */ +void +pq_endcopyout(bool errorAbort) +{ + if (! DoingCopyOut) + return; + if (errorAbort) + pq_putbytes("\n\n\\.\n", 5); + /* in non-error case, copy.c will have emitted the terminator line */ + DoingCopyOut = false; } diff --git a/src/backend/libpq/pqcomprim.c b/src/backend/libpq/pqcomprim.c deleted file mode 100644 index e48a1c1688..0000000000 --- a/src/backend/libpq/pqcomprim.c +++ /dev/null @@ -1,220 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "postgres.h" -#include "miscadmin.h" -#include "libpq/pqcomm.h" -#include "libpq/libpq.h" - - -/* - * The backend supports the old little endian byte order and the current - * network byte order. - */ - -#ifndef FRONTEND - -#include "libpq/libpq-be.h" - -#ifdef HAVE_ENDIAN_H -#include -#endif - -#ifndef BYTE_ORDER -#error BYTE_ORDER must be defined as LITTLE_ENDIAN, BIG_ENDIAN or PDP_ENDIAN -#endif - -#if BYTE_ORDER == LITTLE_ENDIAN - -#define ntoh_s(n) n -#define ntoh_l(n) n -#define hton_s(n) n -#define hton_l(n) n - -#else -#if BYTE_ORDER == BIG_ENDIAN - -/* -#define ntoh_s(n) (uint16)(((u_char *)&n)[1] << 8 \ - | ((u_char *)&n)[0]) -#define ntoh_l(n) (uint32)(((u_char *)&n)[3] << 24 \ - | ((u_char *)&n)[2] << 16 \ - | ((u_char *)&n)[1] << 8 \ - | ((u_char *)&n)[0]) -*/ -#define ntoh_s(n) (uint16)((((uint16)n & 0x00ff) << 8) | \ - (((uint16)n & 0xff00) >> 8)) -#define ntoh_l(n) (uint32)((((uint32)n & 0x000000ff) << 24) | \ - (((uint32)n & 0x0000ff00) << 8) | \ - (((uint32)n & 0x00ff0000) >> 8) | \ - (((uint32)n & 0xff000000) >> 24)) -#define hton_s(n) (ntoh_s(n)) -#define hton_l(n) (ntoh_l(n)) - -#else -#if BYTE_ORDER == PDP_ENDIAN - -#error PDP_ENDIAN macros not written yet - -#else - -#error BYTE_ORDER not defined as anything understood - -#endif -#endif -#endif - -#endif - - -/* --------------------------------------------------------------------- */ -int -pqPutShort(int integer) -{ - uint16 n; - -#ifdef FRONTEND - n = htons((uint16) integer); -#else - n = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_s(integer) : htons((uint16) integer)); -#endif - - return pqPutNBytes((char *)&n, 2); /* 0 on success, EOF otherwise */ -} - -/* --------------------------------------------------------------------- */ -int -pqPutLong(int integer) -{ - uint32 n; - -#ifdef FRONTEND - n = htonl((uint32) integer); -#else - n = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_l(integer) : htonl((uint32) integer)); -#endif - - return pqPutNBytes((char *)&n, 4); -} - -/* --------------------------------------------------------------------- */ -int -pqGetShort(int *result) -{ - uint16 n; - - if (pqGetNBytes((char *)&n, 2) != 0) - return EOF; - -#ifdef FRONTEND - *result = (int) ntohs(n); -#else - *result = (int) ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? ntoh_s(n) : ntohs(n)); -#endif - - return 0; -} - -/* --------------------------------------------------------------------- */ -int -pqGetLong(int *result) -{ - uint32 n; - - if (pqGetNBytes((char *)&n, 4) != 0) - return EOF; - -#ifdef FRONTEND - *result = (int) ntohl(n); -#else - *result = (int) ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? ntoh_l(n) : ntohl(n)); -#endif - - return 0; -} - -/* --------------------------------------------------------------------- */ -/* pqGetNBytes: Read a chunk of exactly len bytes into buffer s. - * Return 0 if ok, EOF if trouble. - */ -int -pqGetNBytes(char *s, size_t len) -{ - size_t amount; - - while (len > 0) - { - while (PqRecvPointer >= PqRecvLength) - { - if (pq_recvbuf()) /* If nothing in buffer, then recv some */ - return EOF; /* Failed to recv data */ - } - amount = PqRecvLength - PqRecvPointer; - if (amount > len) - amount = len; - memcpy(s, PqRecvBuffer + PqRecvPointer, amount); - PqRecvPointer += amount; - s += amount; - len -= amount; - } - return 0; -} - -/* --------------------------------------------------------------------- */ -int -pqPutNBytes(const char *s, size_t len) -{ - size_t amount; - - while (len > 0) - { - if (PqSendPointer >= PQ_BUFFER_SIZE) - if (pq_flush()) /* If buffer is full, then flush it out */ - return EOF; - amount = PQ_BUFFER_SIZE - PqSendPointer; - if (amount > len) - amount = len; - memcpy(PqSendBuffer + PqSendPointer, s, amount); - PqSendPointer += amount; - s += amount; - len -= amount; - } - return 0; -} - -/* --------------------------------------------------------------------- */ -int -pqGetString(char *s, size_t len) -{ - int c; - - /* - * Keep on reading until we get the terminating '\0', - * discarding any bytes we don't have room for. - */ - - while ((c = pq_getchar()) != EOF && c != '\0') - if (len > 1) - { - *s++ = c; - len--; - } - - *s = '\0'; - - if (c == EOF) - return EOF; - - return 0; -} - -/* --------------------------------------------------------------------- */ -int -pqPutString(const char *s) -{ - return pqPutNBytes(s,strlen(s)+1); -} diff --git a/src/backend/libpq/pqformat.c b/src/backend/libpq/pqformat.c new file mode 100644 index 0000000000..06f55d648b --- /dev/null +++ b/src/backend/libpq/pqformat.c @@ -0,0 +1,293 @@ +/*------------------------------------------------------------------------- + * + * pqformat.c + * Routines for formatting and parsing frontend/backend messages + * + * Outgoing messages are built up in a StringInfo buffer (which is expansible) + * and then sent in a single call to pq_putmessage. This module provides data + * formatting/conversion routines that are needed to produce valid messages. + * Note in particular the distinction between "raw data" and "text"; raw data + * is message protocol characters and binary values that are not subject to + * MULTIBYTE conversion, while text is converted by MULTIBYTE rules. + * + * Incoming messages are read directly off the wire, as it were, but there + * are still data-conversion tasks to be performed. + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pqformat.c,v 1.1 1999/04/25 03:19:22 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * Message output: + * pq_beginmessage - initialize StringInfo buffer + * pq_sendbyte - append a raw byte to a StringInfo buffer + * pq_sendint - append a binary integer to a StringInfo buffer + * pq_sendbytes - append raw data to a StringInfo buffer + * pq_sendtext - append a text string (with MULTIBYTE conversion) + * pq_sendstring - append a null-terminated text string (with MULTIBYTE) + * pq_endmessage - send the completed message to the frontend + * Note: it is also possible to append data to the StringInfo buffer using + * the regular StringInfo routines, but this is discouraged since required + * MULTIBYTE conversion may not occur. + * + * Message input: + * pq_getint - get an integer from connection + * pq_getstr - get a null terminated string from connection + * pq_getnchar - get n characters from connection, and null-terminate + * pq_getstr and pq_getnchar perform MULTIBYTE conversion on the collected + * string. Use the raw pqcomm.c routines pq_getstring and pq_getbytes + * to fetch data without conversion. + */ +#include "postgres.h" + +#include "libpq/pqformat.h" +#include "libpq/libpq.h" +#ifdef MULTIBYTE +#include "mb/pg_wchar.h" +#endif +#ifdef HAVE_ENDIAN_H +#include +#endif + +#ifndef BYTE_ORDER +#error BYTE_ORDER must be defined as LITTLE_ENDIAN, BIG_ENDIAN or PDP_ENDIAN +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN + +#define ntoh_s(n) n +#define ntoh_l(n) n +#define hton_s(n) n +#define hton_l(n) n + +#else +#if BYTE_ORDER == BIG_ENDIAN + +#define ntoh_s(n) (uint16)((((uint16)n & 0x00ff) << 8) | \ + (((uint16)n & 0xff00) >> 8)) +#define ntoh_l(n) (uint32)((((uint32)n & 0x000000ff) << 24) | \ + (((uint32)n & 0x0000ff00) << 8) | \ + (((uint32)n & 0x00ff0000) >> 8) | \ + (((uint32)n & 0xff000000) >> 24)) +#define hton_s(n) (ntoh_s(n)) +#define hton_l(n) (ntoh_l(n)) + +#else +#if BYTE_ORDER == PDP_ENDIAN + +#error PDP_ENDIAN macros not written yet + +#else + +#error BYTE_ORDER not defined as anything understood + +#endif +#endif +#endif + + +/* -------------------------------- + * pq_sendbyte - append a raw byte to a StringInfo buffer + * -------------------------------- + */ +void +pq_sendbyte(StringInfo buf, int byt) +{ + appendStringInfoChar(buf, byt); +} + +/* -------------------------------- + * pq_sendbytes - append raw data to a StringInfo buffer + * -------------------------------- + */ +void +pq_sendbytes(StringInfo buf, const char *data, int datalen) +{ + appendBinaryStringInfo(buf, data, datalen); +} + +/* -------------------------------- + * pq_sendtext - append a text string (with MULTIBYTE conversion) + * + * NB: passed text string must be null-terminated, even though we expect + * the caller to hand us the length (this is just because the caller + * usually knows the length anyway). In this routine, the data sent to + * the frontend is NOT null-terminated. + * -------------------------------- + */ +void +pq_sendtext(StringInfo buf, const char *str, int slen) +{ +#ifdef MULTIBYTE + str = (const char *) pg_server_to_client(str, slen); +#endif + appendBinaryStringInfo(buf, str, slen); +} + +/* -------------------------------- + * pq_sendstring - append a null-terminated text string (with MULTIBYTE) + * + * NB: passed text string must be null-terminated, even though we expect + * the caller to hand us the length (this is just because the caller + * usually knows the length anyway). + * -------------------------------- + */ +void +pq_sendstring(StringInfo buf, const char *str, int slen) +{ +#ifdef MULTIBYTE + str = (const char *) pg_server_to_client(str, slen); +#endif + appendBinaryStringInfo(buf, str, slen+1); +} + +/* -------------------------------- + * pq_sendint - append a binary integer to a StringInfo buffer + * -------------------------------- + */ +void +pq_sendint(StringInfo buf, int i, int b) +{ + unsigned char n8; + uint16 n16; + uint32 n32; + + switch (b) + { + case 1: + n8 = (unsigned char) i; + appendBinaryStringInfo(buf, (char *) &n8, 1); + break; + case 2: + n16 = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_s(i) : htons((uint16) i)); + appendBinaryStringInfo(buf, (char *) &n16, 2); + break; + case 4: + n32 = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_l(i) : htonl((uint32) i)); + appendBinaryStringInfo(buf, (char *) &n32, 4); + break; + default: + elog(ERROR, "pq_sendint: unsupported size %d", b); + break; + } +} + +/* -------------------------------- + * pq_endmessage - send the completed message to the frontend + * + * The data buffer is pfree()d, but if the StringInfo was allocated with + * makeStringInfo then the caller must still pfree it. + * -------------------------------- + */ +void +pq_endmessage(StringInfo buf) +{ + if (pq_putmessage('\0', buf->data, buf->len)) + { + snprintf(PQerrormsg, ERROR_MSG_LENGTH, + "FATAL: pq_endmessage failed: errno=%d\n", errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + pfree(buf->data); + buf->data = NULL; +} + +/* -------------------------------- + * pq_getint - get an integer from connection + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +pq_getint(int *result, int b) +{ + int status; + unsigned char n8; + uint16 n16; + uint32 n32; + + switch (b) + { + case 1: + status = pq_getbytes((char *) &n8, 1); + *result = (int) n8; + break; + case 2: + status = pq_getbytes((char *) &n16, 2); + *result = (int) ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? + ntoh_s(n16) : ntohs(n16)); + break; + case 4: + status = pq_getbytes((char *) &n32, 4); + *result = (int) ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? + ntoh_l(n32) : ntohl(n32)); + break; + default: + /* if we elog(ERROR) here, we will lose sync with the frontend, + * so just complain to postmaster log instead... + */ + fprintf(stderr, "pq_getint: unsupported size %d\n", b); + status = EOF; + *result = 0; + break; + } + return status; +} + +/* -------------------------------- + * pq_getstr - get a null terminated string from connection + * + * FIXME: we ought to use an expansible StringInfo buffer, + * rather than dropping data if the message is too long. + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +pq_getstr(char *s, int maxlen) +{ + int c; +#ifdef MULTIBYTE + char *p; +#endif + + c = pq_getstring(s, maxlen); + +#ifdef MULTIBYTE + p = (char*) pg_client_to_server((unsigned char *) s, maxlen); + if (s != p) /* actual conversion has been done? */ + strcpy(s, p); +#endif + + return c; +} + +/* -------------------------------- + * pq_getnchar - get n characters from connection, and null-terminate + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +pq_getnchar(char *s, int len) +{ + int c; +#ifdef MULTIBYTE + char *p; +#endif + + c = pq_getbytes(s, len); + s[len] = '\0'; + +#ifdef MULTIBYTE + p = (char*) pg_client_to_server((unsigned char *) s, len+1); + if (s != p) /* actual conversion has been done? */ + strcpy(s, p); +#endif + + return c; +} diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index e4816e2e25..3de9b7bda1 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: outfuncs.c,v 1.78 1999/03/23 16:50:53 momjian Exp $ + * $Id: outfuncs.c,v 1.79 1999/04/25 03:19:15 tgl Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -1658,21 +1658,15 @@ _outNode(StringInfo str, void *obj) /* * nodeToString - - * returns the ascii representation of the Node + * returns the ascii representation of the Node as a palloc'd string */ char * nodeToString(void *obj) { - StringInfo str; - char *s; + StringInfoData str; - if (obj == NULL) - return ""; - Assert(obj != NULL); - str = makeStringInfo(); - _outNode(str, obj); - s = str->data; - pfree(str); - - return s; + /* see stringinfo.h for an explanation of this maneuver */ + initStringInfo(&str); + _outNode(&str, obj); + return str.data; } diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c index 9d8232e3af..d6f29aa8af 100644 --- a/src/backend/tcop/dest.c +++ b/src/backend/tcop/dest.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.25 1999/02/13 23:18:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.26 1999/04/25 03:19:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -39,6 +39,7 @@ #include "access/htup.h" #include "libpq/libpq.h" +#include "libpq/pqformat.h" #include "access/printtup.h" #include "utils/portal.h" #include "utils/palloc.h" @@ -137,8 +138,7 @@ BeginCommand(char *pname, * send fe info on tuples we're about to send * ---------------- */ - pq_putnchar("P", 1);/* new portal.. */ - pq_putstr(pname); /* portal name */ + pq_putmessage('P', pname, strlen(pname)+1); /* ---------------- * if this is a retrieve, then we send back the tuple @@ -148,17 +148,24 @@ BeginCommand(char *pname, */ if (operation == CMD_SELECT && !isIntoRel) { - pq_putnchar("T", 1); /* type info to follow.. */ - pq_putint(natts, 2); /* number of attributes in tuples */ + StringInfoData buf; + pq_beginmessage(&buf); + pq_sendbyte(&buf, 'T'); /* tuple descriptor message type */ + pq_sendint(&buf, natts, 2); /* # of attributes in tuples */ for (i = 0; i < natts; ++i) { - pq_putstr(attrs[i]->attname.data); - pq_putint((int) attrs[i]->atttypid, sizeof(attrs[i]->atttypid)); - pq_putint(attrs[i]->attlen, sizeof(attrs[i]->attlen)); + pq_sendstring(&buf, attrs[i]->attname.data, + strlen(attrs[i]->attname.data)); + pq_sendint(&buf, (int) attrs[i]->atttypid, + sizeof(attrs[i]->atttypid)); + pq_sendint(&buf, attrs[i]->attlen, + sizeof(attrs[i]->attlen)); if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) - pq_putint(attrs[i]->atttypmod, sizeof(attrs[i]->atttypmod)); + pq_sendint(&buf, attrs[i]->atttypmod, + sizeof(attrs[i]->atttypmod)); } + pq_endmessage(&buf); } break; @@ -265,8 +272,8 @@ EndCommand(char *commandTag, CommandDest dest) * tell the fe that the query is over * ---------------- */ - sprintf(buf, "C%s%s", commandTag, CommandInfo); - pq_putstr(buf); + sprintf(buf, "%s%s", commandTag, CommandInfo); + pq_putmessage('C', buf, strlen(buf)+1); CommandInfo[0] = '\0'; break; @@ -293,18 +300,18 @@ void SendCopyBegin(void) { if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) - pq_putnchar("H", 1); /* new way */ + pq_putbytes("H", 1); /* new way */ else - pq_putnchar("B", 1); /* old way */ + pq_putbytes("B", 1); /* old way */ } void ReceiveCopyBegin(void) { if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) - pq_putnchar("G", 1); /* new way */ + pq_putbytes("G", 1); /* new way */ else - pq_putnchar("D", 1); /* old way */ + pq_putbytes("D", 1); /* old way */ /* We *must* flush here to ensure FE knows it can send. */ pq_flush(); } @@ -330,7 +337,7 @@ NullCommand(CommandDest dest) * tell the fe that we saw an empty query string * ---------------- */ - pq_putstr("I"); + pq_putbytes("I", 1); break; case Local: @@ -359,7 +366,7 @@ ReadyForQuery(CommandDest dest) case RemoteInternal: case Remote: if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) - pq_putnchar("Z", 1); + pq_putbytes("Z", 1); /* Flush output at end of cycle in any case. */ pq_flush(); break; diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c index f9ea00df12..5dd71b2b9e 100644 --- a/src/backend/tcop/fastpath.c +++ b/src/backend/tcop/fastpath.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.22 1999/02/13 23:18:44 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.23 1999/04/25 03:19:10 tgl Exp $ * * NOTES * This cruft is the server side of PQfn. @@ -68,6 +68,7 @@ #include "utils/builtins.h" /* for oideq */ #include "tcop/fastpath.h" #include "libpq/libpq.h" +#include "libpq/pqformat.h" #include "access/xact.h" /* for TransactionId/CommandId protos */ @@ -87,32 +88,36 @@ SendFunctionResult(Oid fid, /* function id */ int retlen /* the length according to the catalogs */ ) { - pq_putnchar("V", 1); + StringInfoData buf; + + pq_beginmessage(&buf); + pq_sendbyte(&buf, 'V'); if (retlen != 0) { - pq_putnchar("G", 1); + pq_sendbyte(&buf, 'G'); if (retbyval) { /* by-value */ - pq_putint(retlen, 4); - pq_putint((int) (Datum) retval, retlen); + pq_sendint(&buf, retlen, 4); + pq_sendint(&buf, (int) (Datum) retval, retlen); } else { /* by-reference ... */ if (retlen < 0) { /* ... varlena */ - pq_putint(VARSIZE(retval) - VARHDRSZ, VARHDRSZ); - pq_putnchar(VARDATA(retval), VARSIZE(retval) - VARHDRSZ); + pq_sendint(&buf, VARSIZE(retval) - VARHDRSZ, VARHDRSZ); + pq_sendbytes(&buf, VARDATA(retval), VARSIZE(retval) - VARHDRSZ); } else { /* ... fixed */ - pq_putint(retlen, 4); - pq_putnchar(retval, retlen); + pq_sendint(&buf, retlen, 4); + pq_sendbytes(&buf, retval, retlen); } } } - pq_putnchar("0", 1); + pq_sendbyte(&buf, '0'); + pq_endmessage(&buf); } /* @@ -276,6 +281,7 @@ HandleFunctionRequest() Oid fid; int argsize; int nargs; + int tmp; char *arg[8]; char *retval; int i; @@ -283,8 +289,9 @@ HandleFunctionRequest() char *p; struct fp_info *fip; - fid = (Oid) pq_getint(4); /* function oid */ - nargs = pq_getint(4); /* # of arguments */ + pq_getint(&tmp, 4); /* function oid */ + fid = (Oid) tmp; + pq_getint(&nargs, 4); /* # of arguments */ /* * This is where the one-back caching is done. If you want to save @@ -311,13 +318,14 @@ HandleFunctionRequest() arg[i] = (char *) NULL; else { - argsize = pq_getint(4); + pq_getint(&argsize, 4); Assert(argsize > 0); if (fip->argbyval[i]) { /* by-value */ Assert(argsize <= 4); - arg[i] = (char *) pq_getint(argsize); + pq_getint(&tmp, argsize); + arg[i] = (char *) tmp; } else { /* by-reference ... */ @@ -328,14 +336,14 @@ HandleFunctionRequest() * 98 Jan 6 */ elog(ERROR, "HandleFunctionRequest: palloc failed"); VARSIZE(p) = argsize + VARHDRSZ; - pq_getnchar(VARDATA(p), 0, argsize); + pq_getbytes(VARDATA(p), argsize); } else { /* ... fixed */ /* XXX cross our fingers and trust "argsize" */ if (!(p = palloc(argsize + 1))) elog(ERROR, "HandleFunctionRequest: palloc failed"); - pq_getnchar(p, 0, argsize); + pq_getbytes(p, argsize); } palloced |= (1 << i); arg[i] = p; diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 231b88d620..5e82de3eda 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.107 1999/04/20 02:19:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.108 1999/04/25 03:19:10 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -52,6 +52,7 @@ #include "executor/execdebug.h" #include "executor/executor.h" #include "libpq/libpq.h" +#include "libpq/pqformat.h" #include "libpq/libpq-be.h" #include "libpq/pqsignal.h" #include "nodes/pg_list.h" @@ -304,15 +305,15 @@ InteractiveBackend(char *inBuf) static char SocketBackend(char *inBuf) { - char qtype[2]; + char qtype; char result = '\0'; /* ---------------- * get input from the frontend * ---------------- */ - strcpy(qtype, "?"); - if (pq_getnchar(qtype, 0, 1) == EOF) + qtype = '?'; + if (pq_getbytes(&qtype, 1) == EOF) { /* ------------ * when front-end applications quits/dies @@ -321,7 +322,7 @@ SocketBackend(char *inBuf) proc_exit(0); } - switch (*qtype) + switch (qtype) { /* ---------------- * 'Q': user entered a query @@ -358,7 +359,7 @@ SocketBackend(char *inBuf) * ---------------- */ default: - elog(FATAL, "Socket command type %c unknown\n", *qtype); + elog(FATAL, "Socket command type %c unknown", qtype); break; } return result; @@ -1461,7 +1462,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) Portfd = open(NULL_DEV, O_RDWR | O_BINARY, 0666); #endif } - pq_init(Portfd); + pq_init(); /* reset libpq */ whereToSendOutput = Remote; } else @@ -1521,16 +1522,19 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) if (whereToSendOutput == Remote && PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) { - pq_putnchar("K", 1); - pq_putint((int32) MyProcPid, sizeof(int32)); - pq_putint((int32) MyCancelKey, sizeof(int32)); + StringInfoData buf; + pq_beginmessage(&buf); + pq_sendbyte(&buf, 'K'); + pq_sendint(&buf, (int32) MyProcPid, sizeof(int32)); + pq_sendint(&buf, (int32) MyCancelKey, sizeof(int32)); + pq_endmessage(&buf); /* Need not flush since ReadyForQuery will do it. */ } if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.107 $ $Date: 1999/04/20 02:19:53 $\n"); + puts("$Revision: 1.108 $ $Date: 1999/04/25 03:19:10 $\n"); } /* ---------------- diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index e1fa74634a..096cd09344 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.41 1999/04/20 02:19:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.42 1999/04/25 03:19:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -188,12 +188,21 @@ elog(int lev, const char *fmt,...) /* Send IPC message to the front-end program */ if (IsUnderPostmaster && lev > DEBUG) { - /* notices are not exactly errors, handle it differently */ + /* notices are not errors, handle 'em differently */ + char msgtype; if (lev == NOTICE) - pq_putnchar("N", 1); + msgtype = 'N'; else - pq_putnchar("E", 1); - pq_putstr(line + TIMESTAMP_SIZE); /* don't show timestamps */ + { + /* Abort any COPY OUT in progress when an error is detected. + * This hack is necessary because of poor design of copy protocol. + */ + pq_endcopyout(true); + msgtype = 'E'; + } + /* exclude the timestamp from msg sent to frontend */ + pq_putmessage(msgtype, line + TIMESTAMP_SIZE, + strlen(line + TIMESTAMP_SIZE) + 1); /* * This flush is normally not necessary, since postgres.c will * flush out waiting data when control returns to the main loop. diff --git a/src/include/lib/stringinfo.h b/src/include/lib/stringinfo.h index cf766521ec..6b86fd6552 100644 --- a/src/include/lib/stringinfo.h +++ b/src/include/lib/stringinfo.h @@ -1,52 +1,108 @@ /*------------------------------------------------------------------------- * * stringinfo.h - * Declarations/definitons for "string" functions. + * Declarations/definitions for "StringInfo" functions. * + * StringInfo provides an indefinitely-extensible string data type. + * It can be used to buffer either ordinary C strings (null-terminated text) + * or arbitrary binary data. All storage is allocated with palloc(). * * Copyright (c) 1994, Regents of the University of California * - * $Id: stringinfo.h,v 1.10 1999/02/13 23:21:32 momjian Exp $ + * $Id: stringinfo.h,v 1.11 1999/04/25 03:19:27 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef STRINGINFO_H #define STRINGINFO_H - /*------------------------- - * StringInfoData holds information about a string. - * 'data' is the string. - * 'len' is the current string length (as returned by 'strlen') - * 'maxlen' is the size in bytes of 'data', i.e. the maximum string - * size (including the terminating '\0' char) that we can + * StringInfoData holds information about an extensible string. + * data is the current buffer for the string (allocated with palloc). + * len is the current string length. There is guaranteed to be + * a terminating '\0' at data[len], although this is not very + * useful when the string holds binary data rather than text. + * maxlen is the allocated size in bytes of 'data', i.e. the maximum + * string size (including the terminating '\0' char) that we can * currently store in 'data' without having to reallocate - * more space. + * more space. We must always have maxlen > len. + *------------------------- */ typedef struct StringInfoData { char *data; - int maxlen; int len; + int maxlen; } StringInfoData; typedef StringInfoData *StringInfo; + +/*------------------------ + * There are two ways to create a StringInfo object initially: + * + * StringInfo stringptr = makeStringInfo(); + * Both the StringInfoData and the data buffer are palloc'd. + * + * StringInfoData string; + * initStringInfo(&string); + * The data buffer is palloc'd but the StringInfoData is just local. + * This is the easiest approach for a StringInfo object that will + * only live as long as the current routine. + * + * To destroy a StringInfo, pfree() the data buffer, and then pfree() the + * StringInfoData if it was palloc'd. There's no special support for this. + * + * NOTE: some routines build up a string using StringInfo, and then + * release the StringInfoData but return the data string itself to their + * caller. At that point the data string looks like a plain palloc'd + * string. + *------------------------- + */ + /*------------------------ * makeStringInfo - * create a 'StringInfoData' & return a pointer to it. + * Create an empty 'StringInfoData' & return a pointer to it. */ extern StringInfo makeStringInfo(void); /*------------------------ - * appendStringInfo - * similar to 'strcat' but reallocates more space if necessary... + * initStringInfo + * Initialize a StringInfoData struct (with previously undefined contents) + * to describe an empty string. */ -extern void appendStringInfo(StringInfo str, const char *fmt,...); +extern void initStringInfo(StringInfo str); /*------------------------ - * nullStringInfo - * return the string itself or "<>" if it is NULL + * appendStringInfo + * Format text data under the control of fmt (an sprintf-like format string) + * and append it to whatever is already in str. More space is allocated + * to str if necessary. This is sort of like a combination of sprintf and + * strcat. + * CAUTION: the current implementation has a 1K limit on the amount of text + * generated in a single call (not on the total string length). + */ +extern void appendStringInfo(StringInfo str, const char *fmt, ...); + +/*------------------------ + * appendStringInfoChar + * Append a single byte to str. + * Like appendStringInfo(str, "%c", ch) but much faster. + */ +extern void appendStringInfoChar(StringInfo str, char ch); + +/*------------------------ + * appendBinaryStringInfo + * Append arbitrary binary data to a StringInfo, allocating more space + * if necessary. + */ +extern void appendBinaryStringInfo(StringInfo str, + const char *data, int datalen); + +/*------------------------ + * stringStringInfo + * Return the string itself or "<>" if it is NULL. + * This is just a convenience macro used by many callers of appendStringInfo. */ #define stringStringInfo(s) (((s) == NULL) ? "<>" : (s)) diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h index 99d408d9c1..873992739e 100644 --- a/src/include/libpq/libpq.h +++ b/src/include/libpq/libpq.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: libpq.h,v 1.27 1999/02/13 23:21:35 momjian Exp $ + * $Id: libpq.h,v 1.28 1999/04/25 03:19:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -120,17 +120,6 @@ typedef struct PortalEntry extern PortalEntry **portals; extern size_t portals_array_size; -/* - * Asynchronous notification - */ -typedef struct PQNotifyList -{ - char relname[NAMEDATALEN]; /* listen/notify name */ - int be_pid; /* process id of backend */ - int valid; /* has this already been handled by user. */ -/* SLNode Node; */ -} PQNotifyList; - /* * Exceptions. */ @@ -194,11 +183,6 @@ extern char *PQgetvalue(PortalBuffer *portal, int tuple_index, int field_number) extern char *PQgetAttr(PortalBuffer *portal, int tuple_index, int field_number); extern int PQgetlength(PortalBuffer *portal, int tuple_index, int field_number); extern void PQclear(char *pname); -extern void PQcleanNotify(void); -extern void PQnotifies_init(void); -extern PQNotifyList *PQnotifies(void); -extern void PQremoveNotify(PQNotifyList *nPtr); -extern void PQappendNotify(char *relname, int pid); /* * prototypes for functions in portalbuf.c @@ -250,51 +234,19 @@ extern int32 pqtest(struct varlena * vlena); /* * prototypes for functions in pqcomm.c */ -extern void pq_init(int fd); -extern void pq_gettty(char *tp); -extern int pq_getport(void); -extern void pq_close(void); -extern int pq_flush(void); -extern int pq_recvbuf(void); -extern int pq_getstr(char *s, int maxlen); -extern int PQgetline(char *s, int maxlen); -extern int PQputline(char *s); -extern int pq_getchar(void); -extern int pq_peekchar(void); -extern int pq_getnchar(char *s, int off, int maxlen); -extern int pq_getint(int b); -extern int pq_putchar(unsigned char c); -extern void pq_putstr(char *s); -extern void pq_putnchar(char *s, int n); -extern void pq_putint(int i, int b); -extern int pq_getinaddr(struct sockaddr_in * sin, char *host, int port); -extern int pq_getinserv(struct sockaddr_in * sin, char *host, char *serv); - -#ifdef MULTIBYTE -extern void pq_putncharlen(char *s, int n); - -#endif - -extern int pq_connect(char *dbname, char *user, char *args, char *hostName, - char *debugTty, char *execFile, short portName); -extern int StreamOpen(char *hostName, short portName, Port *port); -extern void StreamDoUnlink(void); extern int StreamServerPort(char *hostName, short portName, int *fdP); extern int StreamConnection(int server_fd, Port *port); extern void StreamClose(int sock); - -/* - * Internal send/receive buffers in libpq. - * These probably shouldn't be global at all, but unless we merge - * pqcomm.c and pqcomprim.c they have to be... - */ - -#define PQ_BUFFER_SIZE 8192 - -extern unsigned char PqSendBuffer[PQ_BUFFER_SIZE]; -extern int PqSendPointer; /* Next index to store a byte in PqSendBuffer */ -extern unsigned char PqRecvBuffer[PQ_BUFFER_SIZE]; -extern int PqRecvPointer; /* Next index to read a byte from PqRecvBuffer */ -extern int PqRecvLength; /* End of data available in PqRecvBuffer */ +extern void pq_init(void); +extern int pq_getport(void); +extern void pq_close(void); +extern int pq_getbytes(char *s, size_t len); +extern int pq_getstring(char *s, size_t len); +extern int pq_peekbyte(void); +extern int pq_putbytes(const char *s, size_t len); +extern int pq_flush(void); +extern int pq_putmessage(char msgtype, const char *s, size_t len); +extern void pq_startcopyout(void); +extern void pq_endcopyout(bool errorAbort); #endif /* LIBPQ_H */ diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h index 2245b83d62..53542993d8 100644 --- a/src/include/libpq/pqcomm.h +++ b/src/include/libpq/pqcomm.h @@ -3,16 +3,20 @@ * pqcomm.h * Definitions common to frontends and backends. * + * NOTE: for historical reasons, this does not correspond to pqcomm.c. + * pqcomm.c's routines are declared in libpq.h. * * Copyright (c) 1994, Regents of the University of California * - * $Id: pqcomm.h,v 1.33 1999/02/13 23:21:36 momjian Exp $ + * $Id: pqcomm.h,v 1.34 1999/04/25 03:19:13 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef PQCOMM_H #define PQCOMM_H +#include "postgres.h" + #include #include #ifdef WIN32 @@ -23,8 +27,6 @@ #include #endif -#include "postgres.h" - /* Define a generic socket address type. */ typedef union SockAddr @@ -151,18 +153,4 @@ typedef struct CancelRequestPacket uint32 cancelAuthCode; /* secret key to authorize cancel */ } CancelRequestPacket; - -/* in pqcomprim.c */ -int pqGetShort(int *); -int pqGetLong(int *); -int pqGetNBytes(char *, size_t); -int pqGetString(char *, size_t); -int pqGetByte(void); - -int pqPutShort(int); -int pqPutLong(int); -int pqPutNBytes(const char *, size_t); -int pqPutString(const char *); -int pqPutByte(int); - #endif /* PQCOMM_H */ diff --git a/src/include/libpq/pqformat.h b/src/include/libpq/pqformat.h new file mode 100644 index 0000000000..2fe48539a2 --- /dev/null +++ b/src/include/libpq/pqformat.h @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------- + * + * pqformat.h + * Definitions for formatting and parsing frontend/backend messages + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pqformat.h,v 1.1 1999/04/25 03:19:14 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PQFORMAT_H +#define PQFORMAT_H + +#include "postgres.h" +#include "lib/stringinfo.h" + +#define pq_beginmessage(buf) initStringInfo(buf) + +extern void pq_sendbyte(StringInfo buf, int byt); +extern void pq_sendbytes(StringInfo buf, const char *data, int datalen); +extern void pq_sendtext(StringInfo buf, const char *str, int slen); +extern void pq_sendstring(StringInfo buf, const char *str, int slen); +extern void pq_sendint(StringInfo buf, int i, int b); +extern void pq_endmessage(StringInfo buf); + +extern int pq_getint(int *result, int b); +extern int pq_getstr(char *s, int maxlen); +extern int pq_getnchar(char *s, int len); + +#endif /* PQFORMAT_H */