Skip to content

Commit 479a36f

Browse files
committed
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.
1 parent d8dbeb0 commit 479a36f

File tree

3 files changed

+54
-17
lines changed

3 files changed

+54
-17
lines changed

src/backend/utils/adt/txid.c

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,34 @@ cmp_txid(const void *aa, const void *bb)
130130
}
131131

132132
/*
133-
* sort a snapshot's txids, so we can use bsearch() later.
133+
* Sort a snapshot's txids, so we can use bsearch() later. Also remove
134+
* any duplicates.
134135
*
135136
* For consistency of on-disk representation, we always sort even if bsearch
136137
* will not be used.
137138
*/
138139
static void
139140
sort_snapshot(TxidSnapshot *snap)
140141
{
142+
txid last = 0;
143+
int nxip, idx1, idx2;
144+
141145
if (snap->nxip > 1)
146+
{
142147
qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
148+
149+
/* remove duplicates */
150+
nxip = snap->nxip;
151+
idx1 = idx2 = 0;
152+
while (idx1 < nxip)
153+
{
154+
if (snap->xip[idx1] != last)
155+
last = snap->xip[idx2++] = snap->xip[idx1];
156+
else
157+
snap->nxip--;
158+
idx1++;
159+
}
160+
}
143161
}
144162

145163
/*
@@ -294,10 +312,12 @@ parse_snapshot(const char *str)
294312
str = endp;
295313

296314
/* require the input to be in order */
297-
if (val < xmin || val >= xmax || val <= last_val)
315+
if (val < xmin || val >= xmax || val < last_val)
298316
goto bad_format;
299317

300-
buf_add_txid(buf, val);
318+
/* skip duplicates */
319+
if (val != last_val)
320+
buf_add_txid(buf, val);
301321
last_val = val;
302322

303323
if (*str == ',')
@@ -360,8 +380,7 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
360380
{
361381
TxidSnapshot *snap;
362382
uint32 nxip,
363-
i,
364-
size;
383+
i;
365384
TxidEpoch state;
366385
Snapshot cur;
367386

@@ -373,9 +392,7 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
373392

374393
/* allocate */
375394
nxip = cur->xcnt;
376-
size = TXID_SNAPSHOT_SIZE(nxip);
377-
snap = palloc(size);
378-
SET_VARSIZE(snap, size);
395+
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
379396

380397
/* fill */
381398
snap->xmin = convert_xid(cur->xmin, &state);
@@ -384,9 +401,18 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
384401
for (i = 0; i < nxip; i++)
385402
snap->xip[i] = convert_xid(cur->xip[i], &state);
386403

387-
/* we want them guaranteed to be in ascending order */
404+
/*
405+
* We want them guaranteed to be in ascending order. This also removes
406+
* any duplicate xids. Normally, an XID can only be assigned to one
407+
* backend, but when preparing a transaction for two-phase commit, there
408+
* is a transient state when both the original backend and the dummy
409+
* PGPROC entry reserved for the prepared transaction hold the same XID.
410+
*/
388411
sort_snapshot(snap);
389412

413+
/* set size after sorting, because it may have removed duplicate xips */
414+
SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(snap->nxip));
415+
390416
PG_RETURN_POINTER(snap);
391417
}
392418

@@ -464,18 +490,27 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
464490
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
465491
snap->xmin = xmin;
466492
snap->xmax = xmax;
467-
snap->nxip = nxip;
468-
SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
469493

470494
for (i = 0; i < nxip; i++)
471495
{
472496
txid cur = pq_getmsgint64(buf);
473497

474-
if (cur <= last || cur < xmin || cur >= xmax)
498+
if (cur < last || cur < xmin || cur >= xmax)
475499
goto bad_format;
500+
501+
/* skip duplicate xips */
502+
if (cur == last)
503+
{
504+
i--;
505+
nxip--;
506+
continue;
507+
}
508+
476509
snap->xip[i] = cur;
477510
last = cur;
478511
}
512+
snap->nxip = nxip;
513+
SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
479514
PG_RETURN_POINTER(snap);
480515

481516
bad_format:

src/test/regress/expected/txid.out

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ select '12:18:14,16'::txid_snapshot;
1212
12:18:14,16
1313
(1 row)
1414

15+
select '12:16:14,14'::txid_snapshot;
16+
txid_snapshot
17+
---------------
18+
12:16:14
19+
(1 row)
20+
1521
-- errors
1622
select '31:12:'::txid_snapshot;
1723
ERROR: invalid input for txid_snapshot: "31:12:"
@@ -29,10 +35,6 @@ select '12:16:14,13'::txid_snapshot;
2935
ERROR: invalid input for txid_snapshot: "12:16:14,13"
3036
LINE 1: select '12:16:14,13'::txid_snapshot;
3137
^
32-
select '12:16:14,14'::txid_snapshot;
33-
ERROR: invalid input for txid_snapshot: "12:16:14,14"
34-
LINE 1: select '12:16:14,14'::txid_snapshot;
35-
^
3638
create temp table snapshot_test (
3739
nr integer,
3840
snap txid_snapshot

src/test/regress/sql/txid.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
-- i/o
44
select '12:13:'::txid_snapshot;
55
select '12:18:14,16'::txid_snapshot;
6+
select '12:16:14,14'::txid_snapshot;
67

78
-- errors
89
select '31:12:'::txid_snapshot;
910
select '0:1:'::txid_snapshot;
1011
select '12:13:0'::txid_snapshot;
1112
select '12:16:14,13'::txid_snapshot;
12-
select '12:16:14,14'::txid_snapshot;
1313

1414
create temp table snapshot_test (
1515
nr integer,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy