Datum
pg_control_recovery(PG_FUNCTION_ARGS)
{
	Datum		values[5];
	bool		nulls[5];
	TupleDesc	tupdesc;
	HeapTuple	htup;
	ControlFileData *ControlFile;
	bool		crc_ok;

	/*
	 * Construct a tuple descriptor for the result row.  This must match this
	 * function's pg_proc entry!
	 */
	tupdesc = CreateTemplateTupleDesc(5);
	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "min_recovery_end_lsn",
					   LSNOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "min_recovery_end_timeline",
					   INT4OID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "backup_start_lsn",
					   LSNOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "backup_end_lsn",
					   LSNOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "end_of_backup_record_required",
					   BOOLOID, -1, 0);
	tupdesc = BlessTupleDesc(tupdesc);

	/* read the control file */
	ControlFile = get_controlfile(DataDir, NULL, &crc_ok);
	if (!crc_ok)
		ereport(ERROR,
				(errmsg("calculated CRC checksum does not match value stored in file")));

	values[0] = LSNGetDatum(ControlFile->minRecoveryPoint);
	nulls[0] = false;

	values[1] = Int32GetDatum(ControlFile->minRecoveryPointTLI);
	nulls[1] = false;

	values[2] = LSNGetDatum(ControlFile->backupStartPoint);
	nulls[2] = false;

	values[3] = LSNGetDatum(ControlFile->backupEndPoint);
	nulls[3] = false;

	values[4] = BoolGetDatum(ControlFile->backupEndRequired);
	nulls[4] = false;

	htup = heap_form_tuple(tupdesc, values, nulls);

	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
}
Exemple #2
0
/*
 * Perform output plugin write into tuplestore.
 */
static void
LogicalOutputWrite(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
				   bool last_write)
{
	Datum		values[3];
	bool		nulls[3];
	DecodingOutputState *p;

	/* SQL Datums can only be of a limited length... */
	if (ctx->out->len > MaxAllocSize - VARHDRSZ)
		elog(ERROR, "too much output for sql interface");

	p = (DecodingOutputState *) ctx->output_writer_private;

	memset(nulls, 0, sizeof(nulls));
	values[0] = LSNGetDatum(lsn);
	values[1] = TransactionIdGetDatum(xid);

	/*
	 * Assert ctx->out is in database encoding when we're writing textual
	 * output.
	 */
	if (!p->binary_output)
		Assert(pg_verify_mbstr(GetDatabaseEncoding(),
							   ctx->out->data, ctx->out->len,
							   false));

	/* ick, but cstring_to_text_with_len works for bytea perfectly fine */
	values[2] = PointerGetDatum(
								cstring_to_text_with_len(ctx->out->data, ctx->out->len));

	tuplestore_putvalues(p->tupstore, p->tupdesc, values, nulls);
	p->returned_rows++;
}
Exemple #3
0
/*
 * SQL function for creating a new logical replication slot.
 */
Datum
pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
{
	Name		name = PG_GETARG_NAME(0);
	Name		plugin = PG_GETARG_NAME(1);

	LogicalDecodingContext *ctx = NULL;

	TupleDesc	tupdesc;
	HeapTuple	tuple;
	Datum		result;
	Datum		values[2];
	bool		nulls[2];

	Assert(!MyReplicationSlot);

	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
		elog(ERROR, "return type must be a row type");

	check_permissions();

	CheckLogicalDecodingRequirements();

	/*
	 * Acquire a logical decoding slot, this will check for conflicting names.
	 * Initially create it as ephemeral - that allows us to nicely handle
	 * errors during initialization because it'll get dropped if this
	 * transaction fails. We'll make it persistent at the end.
	 */
	ReplicationSlotCreate(NameStr(*name), true, RS_EPHEMERAL);

	/*
	 * Create logical decoding context, to build the initial snapshot.
	 */
	ctx = CreateInitDecodingContext(
									NameStr(*plugin), NIL,
									logical_read_local_xlog_page, NULL, NULL);

	/* build initial snapshot, might take a while */
	DecodingContextFindStartpoint(ctx);

	values[0] = CStringGetTextDatum(NameStr(MyReplicationSlot->data.name));
	values[1] = LSNGetDatum(MyReplicationSlot->data.confirmed_flush);

	/* don't need the decoding context anymore */
	FreeDecodingContext(ctx);

	memset(nulls, 0, sizeof(nulls));

	tuple = heap_form_tuple(tupdesc, values, nulls);
	result = HeapTupleGetDatum(tuple);

	/* ok, slot is now fully created, mark it as persistent */
	ReplicationSlotPersist();
	ReplicationSlotRelease();

	PG_RETURN_DATUM(result);
}
Exemple #4
0
/*
 * SQL function for creating a new physical (streaming replication)
 * replication slot.
 */
Datum
pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
{
	Name		name = PG_GETARG_NAME(0);
	bool 		immediately_reserve = PG_GETARG_BOOL(1);
	Datum		values[2];
	bool		nulls[2];
	TupleDesc	tupdesc;
	HeapTuple	tuple;
	Datum		result;

	Assert(!MyReplicationSlot);

	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
		elog(ERROR, "return type must be a row type");

	check_permissions();

	CheckSlotRequirements();

	/* acquire replication slot, this will check for conflicting names */
	ReplicationSlotCreate(NameStr(*name), false, RS_PERSISTENT);

	values[0] = NameGetDatum(&MyReplicationSlot->data.name);
	nulls[0] = false;

	if (immediately_reserve)
	{
		/* Reserve WAL as the user asked for it */
		ReplicationSlotReserveWal();

		/* Write this slot to disk */
		ReplicationSlotMarkDirty();
		ReplicationSlotSave();

		values[1] = LSNGetDatum(MyReplicationSlot->data.restart_lsn);
		nulls[1] = false;
	}
	else
	{
		values[0] = NameGetDatum(&MyReplicationSlot->data.name);
		nulls[1] = true;
	}

	tuple = heap_form_tuple(tupdesc, values, nulls);
	result = HeapTupleGetDatum(tuple);

	ReplicationSlotRelease();

	PG_RETURN_DATUM(result);
}
/*
 * Update the state of a subscription table.
 */
void
UpdateSubscriptionRelState(Oid subid, Oid relid, char state,
						   XLogRecPtr sublsn)
{
	Relation	rel;
	HeapTuple	tup;
	bool		nulls[Natts_pg_subscription_rel];
	Datum		values[Natts_pg_subscription_rel];
	bool		replaces[Natts_pg_subscription_rel];

	LockSharedObject(SubscriptionRelationId, subid, 0, AccessShareLock);

	rel = table_open(SubscriptionRelRelationId, RowExclusiveLock);

	/* Try finding existing mapping. */
	tup = SearchSysCacheCopy2(SUBSCRIPTIONRELMAP,
							  ObjectIdGetDatum(relid),
							  ObjectIdGetDatum(subid));
	if (!HeapTupleIsValid(tup))
		elog(ERROR, "subscription table %u in subscription %u does not exist",
			 relid, subid);

	/* Update the tuple. */
	memset(values, 0, sizeof(values));
	memset(nulls, false, sizeof(nulls));
	memset(replaces, false, sizeof(replaces));

	replaces[Anum_pg_subscription_rel_srsubstate - 1] = true;
	values[Anum_pg_subscription_rel_srsubstate - 1] = CharGetDatum(state);

	replaces[Anum_pg_subscription_rel_srsublsn - 1] = true;
	if (sublsn != InvalidXLogRecPtr)
		values[Anum_pg_subscription_rel_srsublsn - 1] = LSNGetDatum(sublsn);
	else
		nulls[Anum_pg_subscription_rel_srsublsn - 1] = true;

	tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls,
							replaces);

	/* Update the catalog. */
	CatalogTupleUpdate(rel, &tup->t_self, tup);

	/* Cleanup. */
	table_close(rel, NoLock);
}
/*
 * Add new state record for a subscription table.
 */
