Skip to content

Commit f47e4ce

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 8c19b80 commit f47e4ce

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 == ',')
@@ -359,8 +379,7 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
359379
{
360380
TxidSnapshot *snap;
361381
uint32 nxip,
362-
i,
363-
size;
382+
i;
364383
TxidEpoch state;
365384
Snapshot cur;
366385

@@ -372,9 +391,7 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
372391

373392
/* allocate */
374393
nxip = cur->xcnt;
375-
size = TXID_SNAPSHOT_SIZE(nxip);
376-
snap = palloc(size);
377-
SET_VARSIZE(snap, size);
394+
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
378395

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

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

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

@@ -463,18 +489,27 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
463489
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
464490
snap->xmin = xmin;
465491
snap->xmax = xmax;
466-
snap->nxip = nxip;
467-
SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
468492

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

473-
if (cur <= last || cur < xmin || cur >= xmax)
497+
if (cur < last || cur < xmin || cur >= xmax)
474498
goto bad_format;
499+
500+
/* skip duplicate xips */
501+
if (cur == last)
502+
{
503+
i--;
504+
nxip--;
505+
continue;
506+
}
507+
475508
snap->xip[i] = cur;
476509
last = cur;
477510
}
511+
snap->nxip = nxip;
512+
SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
478513
PG_RETURN_POINTER(snap);
479514

480515
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