mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Handle duplicate XIDs in txid_snapshot.
The proc array can contain duplicate XIDs, when a transaction is just being prepared for two-phase commit. To cope, remove any duplicates in txid_current_snapshot(). Also ignore duplicates in the input functions, so that if e.g. you have an old pg_dump file that already contains duplicates, it will be accepted. Report and fix by Jan Wieck. Backpatch to all supported versions.
This commit is contained in:
		@@ -130,7 +130,8 @@ cmp_txid(const void *aa, const void *bb)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * sort a snapshot's txids, so we can use bsearch() later.
 | 
					 * Sort a snapshot's txids, so we can use bsearch() later.  Also remove
 | 
				
			||||||
 | 
					 * any duplicates.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * For consistency of on-disk representation, we always sort even if bsearch
 | 
					 * For consistency of on-disk representation, we always sort even if bsearch
 | 
				
			||||||
 * will not be used.
 | 
					 * will not be used.
 | 
				
			||||||
@@ -138,8 +139,25 @@ cmp_txid(const void *aa, const void *bb)
 | 
				
			|||||||
static void
 | 
					static void
 | 
				
			||||||
sort_snapshot(TxidSnapshot *snap)
 | 
					sort_snapshot(TxidSnapshot *snap)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						txid	last = 0;
 | 
				
			||||||
 | 
						int		nxip, idx1, idx2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (snap->nxip > 1)
 | 
						if (snap->nxip > 1)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
		qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
 | 
							qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* remove duplicates */
 | 
				
			||||||
 | 
							nxip = snap->nxip;
 | 
				
			||||||
 | 
							idx1 = idx2 = 0;
 | 
				
			||||||
 | 
							while (idx1 < nxip)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (snap->xip[idx1] != last)
 | 
				
			||||||
 | 
									last = snap->xip[idx2++] = snap->xip[idx1];
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									snap->nxip--;
 | 
				
			||||||
 | 
								idx1++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
@@ -294,9 +312,11 @@ parse_snapshot(const char *str)
 | 
				
			|||||||
		str = endp;
 | 
							str = endp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* require the input to be in order */
 | 
							/* require the input to be in order */
 | 
				
			||||||
		if (val < xmin || val >= xmax || val <= last_val)
 | 
							if (val < xmin || val >= xmax || val < last_val)
 | 
				
			||||||
			goto bad_format;
 | 
								goto bad_format;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* skip duplicates */
 | 
				
			||||||
 | 
							if (val != last_val)
 | 
				
			||||||
			buf_add_txid(buf, val);
 | 
								buf_add_txid(buf, val);
 | 
				
			||||||
		last_val = val;
 | 
							last_val = val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -359,8 +379,7 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	TxidSnapshot *snap;
 | 
						TxidSnapshot *snap;
 | 
				
			||||||
	uint32		nxip,
 | 
						uint32		nxip,
 | 
				
			||||||
				i,
 | 
									i;
 | 
				
			||||||
				size;
 | 
					 | 
				
			||||||
	TxidEpoch	state;
 | 
						TxidEpoch	state;
 | 
				
			||||||
	Snapshot	cur;
 | 
						Snapshot	cur;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -372,9 +391,7 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/* allocate */
 | 
						/* allocate */
 | 
				
			||||||
	nxip = cur->xcnt;
 | 
						nxip = cur->xcnt;
 | 
				
			||||||
	size = TXID_SNAPSHOT_SIZE(nxip);
 | 
						snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
 | 
				
			||||||
	snap = palloc(size);
 | 
					 | 
				
			||||||
	SET_VARSIZE(snap, size);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* fill */
 | 
						/* fill */
 | 
				
			||||||
	snap->xmin = convert_xid(cur->xmin, &state);
 | 
						snap->xmin = convert_xid(cur->xmin, &state);
 | 
				
			||||||