void
AddSubscriptionRelState(Oid subid, Oid relid, char state,
						XLogRecPtr sublsn)
{
	Relation	rel;
	HeapTuple	tup;
	bool		nulls[Natts_pg_subscription_rel];
	Datum		values[Natts_pg_subscription_rel];

	LockSharedObject(SubscriptionRelationId, subid, 0, AccessShareLock);

	rel = table_open(SubscriptionRelRelationId, RowExclusiveLock);

	/* Try finding existing mapping. */
	tup = SearchSysCacheCopy2(SUBSCRIPTIONRELMAP,
							  ObjectIdGetDatum(relid),
							  ObjectIdGetDatum(subid));
	if (HeapTupleIsValid(tup))
		elog(ERROR, "subscription table %u in subscription %u already exists",
			 relid, subid);

	/* Form the tuple. */
	memset(values, 0, sizeof(values));
	memset(nulls, false, sizeof(nulls));
	values[Anum_pg_subscription_rel_srsubid - 1] = ObjectIdGetDatum(subid);
	values[Anum_pg_subscription_rel_srrelid - 1] = ObjectIdGetDatum(relid);
	values[Anum_pg_subscription_rel_srsubstate - 1] = CharGetDatum(state);
	if (sublsn != InvalidXLogRecPtr)
		values[Anum_pg_subscription_rel_srsublsn - 1] = LSNGetDatum(sublsn);
	else
		nulls[Anum_pg_subscription_rel_srsublsn - 1] = true;

	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);

	/* Insert tuple into catalog. */
	CatalogTupleInsert(rel, tup);

	heap_freetuple(tup);

	/* Cleanup. */
	table_close(rel, NoLock);
}
Exemple #7
0
Datum
pg_control_checkpoint(PG_FUNCTION_ARGS)
{
	Datum				values[19];
	bool				nulls[19];
	TupleDesc			tupdesc;
	HeapTuple			htup;
	ControlFileData	   *ControlFile;
	XLogSegNo			segno;
	char				xlogfilename[MAXFNAMELEN];

	/*
	 * Construct a tuple descriptor for the result row.  This must match this
	 * function's pg_proc entry!
	 */
	tupdesc = CreateTemplateTupleDesc(19, false);
	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "checkpoint_location",
					   LSNOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "prior_location",
					   LSNOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "redo_location",
					   LSNOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "redo_wal_file",
					   TEXTOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "timeline_id",
					   INT4OID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "prev_timeline_id",
					   INT4OID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "full_page_writes",
					   BOOLOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "next_xid",
					   TEXTOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "next_oid",
					   OIDOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "next_multixact_id",
					   XIDOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "next_multi_offset",
					   XIDOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "oldest_xid",
					   XIDOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "oldest_xid_dbid",
					   OIDOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 14, "oldest_active_xid",
					   XIDOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 15, "oldest_multi_xid",
					   XIDOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 16, "oldest_multi_dbid",
					   OIDOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 17, "oldest_commit_ts_xid",
					   XIDOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 18, "newest_commit_ts_xid",
					   XIDOID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber) 19, "checkpoint_time",
					   TIMESTAMPTZOID, -1, 0);
	tupdesc = BlessTupleDesc(tupdesc);

	/* Read the control file. */
	ControlFile = get_controlfile(DataDir, NULL);

	/*
	 * Calculate name of the WAL file containing the latest checkpoint's REDO
	 * start point.
	 */
	XLByteToSeg(ControlFile->checkPointCopy.redo, segno);
	XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno);

	/* Populate the values and null arrays */
	values[0] = LSNGetDatum(ControlFile->checkPoint);
	nulls[0] = false;

	values[1] = LSNGetDatum(ControlFile->prevCheckPoint);
	nulls[1] = false;

	values[2] = LSNGetDatum(ControlFile->checkPointCopy.redo);
	nulls[2] = false;

	values[3] = CStringGetTextDatum(xlogfilename);
	nulls[3] = false;

	values[4] = Int32GetDatum(ControlFile->checkPointCopy.ThisTimeLineID);
	nulls[4] = false;

	values[5] = Int32GetDatum(ControlFile->checkPointCopy.PrevTimeLineID);
	nulls[5] = false;

	values[6] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites);
	nulls[6] = false;

	values[7] = CStringGetTextDatum(psprintf("%u:%u",
								ControlFile->checkPointCopy.nextXidEpoch,
								ControlFile->checkPointCopy.nextXid));
	nulls[7] = false;

	values[8] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid);
	nulls[8] = false;

	values[9] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMulti);
	nulls[9] = false;

	values[10] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMultiOffset);
	nulls[10] = false;

	values[11] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestXid);
	nulls[11] = false;

	values[12] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestXidDB);
	nulls[12] = false;

	values[13] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestActiveXid);
	nulls[13] = false;

	values[14] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestMulti);
	nulls[14] = false;

	values[15] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestMultiDB);
	nulls[15] = false;

	values[16] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestCommitTsXid);
	nulls[16] = false;

	values[17] = TransactionIdGetDatum(ControlFile->checkPointCopy.newestCommitTsXid);
	nulls[17] = false;

	values[18] = TimestampTzGetDatum(
					time_t_to_timestamptz(ControlFile->checkPointCopy.time));
	nulls[18] = false;

	htup = heap_form_tuple(tupdesc, values, nulls);

	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
}
Exemple #8
0
/*
 * pg_stop_backup_v2: finish taking exclusive or nonexclusive on-line backup.
 *
 * Works the same as pg_stop_backup, except for non-exclusive backups it returns
 * the backup label and tablespace map files as text fields in as part of the
 * resultset.
 *
 * Permission checking for this function is managed through the normal
 * GRANT system.
 */
