mirror of
https://github.com/postgres/postgres.git
synced 2025-04-29 13:56:47 +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:
parent
bb38fb0d43
commit
8f9b9590d7
@ -131,7 +131,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.
|
||||||
@ -139,8 +140,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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -295,9 +313,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;
|
||||||
|
|
||||||
@ -361,8 +381,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;
|
||||||
|
|
||||||
@ -381,9 +400,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);
|
||||||
@ -392,9 +409,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,18 +498,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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user