Ejemplo n.º 1
0
static Source *
CreateRemoteSource(const char *path, TupleDesc desc)
{
	RemoteSource *self = (RemoteSource *) palloc0(sizeof(RemoteSource));
	self->base.close = (SourceCloseProc) RemoteSourceClose;

	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
	{
		/* new way */
		StringInfoData	buf;
		int16			format;
		int				nattrs;
		int				i;

		self->base.read = (SourceReadProc) RemoteSourceRead;

		/* count valid fields */
		for (nattrs = 0, i = 0; i < desc->natts; i++)
		{
			if (desc->attrs[i]->attisdropped)
				continue;
			nattrs++;
		}

		format = (IsBinaryCopy() ? 1 : 0);
		pq_beginmessage(&buf, 'G');
		pq_sendbyte(&buf, format);		/* overall format */
		pq_sendint(&buf, nattrs, 2);
		for (i = 0; i < nattrs; i++)
			pq_sendint(&buf, format, 2);		/* per-column formats */
		pq_endmessage(&buf);
		self->buffer = makeStringInfo();
	}
	else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
	{
		self->base.read = (SourceReadProc) RemoteSourceReadOld;

		/* old way */
		if (IsBinaryCopy())
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			errmsg("COPY BINARY is not supported to stdout or from stdin")));
		pq_putemptymessage('G');
	}
	else
	{
		self->base.read = (SourceReadProc) RemoteSourceReadOld;

		/* very old way */
		if (IsBinaryCopy())
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			errmsg("COPY BINARY is not supported to stdout or from stdin")));
		pq_putemptymessage('D');
	}
	/* We *must* flush here to ensure FE knows it can send. */
	pq_flush();

	return (Source *) self;
}
Ejemplo n.º 2
0
Archivo: hstore_io.c Proyecto: d/gpdb
Datum
hstore_send(PG_FUNCTION_ARGS)
{
	HStore	   *in = PG_GETARG_HS(0);
	int			i;
	int			count = HS_COUNT(in);
	char	   *base = STRPTR(in);
	HEntry	   *entries = ARRPTR(in);
	StringInfoData buf;

	pq_begintypsend(&buf);

	pq_sendint(&buf, count, 4);

	for (i = 0; i < count; i++)
	{
		int32		keylen = HS_KEYLEN(entries, i);

		pq_sendint(&buf, keylen, 4);
		pq_sendtext(&buf, HS_KEY(entries, base, i), keylen);
		if (HS_VALISNULL(entries, i))
		{
			pq_sendint(&buf, -1, 4);
		}
		else
		{
			int32		vallen = HS_VALLEN(entries, i);

			pq_sendint(&buf, vallen, 4);
			pq_sendtext(&buf, HS_VAL(entries, base, i), vallen);
		}
	}

	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 3
0
/*
 * 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);
}
Ejemplo n.º 4
0
/*
 * 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);
}
Ejemplo n.º 5
0
static void
putEndLocationReply(XLogRecPtr *endLocation)
{
	StringInfoData buf;
	
	pq_beginmessage(&buf, 's');
	pq_sendint(&buf, endLocation->xlogid, 4);
	pq_sendint(&buf, endLocation->xrecoff, 4);
	pq_endmessage(&buf);
	pq_flush();
}
Ejemplo n.º 6
0
Archivo: dest.c Proyecto: 50wu/gpdb
/*
 * 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);
}
Ejemplo n.º 7
0
/*
 *		tintervalsend			- converts tinterval to binary format
 */
Datum
tintervalsend(PG_FUNCTION_ARGS)
{
	TimeInterval tinterval = PG_GETARG_TIMEINTERVAL(0);
	StringInfoData buf;

	pq_begintypsend(&buf);
	pq_sendint(&buf, tinterval->status, sizeof(tinterval->status));
	pq_sendint(&buf, tinterval->data[0], sizeof(tinterval->data[0]));
	pq_sendint(&buf, tinterval->data[1], sizeof(tinterval->data[1]));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 8
0
Archivo: dest.c Proyecto: LJoNe/gpdb
/*
 * 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);
}
Ejemplo n.º 9
0
Datum
ipaddr_send(PG_FUNCTION_ARGS)
{
    IP_P arg1 = PG_GETARG_IP_P(0);
    StringInfoData buf;
	IP ip;
	int af = ip_unpack(arg1, &ip);

    pq_begintypsend(&buf);
	pq_sendbyte(&buf, af);
	pq_sendbyte(&buf, ip_maxbits(af));
	pq_sendbyte(&buf, 1);
	pq_sendbyte(&buf, ip_sizeof(af));

	switch (af)
	{
		case PGSQL_AF_INET:
			pq_sendint(&buf, ip.ip4, sizeof(IP4));
			break;

		case PGSQL_AF_INET6:
			pq_sendint64(&buf, ip.ip6.bits[0]);
			pq_sendint64(&buf, ip.ip6.bits[1]);
			break;
	}

    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
 * 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);
}
Ejemplo n.º 11
0
Archivo: xlogloc.c Proyecto: 50wu/gpdb
/*
 *		gpxloglocsend			- converts xlog location to binary format
 */
Datum
gpxloglocsend(PG_FUNCTION_ARGS)
{
	XLogRecPtr *xlogLoc = PG_GETARG_XLOGLOC(0);
	uint32		send_xlogid;
	uint32		send_xrecoff;
	StringInfoData buf;

	send_xlogid = xlogLoc->xlogid;
	send_xrecoff = xlogLoc->xrecoff;

	pq_begintypsend(&buf);
	pq_sendint(&buf, send_xlogid, sizeof(send_xlogid));
	pq_sendint(&buf, send_xrecoff, sizeof(send_xrecoff));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 12
0
Archivo: dest.c Proyecto: ricky-wu/gpdb
/* ----------------
 *		AddQEWriterTransactionInfo - Add QE writer transction information.
 * ----------------
 */
void
AddQEWriterTransactionInfo(StringInfo buf)
{
	DistributedTransactionId	QEDistributedTransactionId;
	CommandId					QECommandId;
	bool						QEDirty;

	TransactionInformationQEWriter(&QEDistributedTransactionId, &QECommandId, &QEDirty);

	elog(DEBUG5,"QEWriterTransactionInfo: (DistributedTransactionId = %u, CommandId = %u, and Dirty = %s)",
	     QEDistributedTransactionId, QECommandId, (QEDirty ? "true" : "false"));

	pq_sendint(buf, QEDistributedTransactionId, 4);
	pq_sendint(buf, QECommandId, 4);
	pq_sendbyte(buf, (QEDirty ? 'T' : 'F'));
}
Ejemplo n.º 13
0
/*
 * 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);
}
Ejemplo n.º 14
0
/*
 * Write UPDATE to the output stream.
 */
void
logicalrep_write_update(StringInfo out, Relation rel, HeapTuple oldtuple,
						HeapTuple newtuple)
{
	pq_sendbyte(out, 'U');		/* action UPDATE */

	Assert(rel->rd_rel->relreplident == REPLICA_IDENTITY_DEFAULT ||
		   rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL ||
		   rel->rd_rel->relreplident == REPLICA_IDENTITY_INDEX);

	/* use Oid as relation identifier */
	pq_sendint(out, RelationGetRelid(rel), 4);

	if (oldtuple != NULL)
	{
		if (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
			pq_sendbyte(out, 'O');	/* old tuple follows */
		else
			pq_sendbyte(out, 'K');	/* old key follows */
		logicalrep_write_tuple(out, rel, oldtuple);
	}

	pq_sendbyte(out, 'N');		/* new tuple follows */
	logicalrep_write_tuple(out, rel, newtuple);
}
Ejemplo n.º 15
0
/**
 *  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));
}
Ejemplo n.º 16
0
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);
}
/*
 * Write BEGIN to the output stream.
 */
static void
pglogical_write_begin(StringInfo out, PGLogicalOutputData *data,
					  ReorderBufferTXN *txn)
{
	bool isRecovery = MtmIsRecoveredNode(MtmReplicationNodeId);
	csn_t csn = MtmTransactionSnapshot(txn->xid);
	MTM_LOG2("%d: pglogical_write_begin XID=%d node=%d CSN=%ld recovery=%d", MyProcPid, txn->xid, MtmReplicationNodeId, csn, isRecovery);
	
	if (csn == INVALID_CSN && !isRecovery) { 
		MtmIsFilteredTxn = true;
	} else { 
		pq_sendbyte(out, 'B');		/* BEGIN */
		pq_sendint(out, MtmNodeId, 4);
		pq_sendint(out, isRecovery ? InvalidTransactionId : txn->xid, 4);
		pq_sendint64(out, csn);
		MtmIsFilteredTxn = false;
	}
}
Ejemplo n.º 18
0
Archivo: int.c Proyecto: colinet/sqlix
/*
 *		int2send			- converts int2 to binary format
 */
datum_t int2send(PG_FUNC_ARGS)
{
	int16 arg1 = ARG_INT16(0);
	struct string buf;

	pq_begintypsend(&buf);
	pq_sendint(&buf, arg1, sizeof(int16));
	RET_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 19
0
/*
 *		tidsend			- converts tid to binary format
 */
Datum
tidsend(PG_FUNCTION_ARGS)
{
	ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
	BlockId		blockId;
	BlockNumber blockNumber;
	OffsetNumber offsetNumber;
	StringInfoData buf;

	blockId = &(itemPtr->ip_blkid);
	blockNumber = BlockIdGetBlockNumber(blockId);
	offsetNumber = itemPtr->ip_posid;

	pq_begintypsend(&buf);
	pq_sendint(&buf, blockNumber, sizeof(blockNumber));
	pq_sendint(&buf, offsetNumber, sizeof(offsetNumber));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 20
0
Archivo: oid.c Proyecto: 50wu/gpdb
/*
 *		oidsend			- converts oid to binary format
 */
Datum
oidsend(PG_FUNCTION_ARGS)
{
	Oid			arg1 = PG_GETARG_OID(0);
	StringInfoData buf;

	pq_begintypsend(&buf);
	pq_sendint(&buf, arg1, sizeof(Oid));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 21
0
/*
 *		abstimesend			- converts abstime to binary format
 */
Datum
abstimesend(PG_FUNCTION_ARGS)
{
	AbsoluteTime time = PG_GETARG_ABSOLUTETIME(0);
	StringInfoData buf;

	pq_begintypsend(&buf);
	pq_sendint(&buf, time, sizeof(time));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 22
0
/*
 *		reltimesend			- converts reltime to binary format
 */
Datum
reltimesend(PG_FUNCTION_ARGS)
{
	RelativeTime time = PG_GETARG_RELATIVETIME(0);
	StringInfoData buf;

	pq_begintypsend(&buf);
	pq_sendint(&buf, time, sizeof(time));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 23
0
/*
 *		cash_send			- converts cash to binary format
 */
Datum
cash_send(PG_FUNCTION_ARGS)
{
    Cash		arg1 = PG_GETARG_CASH(0);
    StringInfoData buf;

    pq_begintypsend(&buf);
    pq_sendint(&buf, arg1, sizeof(Cash));
    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 24
0
/*
 * Write BEGIN to the output stream.
 */
void
logicalrep_write_begin(StringInfo out, ReorderBufferTXN *txn)
{
	pq_sendbyte(out, 'B');		/* BEGIN */

	/* fixed fields */
	pq_sendint64(out, txn->final_lsn);
	pq_sendint64(out, txn->commit_time);
	pq_sendint(out, txn->xid, 4);
}
Ejemplo n.º 25
0
/*
 *		xidsend			- converts xid to binary format
 */
Datum
xidsend(PG_FUNCTION_ARGS)
{
    TransactionId arg1 = PG_GETARG_TRANSACTIONID(0);
    StringInfoData buf;

    pq_begintypsend(&buf);
    pq_sendint(&buf, arg1, sizeof(arg1));
    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 26
0
/*
 *		cidsend			- converts cid to binary format
 */
Datum
cidsend(PG_FUNCTION_ARGS)
{
    CommandId	arg1 = PG_GETARG_COMMANDID(0);
    StringInfoData buf;

    pq_begintypsend(&buf);
    pq_sendint(&buf, arg1, sizeof(arg1));
    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 27
0
/*
 *		int2send			- converts int2 to binary format
 */
Datum
int2send(PG_FUNCTION_ARGS)
{
	int16		arg1 = PG_GETARG_INT16(0);
	StringInfoData buf;

	pq_begintypsend(&buf);
	pq_sendint(&buf, arg1, sizeof(int16));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
Ejemplo n.º 28
0
Archivo: cdbsreh.c Proyecto: LJoNe/gpdb
/*
 * SendNumRowsRejected
 *
 * Using this function the QE sends back to the client QD the number 
 * of rows that were rejected in this last data load in SREH mode.
 */
void SendNumRowsRejected(int numrejected)
{
	StringInfoData buf;
	
	if (Gp_role != GP_ROLE_EXECUTE)
		elog(FATAL, "SendNumRowsRejected: called outside of execute context.");

	pq_beginmessage(&buf, 'j'); /* 'j' is the msg code for rejected records */
	pq_sendint(&buf, numrejected, 4);
	pq_endmessage(&buf);	
}
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);
}
Ejemplo n.º 30
0
/*
 * START_REPLICATION
 */
static void
StartReplication(StartReplicationCmd *cmd)
{
    StringInfoData buf;

    /*
     * Let postmaster know that we're streaming. Once we've declared us as a
     * WAL sender process, postmaster will let us outlive the bgwriter and
     * kill us last in the shutdown sequence, so we get a chance to stream all
     * remaining WAL at shutdown, including the shutdown checkpoint. Note that
     * there's no going back, and we mustn't write any WAL records after this.
     */
    MarkPostmasterChildWalSender();
    SendPostmasterSignal(PMSIGNAL_ADVANCE_STATE_MACHINE);

    /*
     * 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")));

    /*
     * When we first start replication the standby will be behind the primary.
     * For some applications, for example, synchronous replication, it is
     * important to have a clear state for this initial catchup mode, so we
     * can trigger actions when we change streaming state later. We may stay
     * in this state for a long time, which is exactly why we want to be able
     * to monitor whether or not we are still here.
     */
    WalSndSetState(WALSNDSTATE_CATCHUP);

    /* Send a CopyBothResponse message, and start streaming */
    pq_beginmessage(&buf, 'W');
    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 = cmd->startpoint;
}