Datum
pg_stop_backup_v2(PG_FUNCTION_ARGS)
{
	ReturnSetInfo  *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
	TupleDesc		tupdesc;
	Tuplestorestate *tupstore;
	MemoryContext	per_query_ctx;
	MemoryContext	oldcontext;
	Datum			values[3];
	bool			nulls[3];

	bool			exclusive = PG_GETARG_BOOL(0);
	XLogRecPtr		stoppoint;

	/* check to see if caller supports us returning a tuplestore */
	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("set-valued function called in context that cannot accept a set")));
	if (!(rsinfo->allowedModes & SFRM_Materialize))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("materialize mode required, but it is not " \
						"allowed in this context")));

	/* Build a tuple descriptor for our result type */
	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
		elog(ERROR, "return type must be a row type");

	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
	oldcontext = MemoryContextSwitchTo(per_query_ctx);

	tupstore = tuplestore_begin_heap(true, false, work_mem);
	rsinfo->returnMode = SFRM_Materialize;
	rsinfo->setResult = tupstore;
	rsinfo->setDesc = tupdesc;

	MemoryContextSwitchTo(oldcontext);

	MemSet(values, 0, sizeof(values));
	MemSet(nulls, 0, sizeof(nulls));

	if (exclusive)
	{
		if (nonexclusive_backup_running)
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("non-exclusive backup in progress"),
					 errhint("did you mean to use pg_stop_backup('f')?")));

		/*
		 * Stop the exclusive backup, and since we're in an exclusive backup
		 * return NULL for both backup_label and tablespace_map.
		 */
		stoppoint = do_pg_stop_backup(NULL, true, NULL);
		exclusive_backup_running = false;

		nulls[1] = true;
		nulls[2] = true;
	}
	else
	{
		if (!nonexclusive_backup_running)
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("non-exclusive backup is not in progress"),
					 errhint("did you mean to use pg_stop_backup('t')?")));

		/*
		 * Stop the non-exclusive backup. Return a copy of the backup
		 * label and tablespace map so they can be written to disk by
		 * the caller.
		 */
		stoppoint = do_pg_stop_backup(label_file->data, true, NULL);
		nonexclusive_backup_running = false;
		cancel_before_shmem_exit(nonexclusive_base_backup_cleanup, (Datum) 0);

		values[1] = CStringGetTextDatum(label_file->data);
		values[2] = CStringGetTextDatum(tblspc_map_file->data);

		/* Free structures allocated in TopMemoryContext */
		pfree(label_file->data);
		pfree(label_file);
		label_file = NULL;
		pfree(tblspc_map_file->data);
		pfree(tblspc_map_file);
		tblspc_map_file = NULL;
	}

	/* Stoppoint is included on both exclusive and nonexclusive backups */
	values[0]  = LSNGetDatum(stoppoint);

	tuplestore_putvalues(tupstore, tupdesc, values, nulls);
	tuplestore_donestoring(typstore);

	return (Datum) 0;
}
Exemple #9
0
/*
 * pg_get_replication_slots - SQL SRF showing active replication slots.
 */
