/* * Send NOTIFY message to my front end. */ static void NotifyMyFrontEnd(char *relname, int32 listenerPID) { if (whereToSendOutput == DestRemote) { StringInfoData buf; pq_beginmessage(&buf, 'A'); pq_sendint(&buf, listenerPID, sizeof(int32)); pq_sendstring(&buf, relname); if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { /* XXX Add parameter string here later */ pq_sendstring(&buf, ""); } 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. */ } else elog(INFO, "NOTIFY for %s", relname); }
static void serialize_filesystem_credential(StringInfo buffer, struct FileSystemCredential *entry) { Assert(NULL != entry && NULL != buffer); pq_sendstring(buffer, entry->key.host); pq_sendstring(buffer, entry->key.protocol); pq_sendint(buffer, entry->key.port, sizeof(int)); pq_sendstring(buffer, entry->credential); }
/* ---------------- * 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: if (Gp_role == GP_ROLE_EXECUTE && Gp_is_writer) { /* * Extra information that indicates if the transaction made * updates. */ sendQEDetails(); pq_beginmessage(&buf, 'g'); pq_sendstring(&buf, commandTag); AddQEWriterTransactionInfo(&buf); pq_endmessage(&buf); } else if (Gp_role == GP_ROLE_EXECUTE) { sendQEDetails(); pq_beginmessage(&buf, 'C'); pq_sendstring(&buf, commandTag); pq_endmessage(&buf); } else pq_puttextmessage('C', commandTag); break; case DestNone: case DestDebug: case DestSPI: case DestTuplestore: case DestIntoRel: case DestCopyOut: break; } }
/* * 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 */ }
/* ---------------- * EndCommand - clean up the destination at end of command * ---------------- */ void EndCommand(const char *commandTag, CommandDest dest) { StringInfoData buf; switch (dest) { case DestRemote: case DestRemoteExecute: if (Gp_role == GP_ROLE_EXECUTE) { sendQEDetails(); pq_beginmessage(&buf, 'C'); pq_sendstring(&buf, commandTag); pq_endmessage(&buf); } else pq_puttextmessage('C', commandTag); break; case DestNone: case DestDebug: case DestSPI: case DestTuplestore: case DestIntoRel: case DestCopyOut: break; } }
/* * Execute the CREATE BARRIER command. Write a BARRIER WAL record and flush the * WAL buffers to disk before returning to the caller. Writing the WAL record * does not guarantee successful completion of the barrier command. */ void ProcessCreateBarrierExecute(const char *id) { StringInfoData buf; if (!IsConnFromCoord()) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("The CREATE BARRIER EXECUTE message is expected to " "arrive from a Coordinator"))); { XLogRecData rdata[1]; XLogRecPtr recptr; rdata[0].data = (char *) id; rdata[0].len = strlen(id) + 1; rdata[0].buffer = InvalidBuffer; rdata[0].next = NULL; recptr = XLogInsert(RM_BARRIER_ID, XLOG_BARRIER_CREATE, rdata); XLogFlush(recptr); } pq_beginmessage(&buf, 'b'); pq_sendstring(&buf, id); pq_endmessage(&buf); pq_flush(); }
static void SendResultDescriptionMessage(AttributeDefinition *attrs, int natts) { int proto = PG_PROTOCOL_MAJOR(FrontendProtocol); int i; StringInfoData buf; pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */ for (i = 0; i < natts; ++i) { pq_sendstring(&buf, attrs[i].name); /* column ID info appears in protocol 3.0 and up */ if (proto >= 3) { pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); } /* If column is a domain, send the base type and typmod instead */ pq_sendint(&buf, attrs[i].typid, sizeof(Oid)); pq_sendint(&buf, attrs[i].typlen, sizeof(int16)); /* typmod appears in protocol 2.0 and up */ if (proto >= 2) pq_sendint(&buf, attrs[i].typmod, sizeof(int32)); /* format info appears in protocol 3.0 and up */ if (proto >= 3) pq_sendint(&buf, 0, 2); } pq_endmessage(&buf); }
/* * SendRowDescriptionMessage --- send a RowDescription message to the frontend * * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL() * or some similar function; it does not contain a full set of fields. * The targetlist will be NIL when executing a utility function that does * not have a plan. If the targetlist isn't NIL then it is a Query node's * targetlist; it is up to us to ignore resjunk columns in it. The formats[] * array pointer might be NULL (if we are doing Describe on a prepared stmt); * send zeroes for the format codes in that case. */ void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats) { Form_pg_attribute *attrs = typeinfo->attrs; int natts = typeinfo->natts; int proto = PG_PROTOCOL_MAJOR(FrontendProtocol); int i; StringInfoData buf; ListCell *tlist_item = list_head(targetlist); pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */ for (i = 0; i < natts; ++i) { Oid atttypid = attrs[i]->atttypid; int32 atttypmod = attrs[i]->atttypmod; pq_sendstring(&buf, NameStr(attrs[i]->attname)); /* column ID info appears in protocol 3.0 and up */ if (proto >= 3) { /* Do we have a non-resjunk tlist item? */ while (tlist_item && ((TargetEntry *) lfirst(tlist_item))->resjunk) tlist_item = lnext(tlist_item); if (tlist_item) { TargetEntry *tle = (TargetEntry *) lfirst(tlist_item); pq_sendint(&buf, tle->resorigtbl, 4); pq_sendint(&buf, tle->resorigcol, 2); tlist_item = lnext(tlist_item); } else { /* No info available, so send zeroes */ pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); } } /* If column is a domain, send the base type and typmod instead */ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); pq_sendint(&buf, (int) atttypid, sizeof(atttypid)); pq_sendint(&buf, attrs[i]->attlen, sizeof(attrs[i]->attlen)); /* typmod appears in protocol 2.0 and up */ if (proto >= 2) pq_sendint(&buf, atttypmod, sizeof(atttypmod)); /* format info appears in protocol 3.0 and up */ if (proto >= 3) { if (formats) pq_sendint(&buf, formats[i], 2); else pq_sendint(&buf, 0, 2); } } pq_endmessage(&buf); }
/* * Write COMMIT to the output stream. */ static void pglogical_write_commit(StringInfo out, PGLogicalOutputData *data, ReorderBufferTXN *txn, XLogRecPtr commit_lsn) { uint8 flags = 0; if (txn->xact_action == XLOG_XACT_COMMIT) flags = PGLOGICAL_COMMIT; else if (txn->xact_action == XLOG_XACT_PREPARE) flags = PGLOGICAL_PREPARE; else if (txn->xact_action == XLOG_XACT_COMMIT_PREPARED) flags = PGLOGICAL_COMMIT_PREPARED; else if (txn->xact_action == XLOG_XACT_ABORT_PREPARED) flags = PGLOGICAL_ABORT_PREPARED; else Assert(false); if (flags == PGLOGICAL_COMMIT || flags == PGLOGICAL_PREPARE) { if (MtmIsFilteredTxn) { return; } } else { csn_t csn = MtmTransactionSnapshot(txn->xid); bool isRecovery = MtmIsRecoveredNode(MtmReplicationNodeId); /* * INVALID_CSN means replicated transaction (transaction initiated by some other nodes). * We do not need to send such transactions unless we perform recovery */ if (csn == INVALID_CSN && !isRecovery) { return; } if (MtmRecoveryCaughtUp(MtmReplicationNodeId, txn->end_lsn)) { MTM_LOG1("wal-sender complete recovery of node %d at LSN(commit %lx, end %lx, log %lx) in transaction %s event %d", MtmReplicationNodeId, commit_lsn, txn->end_lsn, GetXLogInsertRecPtr(), txn->gid, flags); flags |= PGLOGICAL_CAUGHT_UP; } } pq_sendbyte(out, 'C'); /* sending COMMIT */ MTM_LOG2("PGLOGICAL_SEND commit: event=%d, gid=%s, commit_lsn=%lx, txn->end_lsn=%lx, xlog=%lx", flags, txn->gid, commit_lsn, txn->end_lsn, GetXLogInsertRecPtr()); /* send the flags field */ pq_sendbyte(out, flags); pq_sendbyte(out, MtmNodeId); /* send fixed fields */ pq_sendint64(out, commit_lsn); pq_sendint64(out, txn->end_lsn); pq_sendint64(out, txn->commit_time); if (txn->xact_action == XLOG_XACT_COMMIT_PREPARED) { pq_sendint64(out, MtmGetTransactionCSN(txn->xid)); } if (txn->xact_action != XLOG_XACT_COMMIT) { pq_sendstring(out, txn->gid); } }
/* * Write relation attributes to the stream. */ static void logicalrep_write_attrs(StringInfo out, Relation rel) { TupleDesc desc; int i; uint16 nliveatts = 0; Bitmapset *idattrs = NULL; bool replidentfull; desc = RelationGetDescr(rel); /* send number of live attributes */ for (i = 0; i < desc->natts; i++) { if (TupleDescAttr(desc, i)->attisdropped) continue; nliveatts++; } pq_sendint(out, nliveatts, 2); /* fetch bitmap of REPLICATION IDENTITY attributes */ replidentfull = (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL); if (!replidentfull) idattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY); /* send the attributes */ for (i = 0; i < desc->natts; i++) { Form_pg_attribute att = TupleDescAttr(desc, i); uint8 flags = 0; if (att->attisdropped) continue; /* REPLICA IDENTITY FULL means all columns are sent as part of key. */ if (replidentfull || bms_is_member(att->attnum - FirstLowInvalidHeapAttributeNumber, idattrs)) flags |= LOGICALREP_IS_REPLICA_IDENTITY; pq_sendbyte(out, flags); /* attribute name */ pq_sendstring(out, NameStr(att->attname)); /* attribute type id */ pq_sendint(out, (int) att->atttypid, sizeof(att->atttypid)); /* attribute mode */ pq_sendint(out, att->atttypmod, sizeof(att->atttypmod)); } bms_free(idattrs); }
Datum country_send(PG_FUNCTION_ARGS) { country c = PG_GETARG_UINT8(0); StringInfoData buf; pq_begintypsend(&buf); pq_sendstring(&buf, country_to_str(c)); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); }
/* * Write ORIGIN to the output stream. */ void logicalrep_write_origin(StringInfo out, const char *origin, XLogRecPtr origin_lsn) { pq_sendbyte(out, 'O'); /* ORIGIN */ /* fixed fields */ pq_sendint64(out, origin_lsn); /* origin string */ pq_sendstring(out, origin); }
static void send_buffer() { if (buffer_len > 0) { StringInfoData msgbuf; char *cursor = buffer; while (--buffer_len > 0) { if (*cursor == '\0') *cursor = '\n'; cursor++; } if (*cursor != '\0') ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("internal error"), errdetail("Wrong message format detected"))); pq_beginmessage(&msgbuf, 'N'); if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY); pq_sendstring(&msgbuf, buffer); pq_sendbyte(&msgbuf, '\0'); } else { *cursor++ = '\n'; *cursor = '\0'; pq_sendstring(&msgbuf, buffer); } pq_endmessage(&msgbuf); pq_flush(); } }
/* * Write the namespace name or empty string for pg_catalog (to save space). */ static void logicalrep_write_namespace(StringInfo out, Oid nspid) { if (nspid == PG_CATALOG_NAMESPACE) pq_sendbyte(out, '\0'); else { char *nspname = get_namespace_name(nspid); if (nspname == NULL) elog(ERROR, "cache lookup failed for namespace %u", nspid); pq_sendstring(out, nspname); } }
/* * Write relation description to the output stream. */ void logicalrep_write_rel(StringInfo out, Relation rel) { char *relname; pq_sendbyte(out, 'R'); /* sending RELATION */ /* use Oid as relation identifier */ pq_sendint(out, RelationGetRelid(rel), 4); /* send qualified relation name */ logicalrep_write_namespace(out, RelationGetNamespace(rel)); relname = RelationGetRelationName(rel); pq_sendstring(out, relname); /* send replica identity */ pq_sendbyte(out, rel->rd_rel->relreplident); /* send the attribute info */ logicalrep_write_attrs(out, rel); }
/* * Mark the completion of an on-going barrier. We must have remembered the * barrier ID when we received the CREATE BARRIER PREPARE command */ void ProcessCreateBarrierEnd(const char *id) { StringInfoData buf; if (!IS_PGXC_COORDINATOR || !IsConnFromCoord()) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("The CREATE BARRIER END message is expected to " "arrive at a Coordinator from another Coordinator"))); LWLockRelease(BarrierLock); pq_beginmessage(&buf, 'b'); pq_sendstring(&buf, id); pq_endmessage(&buf); pq_flush(); /* * TODO Stop the timer */ }
/* * Prepare ourselves for an incoming BARRIER. We must disable all new 2PC * commits and let the ongoing commits to finish. We then remember the * barrier id (so that it can be matched with the final END message) and * tell the driving Coordinator to proceed with the next step. * * A simple way to implement this is to grab a lock in an exclusive mode * while all other backend starting a 2PC will grab the lock in shared * mode. So as long as we hold the exclusive lock, no other backend start a * new 2PC and there can not be any 2PC in-progress. This technique would * rely on assumption that an exclusive lock requester is not starved by * share lock requesters. * * Note: To ensure that the 2PC are not blocked for a long time, we should * set a timeout. The lock should be release after the timeout and the * barrier should be canceled. */ void ProcessCreateBarrierPrepare(const char *id) { StringInfoData buf; if (!IS_PGXC_COORDINATOR || !IsConnFromCoord()) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("The CREATE BARRIER PREPARE message is expected to " "arrive at a Coordinator from another Coordinator"))); LWLockAcquire(BarrierLock, LW_EXCLUSIVE); pq_beginmessage(&buf, 'b'); pq_sendstring(&buf, id); pq_endmessage(&buf); pq_flush(); /* * TODO Start a timer to terminate the pending barrier after a specified * timeout */ }
/* * Send description for each column when using v2 protocol */ static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats) { int natts = typeinfo->natts; int i; for (i = 0; i < natts; ++i) { Form_pg_attribute att = TupleDescAttr(typeinfo, i); Oid atttypid = att->atttypid; int32 atttypmod = att->atttypmod; /* If column is a domain, send the base type and typmod instead */ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); pq_sendstring(buf, NameStr(att->attname)); /* column ID only info appears in protocol 3.0 and up */ pq_sendint32(buf, atttypid); pq_sendint16(buf, att->attlen); pq_sendint32(buf, atttypmod); /* format info only appears in protocol 3.0 and up */ } }
/* * Write type info to the output stream. * * This function will always write base type info. */ void logicalrep_write_typ(StringInfo out, Oid typoid) { Oid basetypoid = getBaseType(typoid); HeapTuple tup; Form_pg_type typtup; pq_sendbyte(out, 'Y'); /* sending TYPE */ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(basetypoid)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for type %u", basetypoid); typtup = (Form_pg_type) GETSTRUCT(tup); /* use Oid as relation identifier */ pq_sendint(out, typoid, 4); /* send qualified type name */ logicalrep_write_namespace(out, typtup->typnamespace); pq_sendstring(out, NameStr(typtup->typname)); ReleaseSysCache(tup); }
/* * Write a tuple to the outputstream, in the most efficient format possible. */ static void logicalrep_write_tuple(StringInfo out, Relation rel, HeapTuple tuple) { TupleDesc desc; Datum values[MaxTupleAttributeNumber]; bool isnull[MaxTupleAttributeNumber]; int i; uint16 nliveatts = 0; desc = RelationGetDescr(rel); 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)); heap_deform_tuple(tuple, desc, values, isnull); /* Write the values */ for (i = 0; i < desc->natts; i++) { HeapTuple typtup; Form_pg_type typclass; Form_pg_attribute att = desc->attrs[i]; char *outputstr; int len; /* 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); pq_sendbyte(out, 't'); /* 'text' data follows */ outputstr = OidOutputFunctionCall(typclass->typoutput, values[i]); len = strlen(outputstr) + 1; /* null terminated */ pq_sendint(out, len, 4); /* length */ pq_sendstring(out, outputstr); /* data */ pfree(outputstr); ReleaseSysCache(typtup); } }
/* * 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 */ }
/* * SendRowDescriptionMessage --- send a RowDescription message to the frontend * * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL() * or some similar function; it does not contain a full set of fields. * The targetlist will be NIL when executing a utility function that does * not have a plan. If the targetlist isn't NIL then it is a Query node's * targetlist; it is up to us to ignore resjunk columns in it. The formats[] * array pointer might be NULL (if we are doing Describe on a prepared stmt); * send zeroes for the format codes in that case. */ void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats) { Form_pg_attribute *attrs = typeinfo->attrs; int natts = typeinfo->natts; int proto = PG_PROTOCOL_MAJOR(FrontendProtocol); int i; StringInfoData buf; ListCell *tlist_item = list_head(targetlist); pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */ for (i = 0; i < natts; ++i) { Oid atttypid = attrs[i]->atttypid; int32 atttypmod = attrs[i]->atttypmod; pq_sendstring(&buf, NameStr(attrs[i]->attname)); #ifdef PGXC /* * Send the type name from a Postgres-XC backend node. * This preserves from OID inconsistencies as architecture is shared nothing. */ if (IsConnFromCoord()) { char *typenamespace = NULL; char *typename; Oid nspid; nspid = get_typenamespace(atttypid); if (OidIsValid(nspid) && nspid != PG_CATALOG_NAMESPACE) { typenamespace = get_namespace_name(nspid); } typename = get_typename(atttypid); pq_sendstring(&buf, quote_qualified_identifier(typenamespace, typename)); } #endif /* column ID info appears in protocol 3.0 and up */ if (proto >= 3) { /* Do we have a non-resjunk tlist item? */ while (tlist_item && ((TargetEntry *) lfirst(tlist_item))->resjunk) tlist_item = lnext(tlist_item); if (tlist_item) { TargetEntry *tle = (TargetEntry *) lfirst(tlist_item); pq_sendint(&buf, tle->resorigtbl, 4); pq_sendint(&buf, tle->resorigcol, 2); tlist_item = lnext(tlist_item); } else { /* No info available, so send zeroes */ pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); } } /* If column is a domain, send the base type and typmod instead */ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); pq_sendint(&buf, (int) atttypid, sizeof(atttypid)); pq_sendint(&buf, attrs[i]->attlen, sizeof(attrs[i]->attlen)); /* typmod appears in protocol 2.0 and up */ if (proto >= 2) pq_sendint(&buf, atttypmod, sizeof(atttypmod)); /* format info appears in protocol 3.0 and up */ if (proto >= 3) { if (formats) pq_sendint(&buf, formats[i], 2); else pq_sendint(&buf, 0, 2); } } 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))); } } }