/* ---------------- * NullCommand - tell dest that an empty query string was recognized * * In FE/BE protocol version 1.0, this hack is necessary to support * libpq's crufty way of determining whether a multiple-command * query string is done. In protocol 2.0 it's probably not really * necessary to distinguish empty queries anymore, but we still do it * 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 NullCommand(CommandDest dest) { switch (dest) { case DestRemote: case DestRemoteExecute: /* * tell the fe that we saw an empty query string. In protocols * before 3.0 this has a useless empty-string message body. */ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) pq_putemptymessage('I'); else pq_putmessage('I', "", 1); break; case DestNone: case DestDebug: case DestSPI: case DestTuplestore: case DestIntoRel: case DestCopyOut: case DestSQLFunction: case DestTupleTable: case DestTransientRel: case DestCombiner: case DestStream: break; } }
/* ---------------- * EndCommand - clean up the destination at end of command * ---------------- */ void EndCommand(const char *commandTag, CommandDest dest) { switch (dest) { case DestRemote: case DestRemoteExecute: /* * We assume the commandTag is plain ASCII and therefore requires * no encoding conversion. */ pq_putmessage('C', commandTag, strlen(commandTag) + 1); break; case DestNone: case DestDebug: case DestSPI: case DestTuplestore: case DestIntoRel: case DestCopyOut: case DestSQLFunction: case DestTransientRel: case DestTupleTable: case DestCombiner: case DestStream: break; } }
/* -------------------------------- * pq_putmessage_noblock - like pq_putmessage, but never blocks * * If the output buffer is too small to hold the message, the buffer * is enlarged. */ void pq_putmessage_noblock(char msgtype, const char *s, size_t len) { int res; int required; /* * Ensure we have enough space in the output buffer for the message header * as well as the message itself. */ required = PqSendPointer + 1 + 4 + len; if (required > PqSendBufferSize) { PqSendBuffer = repalloc(PqSendBuffer, required); PqSendBufferSize = required; } res = pq_putmessage(msgtype, s, len); Assert(res == 0); /* should not fail when the message fits in buffer */ }
/* ---------------- * EndCommand - clean up the destination at end of command * ---------------- */ void EndCommand(const char *commandTag, CommandDest dest) { StringInfoData buf; if (Gp_role == GP_ROLE_DISPATCH) { /* * Just before a successful reply, let's see if the DTM has * phase 2 retry work. */ doDtxPhase2Retry(); } switch (dest) { case DestRemote: case DestRemoteExecute: /* * We assume the commandTag is plain ASCII and therefore * requires no encoding conversion. */ if (Gp_role == GP_ROLE_EXECUTE) { sendQEDetails(); pq_beginmessage(&buf, 'C'); pq_send_ascii_string(&buf, commandTag); pq_endmessage(&buf); } else pq_putmessage('C', commandTag, strlen(commandTag) + 1); break; case DestNone: case DestDebug: case DestSPI: case DestTuplestore: case DestIntoRel: case DestCopyOut: break; } }
/* * Read up to MAX_SEND_SIZE bytes of WAL that's been flushed to disk, * but not yet sent to the client, and send it. * * msgbuf is a work area in which the output message is constructed. It's * passed in just so we can avoid re-palloc'ing the buffer on each cycle. * It must be of size 1 + sizeof(WalDataMessageHeader) + MAX_SEND_SIZE. * * If there is no unsent WAL remaining, *caughtup is set to true, otherwise * *caughtup is set to false. * * Returns true if OK, false if trouble. */ static bool XLogSend(char *msgbuf, bool *caughtup) { XLogRecPtr SendRqstPtr; XLogRecPtr startptr; XLogRecPtr endptr; Size nbytes; WalDataMessageHeader msghdr; /* * Attempt to send all data that's already been written out and fsync'd to * disk. We cannot go further than what's been written out given the * current implementation of XLogRead(). And in any case it's unsafe to * send WAL that is not securely down to disk on the master: if the master * subsequently crashes and restarts, slaves must not have applied any WAL * that gets lost on the master. */ SendRqstPtr = GetFlushRecPtr(); /* Quick exit if nothing to do */ if (XLByteLE(SendRqstPtr, sentPtr)) { *caughtup = true; return true; } /* * Figure out how much to send in one message. If there's no more than * MAX_SEND_SIZE bytes to send, send everything. Otherwise send * MAX_SEND_SIZE bytes, but round back to logfile or page boundary. * * The rounding is not only for performance reasons. Walreceiver relies on * the fact that we never split a WAL record across two messages. Since a * long WAL record is split at page boundary into continuation records, * page boundary is always a safe cut-off point. We also assume that * SendRqstPtr never points to the middle of a WAL record. */ startptr = sentPtr; if (startptr.xrecoff >= XLogFileSize) { /* * crossing a logid boundary, skip the non-existent last log segment * in previous logical log file. */ startptr.xlogid += 1; startptr.xrecoff = 0; } endptr = startptr; XLByteAdvance(endptr, MAX_SEND_SIZE); if (endptr.xlogid != startptr.xlogid) { /* Don't cross a logfile boundary within one message */ Assert(endptr.xlogid == startptr.xlogid + 1); endptr.xlogid = startptr.xlogid; endptr.xrecoff = XLogFileSize; } /* if we went beyond SendRqstPtr, back off */ if (XLByteLE(SendRqstPtr, endptr)) { endptr = SendRqstPtr; *caughtup = true; } else { /* round down to page boundary. */ endptr.xrecoff -= (endptr.xrecoff % XLOG_BLCKSZ); *caughtup = false; } nbytes = endptr.xrecoff - startptr.xrecoff; Assert(nbytes <= MAX_SEND_SIZE); /* * OK to read and send the slice. */ msgbuf[0] = 'w'; /* * Read the log directly into the output buffer to avoid extra memcpy * calls. */ XLogRead(msgbuf + 1 + sizeof(WalDataMessageHeader), startptr, nbytes); /* * We fill the message header last so that the send timestamp is taken as * late as possible. */ msghdr.dataStart = startptr; msghdr.walEnd = SendRqstPtr; msghdr.sendTime = GetCurrentTimestamp(); memcpy(msgbuf + 1, &msghdr, sizeof(WalDataMessageHeader)); pq_putmessage('d', msgbuf, 1 + sizeof(WalDataMessageHeader) + nbytes); /* Flush pending output to the client */ if (pq_flush()) return false; sentPtr = endptr; /* Update shared memory status */ { /* use volatile pointer to prevent code rearrangement */ volatile WalSnd *walsnd = MyWalSnd; SpinLockAcquire(&walsnd->mutex); walsnd->sentPtr = sentPtr; SpinLockRelease(&walsnd->mutex); } /* Report progress of XLOG streaming in PS display */ if (update_process_title) { char activitymsg[50]; snprintf(activitymsg, sizeof(activitymsg), "streaming %X/%X", sentPtr.xlogid, sentPtr.xrecoff); set_ps_display(activitymsg, false); } return true; }
/* ---------------- * printtup --- print a tuple in protocol 3.0 * ---------------- */ static void printtup(TupleTableSlot *slot, DestReceiver *self) { TupleDesc typeinfo = slot->tts_tupleDescriptor; DR_printtup *myState = (DR_printtup *) self; StringInfoData buf; int natts = typeinfo->natts; int i; #ifdef PGXC /* * If we are having DataRow-based tuple we do not have to encode attribute * values, just send over the DataRow message as we received it from the * Datanode */ if (slot->tts_dataRow) { pq_putmessage('D', slot->tts_dataRow, slot->tts_dataLen); return; } #endif /* Set or update my derived attribute info, if needed */ if (myState->attrinfo != typeinfo || myState->nattrs != natts) printtup_prepare_info(myState, typeinfo, natts); /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* * Prepare a DataRow message */ pq_beginmessage(&buf, 'D'); pq_sendint(&buf, natts, 2); /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { PrinttupAttrInfo *thisState = myState->myinfo + i; Datum origattr = slot->tts_values[i], attr; if (slot->tts_isnull[i]) { pq_sendint(&buf, -1, 4); continue; } /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage inside the type's output routine. */ if (thisState->typisvarlena) attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); else attr = origattr; if (thisState->format == 0) { /* Text output */ char *outputstr; outputstr = OutputFunctionCall(&thisState->finfo, attr); pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false); pfree(outputstr); } else { /* Binary output */ bytea *outputbytes; outputbytes = SendFunctionCall(&thisState->finfo, attr); pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); pfree(outputbytes); } /* Clean up detoasted copy, if any */ if (DatumGetPointer(attr) != DatumGetPointer(origattr)) pfree(DatumGetPointer(attr)); } pq_endmessage(&buf); }
/* ---------------- * printtup --- print a tuple in protocol 3.0 * ---------------- */ static void printtup(TupleTableSlot *slot, DestReceiver *self) { TupleDesc typeinfo = slot->tts_tupleDescriptor; DR_printtup *myState = (DR_printtup *) self; MemoryContext oldcontext; StringInfoData buf; int natts = typeinfo->natts; int i; #ifdef PGXC /* * If we are having DataRow-based tuple we do not have to encode attribute * values, just send over the DataRow message as we received it from the * Datanode */ #ifdef XCP if (slot->tts_datarow) { pq_putmessage('D', slot->tts_datarow->msg, slot->tts_datarow->msglen); return; } #else if (slot->tts_dataRow) { pq_putmessage('D', slot->tts_dataRow, slot->tts_dataLen); return; } #endif #endif /* Set or update my derived attribute info, if needed */ if (myState->attrinfo != typeinfo || myState->nattrs != natts) printtup_prepare_info(myState, typeinfo, natts); /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* Switch into per-row context so we can recover memory below */ oldcontext = MemoryContextSwitchTo(myState->tmpcontext); /* * Prepare a DataRow message (note buffer is in per-row context) */ pq_beginmessage(&buf, 'D'); pq_sendint(&buf, natts, 2); /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { PrinttupAttrInfo *thisState = myState->myinfo + i; Datum attr = slot->tts_values[i]; if (slot->tts_isnull[i]) { pq_sendint(&buf, -1, 4); continue; } /* * Here we catch undefined bytes in datums that are returned to the * client without hitting disk; see comments at the related check in * PageAddItem(). This test is most useful for uncompressed, * non-external datums, but we're quite likely to see such here when * testing new C functions. */ if (thisState->typisvarlena) VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr), VARSIZE_ANY(attr)); if (thisState->format == 0) { /* Text output */ char *outputstr; outputstr = OutputFunctionCall(&thisState->finfo, attr); pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false); } else { /* Binary output */ bytea *outputbytes; outputbytes = SendFunctionCall(&thisState->finfo, attr); pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); } } pq_endmessage(&buf); /* Return to caller's context, and flush row's temporary memory */ MemoryContextSwitchTo(oldcontext); MemoryContextReset(myState->tmpcontext); }
static void MPPnoticeReceiver(void * arg, const PGresult * res) { PQExpBufferData msgbuf; PGMessageField *pfield; int elevel = INFO; char * sqlstate = "00000"; char * severity = "WARNING"; char * file = ""; char * line = NULL; char * func = ""; char message[1024]; char * detail = NULL; char * hint = NULL; char * context = NULL; SegmentDatabaseDescriptor *segdbDesc = (SegmentDatabaseDescriptor *) arg; if (!res) return; strcpy(message,"missing error text"); for (pfield = res->errFields; pfield != NULL; pfield = pfield->next) { switch (pfield->code) { case PG_DIAG_SEVERITY: severity = pfield->contents; if (strcmp(pfield->contents,"WARNING")==0) elevel = WARNING; else if (strcmp(pfield->contents,"NOTICE")==0) elevel = NOTICE; else if (strcmp(pfield->contents,"DEBUG1")==0 || strcmp(pfield->contents,"DEBUG")==0) elevel = DEBUG1; else if (strcmp(pfield->contents,"DEBUG2")==0) elevel = DEBUG2; else if (strcmp(pfield->contents,"DEBUG3")==0) elevel = DEBUG3; else if (strcmp(pfield->contents,"DEBUG4")==0) elevel = DEBUG4; else if (strcmp(pfield->contents,"DEBUG5")==0) elevel = DEBUG5; else elevel = INFO; break; case PG_DIAG_SQLSTATE: sqlstate = pfield->contents; break; case PG_DIAG_MESSAGE_PRIMARY: strncpy(message, pfield->contents, 800); message[800] = '\0'; if (segdbDesc && segdbDesc->whoami && strlen(segdbDesc->whoami) < 200) { strcat(message," ("); strcat(message, segdbDesc->whoami); strcat(message,")"); } break; case PG_DIAG_MESSAGE_DETAIL: detail = pfield->contents; break; case PG_DIAG_MESSAGE_HINT: hint = pfield->contents; break; case PG_DIAG_STATEMENT_POSITION: case PG_DIAG_INTERNAL_POSITION: case PG_DIAG_INTERNAL_QUERY: break; case PG_DIAG_CONTEXT: context = pfield->contents; break; case PG_DIAG_SOURCE_FILE: file = pfield->contents; break; case PG_DIAG_SOURCE_LINE: line = pfield->contents; break; case PG_DIAG_SOURCE_FUNCTION: func = pfield->contents; break; case PG_DIAG_GP_PROCESS_TAG: break; default: break; } } if (elevel < client_min_messages && elevel != INFO) return; /* * We use PQExpBufferData instead of StringInfoData * because the former uses malloc, the latter palloc. * We are in a thread, and we CANNOT use palloc since it's not * thread safe. We cannot call elog or ereport either for the * same reason. */ initPQExpBuffer(&msgbuf); if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { /* New style with separate fields */ appendPQExpBufferChar(&msgbuf, PG_DIAG_SEVERITY); appendBinaryPQExpBuffer(&msgbuf, severity, strlen(severity)+1); appendPQExpBufferChar(&msgbuf, PG_DIAG_SQLSTATE); appendBinaryPQExpBuffer(&msgbuf, sqlstate, strlen(sqlstate)+1); /* M field is required per protocol, so always send something */ appendPQExpBufferChar(&msgbuf, PG_DIAG_MESSAGE_PRIMARY); appendBinaryPQExpBuffer(&msgbuf, message , strlen(message) + 1); if (detail) { appendPQExpBufferChar(&msgbuf, PG_DIAG_MESSAGE_DETAIL); appendBinaryPQExpBuffer(&msgbuf, detail, strlen(detail)+1); } if (hint) { appendPQExpBufferChar(&msgbuf, PG_DIAG_MESSAGE_HINT); appendBinaryPQExpBuffer(&msgbuf, hint, strlen(hint)+1); } if (context) { appendPQExpBufferChar(&msgbuf, PG_DIAG_CONTEXT); appendBinaryPQExpBuffer(&msgbuf, context, strlen(context)+1); } if (file) { appendPQExpBufferChar(&msgbuf, PG_DIAG_SOURCE_FILE); appendBinaryPQExpBuffer(&msgbuf, file, strlen(file)+1); } if (line) { appendPQExpBufferChar(&msgbuf, PG_DIAG_SOURCE_LINE); appendBinaryPQExpBuffer(&msgbuf, line, strlen(line)+1); } if (func) { appendPQExpBufferChar(&msgbuf, PG_DIAG_SOURCE_FUNCTION); appendBinaryPQExpBuffer(&msgbuf, func, strlen(func)+1); } } else { appendPQExpBuffer(&msgbuf, "%s: ", severity); appendBinaryPQExpBuffer(&msgbuf, message, strlen(message)); appendPQExpBufferChar(&msgbuf, '\n'); appendPQExpBufferChar(&msgbuf, '\0'); } appendPQExpBufferChar(&msgbuf, '\0'); /* terminator */ pq_putmessage('N', msgbuf.data, msgbuf.len); termPQExpBuffer(&msgbuf); pq_flush(); }