Datum
pg_get_replication_slots(PG_FUNCTION_ARGS)
{
#define PG_GET_REPLICATION_SLOTS_COLS 10
	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
	TupleDesc	tupdesc;
	Tuplestorestate *tupstore;
	MemoryContext per_query_ctx;
	MemoryContext oldcontext;
	int			slotno;

	/* check to see if caller supports us returning a tuplestore */
	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("set-valued function called in context that cannot accept a set")));
	if (!(rsinfo->allowedModes & SFRM_Materialize))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("materialize mode required, but it is not " \
						"allowed in this context")));

	/* Build a tuple descriptor for our result type */
	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
		elog(ERROR, "return type must be a row type");

	/*
	 * We don't require any special permission to see this function's data
	 * because nothing should be sensitive. The most critical being the slot
	 * name, which shouldn't contain anything particularly sensitive.
	 */

	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
	oldcontext = MemoryContextSwitchTo(per_query_ctx);

	tupstore = tuplestore_begin_heap(true, false, work_mem);
	rsinfo->returnMode = SFRM_Materialize;
	rsinfo->setResult = tupstore;
	rsinfo->setDesc = tupdesc;

	MemoryContextSwitchTo(oldcontext);

	for (slotno = 0; slotno < max_replication_slots; slotno++)
	{
		ReplicationSlot *slot = &ReplicationSlotCtl->replication_slots[slotno];
		Datum		values[PG_GET_REPLICATION_SLOTS_COLS];
		bool		nulls[PG_GET_REPLICATION_SLOTS_COLS];

		TransactionId xmin;
		TransactionId catalog_xmin;
		XLogRecPtr	restart_lsn;
		XLogRecPtr	confirmed_flush_lsn;
		pid_t		active_pid;
		Oid			database;
		NameData	slot_name;
		NameData	plugin;
		int			i;

		SpinLockAcquire(&slot->mutex);
		if (!slot->in_use)
		{
			SpinLockRelease(&slot->mutex);
			continue;
		}
		else
		{
			xmin = slot->data.xmin;
			catalog_xmin = slot->data.catalog_xmin;
			database = slot->data.database;
			restart_lsn = slot->data.restart_lsn;
			confirmed_flush_lsn = slot->data.confirmed_flush;
			namecpy(&slot_name, &slot->data.name);
			namecpy(&plugin, &slot->data.plugin);

			active_pid = slot->active_pid;
		}
		SpinLockRelease(&slot->mutex);

		memset(nulls, 0, sizeof(nulls));

		i = 0;
		values[i++] = NameGetDatum(&slot_name);

		if (database == InvalidOid)
			nulls[i++] = true;
		else
			values[i++] = NameGetDatum(&plugin);

		if (database == InvalidOid)
			values[i++] = CStringGetTextDatum("physical");
		else
			values[i++] = CStringGetTextDatum("logical");

		if (database == InvalidOid)
			nulls[i++] = true;
		else
			values[i++] = database;

		values[i++] = BoolGetDatum(active_pid != 0);

		if (active_pid != 0)
			values[i++] = Int32GetDatum(active_pid);
		else
			nulls[i++] = true;

		if (xmin != InvalidTransactionId)
			values[i++] = TransactionIdGetDatum(xmin);
		else
			nulls[i++] = true;

		if (catalog_xmin != InvalidTransactionId)
			values[i++] = TransactionIdGetDatum(catalog_xmin);
		else
			nulls[i++] = true;

		if (restart_lsn != InvalidXLogRecPtr)
			values[i++] = LSNGetDatum(restart_lsn);
		else
			nulls[i++] = true;

		if (confirmed_flush_lsn != InvalidXLogRecPtr)
			values[i++] = LSNGetDatum(confirmed_flush_lsn);
		else
			nulls[i++] = true;

		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
	}

	tuplestore_donestoring(tupstore);

	return (Datum) 0;
}
Exemple #10
0
/*
 * Set the state of a subscription table.
 *
 * If update_only is true and the record for given table doesn't exist, do
 * nothing.  This can be used to avoid inserting a new record that was deleted
 * by someone else.  Generally, subscription DDL commands should use false,
 * workers should use true.
 *
 * The insert-or-update logic in this function is not concurrency safe so it
 * might raise an error in rare circumstances.  But if we took a stronger lock
 * such as ShareRowExclusiveLock, we would risk more deadlocks.
 */
