/* * Write relation description to the output stream. */ static void pglogical_write_rel(StringInfo out, PGLogicalOutputData *data, Relation rel) { const char *nspname; uint8 nspnamelen; const char *relname; uint8 relnamelen; Oid relid; if (MtmIsFilteredTxn) { return; } relid = RelationGetRelid(rel); pq_sendbyte(out, 'R'); /* sending RELATION */ pq_sendint(out, relid, sizeof relid); /* use Oid as relation identifier */ nspname = get_namespace_name(rel->rd_rel->relnamespace); if (nspname == NULL) elog(ERROR, "cache lookup failed for namespace %u", rel->rd_rel->relnamespace); nspnamelen = strlen(nspname) + 1; relname = NameStr(rel->rd_rel->relname); relnamelen = strlen(relname) + 1; pq_sendbyte(out, nspnamelen); /* schema name length */ pq_sendbytes(out, nspname, nspnamelen); pq_sendbyte(out, relnamelen); /* table name length */ pq_sendbytes(out, relname, relnamelen); }
/* * IDENTIFY_SYSTEM */ static void IdentifySystem(void) { StringInfoData buf; char sysid[32]; char tli[11]; /* * Reply with a result set with one row, two columns. First col is system * ID, and second is timeline ID */ snprintf(sysid, sizeof(sysid), UINT64_FORMAT, GetSystemIdentifier()); snprintf(tli, sizeof(tli), "%u", ThisTimeLineID); /* Send a RowDescription message */ pq_beginmessage(&buf, 'T'); pq_sendint(&buf, 2, 2); /* 2 fields */ /* first field */ pq_sendstring(&buf, "systemid"); /* col name */ pq_sendint(&buf, 0, 4); /* table oid */ pq_sendint(&buf, 0, 2); /* attnum */ pq_sendint(&buf, TEXTOID, 4); /* type oid */ pq_sendint(&buf, -1, 2); /* typlen */ pq_sendint(&buf, 0, 4); /* typmod */ pq_sendint(&buf, 0, 2); /* format code */ /* second field */ pq_sendstring(&buf, "timeline"); /* col name */ pq_sendint(&buf, 0, 4); /* table oid */ pq_sendint(&buf, 0, 2); /* attnum */ pq_sendint(&buf, INT4OID, 4); /* type oid */ pq_sendint(&buf, 4, 2); /* typlen */ pq_sendint(&buf, 0, 4); /* typmod */ pq_sendint(&buf, 0, 2); /* format code */ pq_endmessage(&buf); /* Send a DataRow message */ pq_beginmessage(&buf, 'D'); pq_sendint(&buf, 2, 2); /* # of columns */ pq_sendint(&buf, strlen(sysid), 4); /* col1 len */ pq_sendbytes(&buf, (char *) &sysid, strlen(sysid)); pq_sendint(&buf, strlen(tli), 4); /* col2 len */ pq_sendbytes(&buf, (char *) tli, strlen(tli)); pq_endmessage(&buf); /* Send CommandComplete and ReadyForQuery messages */ EndCommand("SELECT", DestRemote); ReadyForQuery(DestRemote); /* ReadyForQuery did pq_flush for us */ }
/** * svec_send - converts text to binary format */ Datum svec_send(PG_FUNCTION_ARGS) { StringInfoData buf; SvecType *svec = PG_GETARG_SVECTYPE_P(0); SparseData sdata = sdata_from_svec(svec); pq_begintypsend(&buf); pq_sendint(&buf,sdata->type_of_data,sizeof(Oid)); pq_sendint(&buf,sdata->unique_value_count,sizeof(int)); pq_sendint(&buf,sdata->total_value_count,sizeof(int)); pq_sendint(&buf,sdata->vals->len,sizeof(int)); pq_sendint(&buf,sdata->index->len,sizeof(int)); pq_sendbytes(&buf,sdata->vals->data,sdata->vals->len); pq_sendbytes(&buf,sdata->index->data,sdata->index->len); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); }
Datum uuid_send(PG_FUNCTION_ARGS) { pg_uuid_t *uuid = PG_GETARG_UUID_P(0); StringInfoData buffer; pq_begintypsend(&buffer); pq_sendbytes(&buffer, (char *) uuid->data, UUID_LEN); PG_RETURN_BYTEA_P(pq_endtypsend(&buffer)); }
/* * Send a gpdb libpq message. */ void sendQEDetails(void) { StringInfoData buf; pq_beginmessage(&buf, 'w'); pq_sendint(&buf, (int32) ICListenerPort, sizeof(int32)); pq_sendint(&buf, sizeof(PG_VERSION_STR), sizeof(int32)); pq_sendbytes(&buf, PG_VERSION_STR, sizeof(PG_VERSION_STR)); pq_endmessage(&buf); }
datum_t uuid_send(PG_FUNC_ARGS) { pg_uuid_t* uuid; struct string buffer; uuid = ARG_UUID_P(0); pq_begintypsend(&buffer); pq_sendbytes(&buffer, (char*) uuid->data, UUID_LEN); RET_BYTEA_P(pq_endtypsend(&buffer)); }
/* * Send an authentication request packet to the frontend. */ static void sendAuthRequest(Port *port, AuthRequest areq) { StringInfoData buf; pq_beginmessage(&buf, 'R'); pq_sendint(&buf, (int32) areq, sizeof(int32)); /* Add the salt for encrypted passwords. */ if (areq == AUTH_REQ_MD5) pq_sendbytes(&buf, port->md5Salt, 4); else if (areq == AUTH_REQ_CRYPT) pq_sendbytes(&buf, port->cryptSalt, 2); #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) /* * Add the authentication data for the next step of the GSSAPI or SSPI * negotiation. */ else if (areq == AUTH_REQ_GSS_CONT) { if (port->gss->outbuf.length > 0) { elog(DEBUG4, "sending GSS token of length %u", (unsigned int) port->gss->outbuf.length); pq_sendbytes(&buf, port->gss->outbuf.value, port->gss->outbuf.length); } } #endif pq_endmessage(&buf); /* * Flush message so client will see it, except for AUTH_REQ_OK, which need * not be sent until we are ready for queries. */ if (areq != AUTH_REQ_OK) pq_flush(); }
/* * Send a gpdb libpq message. */ void sendQEDetails(void) { StringInfoData buf; pq_beginmessage(&buf, 'w'); pq_sendint(&buf, (int32) Gp_listener_port, sizeof(int32)); pq_sendint64(&buf, VmemTracker_GetMaxReservedVmemBytes()); pq_sendint(&buf, sizeof(PG_VERSION_STR), sizeof(int32)); pq_sendbytes(&buf, PG_VERSION_STR, sizeof(PG_VERSION_STR)); pq_endmessage(&buf); }
/* * Send an authentication request packet to the frontend. */ static void sendAuthRequest(Port *port, AuthRequest areq) { StringInfoData buf; pq_beginmessage(&buf, 'R'); pq_sendint(&buf, (int32) areq, sizeof(int32)); /* Add the salt for encrypted passwords. */ if (areq == AUTH_REQ_MD5) pq_sendbytes(&buf, port->md5Salt, 4); else if (areq == AUTH_REQ_CRYPT) pq_sendbytes(&buf, port->cryptSalt, 2); pq_endmessage(&buf); /* * Flush message so client will see it, except for AUTH_REQ_OK, which * need not be sent until we are ready for queries. */ if (areq != AUTH_REQ_OK) pq_flush(); }
/* * record_send - binary output routine for any composite type. */ Datum record_send(PG_FUNCTION_ARGS) { HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tuple; RecordIOData *my_extra; int ncolumns; int validcols; int i; Datum *values; bool *nulls; StringInfoData buf; /* Extract type info from the tuple itself */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); ncolumns = tupdesc->natts; /* Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = rec; /* * We arrange to look up the needed I/O info just once per series of * calls, assuming the record type doesn't change underneath us. */ my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns != ncolumns) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; my_extra->record_type = InvalidOid; my_extra->record_typmod = 0; } if (my_extra->record_type != tupType || my_extra->record_typmod != tupTypmod) { MemSet(my_extra, 0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra->record_type = tupType; my_extra->record_typmod = tupTypmod; my_extra->ncolumns = ncolumns; } values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (bool *) palloc(ncolumns * sizeof(bool)); /* Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); /* And build the result string */ pq_begintypsend(&buf); /* Need to scan to count nondeleted columns */ validcols = 0; for (i = 0; i < ncolumns; i++) { if (!tupdesc->attrs[i]->attisdropped) validcols++; } pq_sendint(&buf, validcols, 4); for (i = 0; i < ncolumns; i++) { ColumnIOData *column_info = &my_extra->columns[i]; Oid column_type = tupdesc->attrs[i]->atttypid; bytea *outputbytes; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) continue; pq_sendint(&buf, column_type, sizeof(Oid)); if (nulls[i]) { /* emit -1 data length to signify a NULL */ pq_sendint(&buf, -1, 4); continue; } /* * Convert the column value to binary */ if (column_info->column_type != column_type) { bool typIsVarlena; getTypeBinaryOutputInfo(column_type, &column_info->typiofunc, &typIsVarlena); fmgr_info_cxt(column_info->typiofunc, &column_info->proc, fcinfo->flinfo->fn_mcxt); column_info->column_type = column_type; } outputbytes = SendFunctionCall(&column_info->proc, values[i]); /* We assume the result will not have been toasted */ pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); pfree(outputbytes); } pfree(values); pfree(nulls); ReleaseTupleDesc(tupdesc); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); }
/* * IDENTIFY_SYSTEM */ static void IdentifySystem(void) { StringInfoData buf; char sysid[32]; char tli[11]; char xpos[MAXFNAMELEN]; XLogRecPtr logptr; /* * Reply with a result set with one row, three columns. First col is * system ID, second is timeline ID, and third is current xlog location. */ snprintf(sysid, sizeof(sysid), UINT64_FORMAT, GetSystemIdentifier()); snprintf(tli, sizeof(tli), "%u", ThisTimeLineID); logptr = am_cascading_walsender ? GetStandbyFlushRecPtr() : GetInsertRecPtr(); snprintf(xpos, sizeof(xpos), "%X/%X", logptr.xlogid, logptr.xrecoff); /* Send a RowDescription message */ pq_beginmessage(&buf, 'T'); pq_sendint(&buf, 3, 2); /* 3 fields */ /* first field */ pq_sendstring(&buf, "systemid"); /* col name */ pq_sendint(&buf, 0, 4); /* table oid */ pq_sendint(&buf, 0, 2); /* attnum */ pq_sendint(&buf, TEXTOID, 4); /* type oid */ pq_sendint(&buf, -1, 2); /* typlen */ pq_sendint(&buf, 0, 4); /* typmod */ pq_sendint(&buf, 0, 2); /* format code */ /* second field */ pq_sendstring(&buf, "timeline"); /* col name */ pq_sendint(&buf, 0, 4); /* table oid */ pq_sendint(&buf, 0, 2); /* attnum */ pq_sendint(&buf, INT4OID, 4); /* type oid */ pq_sendint(&buf, 4, 2); /* typlen */ pq_sendint(&buf, 0, 4); /* typmod */ pq_sendint(&buf, 0, 2); /* format code */ /* third field */ pq_sendstring(&buf, "xlogpos"); pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); pq_sendint(&buf, TEXTOID, 4); pq_sendint(&buf, -1, 2); pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); pq_endmessage(&buf); /* Send a DataRow message */ pq_beginmessage(&buf, 'D'); pq_sendint(&buf, 3, 2); /* # of columns */ pq_sendint(&buf, strlen(sysid), 4); /* col1 len */ pq_sendbytes(&buf, (char *) &sysid, strlen(sysid)); pq_sendint(&buf, strlen(tli), 4); /* col2 len */ pq_sendbytes(&buf, (char *) tli, strlen(tli)); pq_sendint(&buf, strlen(xpos), 4); /* col3 len */ pq_sendbytes(&buf, (char *) xpos, strlen(xpos)); pq_endmessage(&buf); /* Send CommandComplete and ReadyForQuery messages */ EndCommand("SELECT", DestRemote); ReadyForQuery(DestRemote); /* ReadyForQuery did pq_flush for us */ }
/* ---------------- * printtup_internal_20 --- print a binary tuple in protocol 2.0 * * We use a different message type, i.e. 'B' instead of 'D' to * indicate a tuple in internal (binary) form. * * This is largely same as printtup_20, except we use binary formatting. * ---------------- */ static void printtup_internal_20(TupleTableSlot *slot, DestReceiver *self) { TupleDesc typeinfo = slot->tts_tupleDescriptor; DR_printtup *myState = (DR_printtup *) self; StringInfoData buf; int natts = typeinfo->natts; int i, j, k; /* 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); /* * tell the frontend to expect new tuple data (in binary style) */ pq_beginmessage(&buf, 'B'); /* * send a bitmap of which attributes are not null */ j = 0; k = 1 << 7; for (i = 0; i < natts; ++i) { if (!slot->tts_isnull[i]) j |= k; /* set bit if not null */ k >>= 1; if (k == 0) /* end of byte? */ { pq_sendint(&buf, j, 1); j = 0; k = 1 << 7; } } if (k != (1 << 7)) /* flush last partial byte */ pq_sendint(&buf, j, 1); /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { PrinttupAttrInfo *thisState = myState->myinfo + i; Datum origattr = slot->tts_values[i], attr; bytea *outputbytes; if (slot->tts_isnull[i]) continue; Assert(thisState->format == 1); /* * 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; outputbytes = SendFunctionCall(&thisState->finfo, attr); /* We assume the result will not have been toasted */ 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; StringInfoData buf; int natts = typeinfo->natts; int i; /* 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); }
/* * TupleToBytes * * Serialize a tuple to bytes */ StringInfo TupleToBytes(BufferPrinterState *self, TupleTableSlot *slot) { StringInfo buf = makeStringInfo(); TupleDesc typeinfo = slot->tts_tupleDescriptor; int natts = typeinfo->natts; int i; int len = 0; /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* * Prepare a DataRow message */ pq_beginmessage(buf, 'D'); len += 1; pq_sendint(buf, natts, 2); len+= 2; /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { BufferPrinterAttrInfo *thisState = self->typeinfo + i; Datum origattr = slot->tts_values[i], attr; if (slot->tts_isnull[i]) { pq_sendint(buf, -1, 4); len += 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); len += strlen(outputstr); pfree(outputstr); } else { /* Binary output */ bytea *outputbytes; int size; outputbytes = SendFunctionCall(&thisState->finfo, attr); pq_sendint(buf, VARSIZE(outputbytes) - VARHDRSZ, 4); len += 4; size = VARSIZE(outputbytes) - VARHDRSZ; pq_sendbytes(buf, VARDATA(outputbytes), size); len += size; pfree(outputbytes); } /* Clean up detoasted copy, if any */ if (DatumGetPointer(attr) != DatumGetPointer(origattr)) pfree(DatumGetPointer(attr)); } return buf; }
/* ---------------- * printtup --- print a tuple in protocol 3.0 * ---------------- */ static bool 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; /* 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); 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; /* 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. * * Here we catch undefined bytes in tuples that are returned to the * client without hitting disk; see comments at the related check in * PageAddItem(). Whether to test before or after detoast is somewhat * arbitrary, as is whether to test external/compressed data at all. * Undefined bytes in the pre-toast datum will have triggered Valgrind * errors in the compressor or toaster; any error detected here for * such datums would indicate an (unlikely) bug in a type-independent * facility. Therefore, this test is most useful for uncompressed, * non-external datums. * * We don't presently bother checking non-varlena datums for undefined * data. PageAddItem() does check them. */ if (thisState->typisvarlena) { VALGRIND_CHECK_MEM_IS_DEFINED(origattr, VARSIZE_ANY(origattr)); 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); }
/* * Execute commands from walreceiver, until we enter streaming mode. */ static void WalSndHandshake(void) { StringInfoData input_message; bool replication_started = false; initStringInfo(&input_message); while (!replication_started) { int firstchar; /* Wait for a command to arrive */ firstchar = pq_getbyte(); /* * Emergency bailout if postmaster has died. This is to avoid the * necessity for manual cleanup of all postmaster children. */ if (!PostmasterIsAlive(true)) exit(1); /* * Check for any other interesting events that happened while we * slept. */ if (got_SIGHUP) { got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); } if (firstchar != EOF) { /* * Read the message contents. This is expected to be done without * blocking because we've been able to get message type code. */ if (pq_getmessage(&input_message, 0)) firstchar = EOF; /* suitable message already logged */ } /* Handle the very limited subset of commands expected in this phase */ switch (firstchar) { case 'Q': /* Query message */ { const char *query_string; XLogRecPtr recptr; query_string = pq_getmsgstring(&input_message); pq_getmsgend(&input_message); if (strcmp(query_string, "IDENTIFY_SYSTEM") == 0) { StringInfoData buf; char sysid[32]; char tli[11]; /* * Reply with a result set with one row, two columns. * First col is system ID, and second is timeline ID */ snprintf(sysid, sizeof(sysid), UINT64_FORMAT, GetSystemIdentifier()); snprintf(tli, sizeof(tli), "%u", ThisTimeLineID); /* Send a RowDescription message */ pq_beginmessage(&buf, 'T'); pq_sendint(&buf, 2, 2); /* 2 fields */ /* first field */ pq_sendstring(&buf, "systemid"); /* col name */ pq_sendint(&buf, 0, 4); /* table oid */ pq_sendint(&buf, 0, 2); /* attnum */ pq_sendint(&buf, TEXTOID, 4); /* type oid */ pq_sendint(&buf, -1, 2); /* typlen */ pq_sendint(&buf, 0, 4); /* typmod */ pq_sendint(&buf, 0, 2); /* format code */ /* second field */ pq_sendstring(&buf, "timeline"); /* col name */ pq_sendint(&buf, 0, 4); /* table oid */ pq_sendint(&buf, 0, 2); /* attnum */ pq_sendint(&buf, INT4OID, 4); /* type oid */ pq_sendint(&buf, 4, 2); /* typlen */ pq_sendint(&buf, 0, 4); /* typmod */ pq_sendint(&buf, 0, 2); /* format code */ pq_endmessage(&buf); /* Send a DataRow message */ pq_beginmessage(&buf, 'D'); pq_sendint(&buf, 2, 2); /* # of columns */ pq_sendint(&buf, strlen(sysid), 4); /* col1 len */ pq_sendbytes(&buf, (char *) &sysid, strlen(sysid)); pq_sendint(&buf, strlen(tli), 4); /* col2 len */ pq_sendbytes(&buf, (char *) tli, strlen(tli)); pq_endmessage(&buf); /* Send CommandComplete and ReadyForQuery messages */ EndCommand("SELECT", DestRemote); ReadyForQuery(DestRemote); /* ReadyForQuery did pq_flush for us */ } else if (sscanf(query_string, "START_REPLICATION %X/%X", &recptr.xlogid, &recptr.xrecoff) == 2) { StringInfoData buf; /* * Check that we're logging enough information in the * WAL for log-shipping. * * NOTE: This only checks the current value of * wal_level. Even if the current setting is not * 'minimal', there can be old WAL in the pg_xlog * directory that was created with 'minimal'. So this * is not bulletproof, the purpose is just to give a * user-friendly error message that hints how to * configure the system correctly. */ if (wal_level == WAL_LEVEL_MINIMAL) ereport(FATAL, (errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("standby connections not allowed because wal_level=minimal"))); /* Send a CopyOutResponse message, and start streaming */ pq_beginmessage(&buf, 'H'); pq_sendbyte(&buf, 0); pq_sendint(&buf, 0, 2); pq_endmessage(&buf); pq_flush(); /* * Initialize position to the received one, then the * xlog records begin to be shipped from that position */ sentPtr = recptr; /* break out of the loop */ replication_started = true; } else { ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid standby query string: %s", query_string))); } break; } case 'X': /* standby is closing the connection */ proc_exit(0); case EOF: /* standby disconnected unexpectedly */ ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unexpected EOF on standby connection"))); proc_exit(0); default: ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid standby handshake message type %d", firstchar))); } } }
/* ---------------- * printtup_internal_20 --- print a binary tuple in protocol 2.0 * * We use a different message type, i.e. 'B' instead of 'D' to * indicate a tuple in internal (binary) form. * * This is largely same as printtup_20, except we use binary formatting. * ---------------- */ static bool printtup_internal_20(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, j, k; /* 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); /* * tell the frontend to expect new tuple data (in binary style) */ pq_beginmessage(&buf, 'B'); /* * send a bitmap of which attributes are not null */ j = 0; k = 1 << 7; for (i = 0; i < natts; ++i) { if (!slot->tts_isnull[i]) j |= k; /* set bit if not null */ k >>= 1; if (k == 0) /* end of byte? */ { pq_sendint(&buf, j, 1); j = 0; k = 1 << 7; } } if (k != (1 << 7)) /* flush last partial byte */ pq_sendint(&buf, j, 1); /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { PrinttupAttrInfo *thisState = myState->myinfo + i; Datum attr = slot->tts_values[i]; bytea *outputbytes; if (slot->tts_isnull[i]) continue; Assert(thisState->format == 1); 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); return true; }
/* * Write a tuple to the outputstream, in the most efficient format possible. */ static void pglogical_write_tuple(StringInfo out, PGLogicalOutputData *data, Relation rel, HeapTuple tuple) { TupleDesc desc; Datum values[MaxTupleAttributeNumber]; bool isnull[MaxTupleAttributeNumber]; int i; uint16 nliveatts = 0; desc = RelationGetDescr(rel); pq_sendbyte(out, 'T'); /* sending TUPLE */ for (i = 0; i < desc->natts; i++) { if (desc->attrs[i]->attisdropped) continue; nliveatts++; } pq_sendint(out, nliveatts, 2); /* try to allocate enough memory from the get go */ enlargeStringInfo(out, tuple->t_len + nliveatts * (1 + 4)); /* * XXX: should this prove to be a relevant bottleneck, it might be * interesting to inline heap_deform_tuple() here, we don't actually need * the information in the form we get from it. */ heap_deform_tuple(tuple, desc, values, isnull); for (i = 0; i < desc->natts; i++) { HeapTuple typtup; Form_pg_type typclass; Form_pg_attribute att = desc->attrs[i]; char transfer_type; /* skip dropped columns */ if (att->attisdropped) continue; if (isnull[i]) { pq_sendbyte(out, 'n'); /* null column */ continue; } else if (att->attlen == -1 && VARATT_IS_EXTERNAL_ONDISK(values[i])) { pq_sendbyte(out, 'u'); /* unchanged toast column */ continue; } typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(att->atttypid)); if (!HeapTupleIsValid(typtup)) elog(ERROR, "cache lookup failed for type %u", att->atttypid); typclass = (Form_pg_type) GETSTRUCT(typtup); transfer_type = decide_datum_transfer(att, typclass, data->allow_internal_basetypes, data->allow_binary_basetypes); pq_sendbyte(out, transfer_type); switch (transfer_type) { case 'b': /* internal-format binary data follows */ /* pass by value */ if (att->attbyval) { pq_sendint(out, att->attlen, 4); /* length */ enlargeStringInfo(out, att->attlen); store_att_byval(out->data + out->len, values[i], att->attlen); out->len += att->attlen; out->data[out->len] = '\0'; } /* fixed length non-varlena pass-by-reference type */ else if (att->attlen > 0) { pq_sendint(out, att->attlen, 4); /* length */ appendBinaryStringInfo(out, DatumGetPointer(values[i]), att->attlen); } /* varlena type */ else if (att->attlen == -1) { char *data = DatumGetPointer(values[i]); /* send indirect datums inline */ if (VARATT_IS_EXTERNAL_INDIRECT(values[i])) { struct varatt_indirect redirect; VARATT_EXTERNAL_GET_POINTER(redirect, data); data = (char *) redirect.pointer; } Assert(!VARATT_IS_EXTERNAL(data)); pq_sendint(out, VARSIZE_ANY(data), 4); /* length */ appendBinaryStringInfo(out, data, VARSIZE_ANY(data)); } else elog(ERROR, "unsupported tuple type"); break; case 's': /* binary send/recv data follows */ { bytea *outputbytes; int len; outputbytes = OidSendFunctionCall(typclass->typsend, values[i]); len = VARSIZE(outputbytes) - VARHDRSZ; pq_sendint(out, len, 4); /* length */ pq_sendbytes(out, VARDATA(outputbytes), len); /* data */ pfree(outputbytes); } break; default: { char *outputstr; int len; outputstr = OidOutputFunctionCall(typclass->typoutput, values[i]); len = strlen(outputstr) + 1; pq_sendint(out, len, 4); /* length */ appendBinaryStringInfo(out, outputstr, len); /* data */ pfree(outputstr); } } ReleaseSysCache(typtup); } }