@@ -383,9 +400,18 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
 | 
				
			|||||||
	for (i = 0; i < nxip; i++)
 | 
						for (i = 0; i < nxip; i++)
 | 
				
			||||||
		snap->xip[i] = convert_xid(cur->xip[i], &state);
 | 
							snap->xip[i] = convert_xid(cur->xip[i], &state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* we want them guaranteed to be in ascending order */
 | 
						/*
 | 
				
			||||||
 | 
						 * We want them guaranteed to be in ascending order.  This also removes
 | 
				
			||||||
 | 
						 * any duplicate xids.  Normally, an XID can only be assigned to one
 | 
				
			||||||
 | 
						 * backend, but when preparing a transaction for two-phase commit, there
 | 
				
			||||||
 | 
						 * is a transient state when both the original backend and the dummy
 | 
				
			||||||
 | 
						 * PGPROC entry reserved for the prepared transaction hold the same XID.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	sort_snapshot(snap);
 | 
						sort_snapshot(snap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* set size after sorting, because it may have removed duplicate xips */
 | 
				
			||||||
 | 
						SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(snap->nxip));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PG_RETURN_POINTER(snap);
 | 
						PG_RETURN_POINTER(snap);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -463,18 +489,27 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
 | 
				
			|||||||
	snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
 | 
						snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
 | 
				
			||||||
	snap->xmin = xmin;
 | 
						snap->xmin = xmin;
 | 
				
			||||||
	snap->xmax = xmax;
 | 
						snap->xmax = xmax;
 | 
				
			||||||
	snap->nxip = nxip;
 | 
					 | 
				
			||||||
	SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < nxip; i++)
 | 
						for (i = 0; i < nxip; i++)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		txid		cur = pq_getmsgint64(buf);
 | 
							txid		cur = pq_getmsgint64(buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (cur <= last || cur < xmin || cur >= xmax)
 | 
							if (cur < last || cur < xmin || cur >= xmax)
 | 
				
			||||||
			goto bad_format;
 | 
								goto bad_format;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* skip duplicate xips */
 | 
				
			||||||
 | 
							if (cur == last)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								i--;
 | 
				
			||||||
 | 
								nxip--;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		snap->xip[i] = cur;
 | 
							snap->xip[i] = cur;
 | 
				
			||||||
		last = cur;
 | 
							last = cur;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						snap->nxip = nxip;
 | 
				
			||||||
 | 
						SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
 | 
				
			||||||
	PG_RETURN_POINTER(snap);
 | 
						PG_RETURN_POINTER(snap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bad_format:
 | 
					bad_format:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,12 @@ select '12:18:14,16'::txid_snapshot;
 | 
				
			|||||||
 12:18:14,16
 | 
					 12:18:14,16
 | 
				
			||||||
(1 row)
 | 
					(1 row)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					select '12:16:14,14'::txid_snapshot;
 | 
				
			||||||
 | 
					 txid_snapshot 
 | 
				
			||||||
 | 
					---------------
 | 
				
			||||||
 | 
					 12:16:14
 | 
				
			||||||
 | 
					(1 row)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- errors
 | 
					-- errors
 | 
				
			||||||
select '31:12:'::txid_snapshot;
 | 
					select '31:12:'::txid_snapshot;
 | 
				
			||||||
ERROR:  invalid input for txid_snapshot: "31:12:"
 | 
					ERROR:  invalid input for txid_snapshot: "31:12:"
 | 
				
			||||||
@@ -29,10 +35,6 @@ select '12:16:14,13'::txid_snapshot;
 | 
				
			|||||||
ERROR:  invalid input for txid_snapshot: "12:16:14,13"
 | 
					ERROR:  invalid input for txid_snapshot: "12:16:14,13"
 | 
				
			||||||
LINE 1: select '12:16:14,13'::txid_snapshot;
 | 
					LINE 1: select '12:16:14,13'::txid_snapshot;
 | 
				
			||||||
               ^
 | 
					               ^
 | 
				
			||||||
select '12:16:14,14'::txid_snapshot;
 | 
					 | 
				
			||||||
ERROR:  invalid input for txid_snapshot: "12:16:14,14"
 | 
					 | 
				
			||||||
LINE 1: select '12:16:14,14'::txid_snapshot;
 | 
					 | 
				
			||||||
               ^
 | 
					 | 
				
			||||||
create temp table snapshot_test (
 | 
					create temp table snapshot_test (
 | 
				
			||||||
	nr	integer,
 | 
						nr	integer,
 | 
				
			||||||
	snap	txid_snapshot
 | 
						snap	txid_snapshot
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,13 +3,13 @@
 | 
				
			|||||||
-- i/o
 | 
					-- i/o
 | 
				
			||||||
select '12:13:'::txid_snapshot;
 | 
					select '12:13:'::txid_snapshot;
 | 
				
			||||||
select '12:18:14,16'::txid_snapshot;
 | 
					select '12:18:14,16'::txid_snapshot;
 | 
				
			||||||
 | 
					select '12:16:14,14'::txid_snapshot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- errors
 | 
					-- errors
 | 
				
			||||||
select '31:12:'::txid_snapshot;
 | 
					select '31:12:'::txid_snapshot;
 | 
				
			||||||
select '0:1:'::txid_snapshot;
 | 
					select '0:1:'::txid_snapshot;
 | 
				
			||||||
select '12:13:0'::txid_snapshot;
 | 
					select '12:13:0'::txid_snapshot;
 | 
				
			||||||
select '12:16:14,13'::txid_snapshot;
 | 
					select '12:16:14,13'::txid_snapshot;
 | 
				
			||||||
select '12:16:14,14'::txid_snapshot;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
create temp table snapshot_test (
 | 
					create temp table snapshot_test (
 | 
				
			||||||
	nr	integer,
 | 
						nr	integer,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user