Oid
SetSubscriptionRelState(Oid subid, Oid relid, char state,
						XLogRecPtr sublsn, bool update_only)
{
	Relation	rel;
	HeapTuple	tup;
	Oid			subrelid = InvalidOid;
	bool		nulls[Natts_pg_subscription_rel];
	Datum		values[Natts_pg_subscription_rel];

	LockSharedObject(SubscriptionRelationId, subid, 0, AccessShareLock);

	rel = heap_open(SubscriptionRelRelationId, RowExclusiveLock);

	/* Try finding existing mapping. */
	tup = SearchSysCacheCopy2(SUBSCRIPTIONRELMAP,
							  ObjectIdGetDatum(relid),
							  ObjectIdGetDatum(subid));

	/*
	 * If the record for given table does not exist yet create new record,
	 * otherwise update the existing one.
	 */
	if (!HeapTupleIsValid(tup) && !update_only)
	{
		/* Form the tuple. */
		memset(values, 0, sizeof(values));
		memset(nulls, false, sizeof(nulls));
		values[Anum_pg_subscription_rel_srsubid - 1] = ObjectIdGetDatum(subid);
		values[Anum_pg_subscription_rel_srrelid - 1] = ObjectIdGetDatum(relid);
		values[Anum_pg_subscription_rel_srsubstate - 1] = CharGetDatum(state);
		if (sublsn != InvalidXLogRecPtr)
			values[Anum_pg_subscription_rel_srsublsn - 1] = LSNGetDatum(sublsn);
		else
			nulls[Anum_pg_subscription_rel_srsublsn - 1] = true;

		tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);

		/* Insert tuple into catalog. */
		subrelid = CatalogTupleInsert(rel, tup);

		heap_freetuple(tup);
	}
	else if (HeapTupleIsValid(tup))
	{
		bool		replaces[Natts_pg_subscription_rel];

		/* Update the tuple. */
		memset(values, 0, sizeof(values));
		memset(nulls, false, sizeof(nulls));
		memset(replaces, false, sizeof(replaces));

		replaces[Anum_pg_subscription_rel_srsubstate - 1] = true;
		values[Anum_pg_subscription_rel_srsubstate - 1] = CharGetDatum(state);

		replaces[Anum_pg_subscription_rel_srsublsn - 1] = true;
		if (sublsn != InvalidXLogRecPtr)
			values[Anum_pg_subscription_rel_srsublsn - 1] = LSNGetDatum(sublsn);
		else
			nulls[Anum_pg_subscription_rel_srsublsn - 1] = true;

		tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls,
								replaces);

		/* Update the catalog. */
		CatalogTupleUpdate(rel, &tup->t_self, tup);

		subrelid = HeapTupleGetOid(tup);
	}

	/* Cleanup. */
	heap_close(rel, NoLock);

	return subrelid;
}