PostgreSQL
Check-in [92e43657bc]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment: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.
Timelines: family | ancestors | descendants | both | trunk | WIN32_DEV | REL9_0_ALPHA4_BRANCH
Files: files | file ages | folders
SHA1:92e43657bcaa571eee2b789d6e57fe5cf45f817b
User & Date: heikki.linnakangas@iki.fi 2014-05-15 15:29:20
Context
2014-05-15
16:47
Fix a couple of bugs in pg_recvlogical output to stdout. Don't close stdout on SIGHUP. Also, when a SIGHUP is received, close the file immediately, rather than only after receiving some more data fro... check-in: ff5cb39ea2 user: heikki.linnakangas@iki.fi tags: trunk, WIN32_DEV, REL9_0_ALPHA4_BRANCH
15:29
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_snaps... check-in: 92e43657bc user: heikki.linnakangas@iki.fi tags: trunk, WIN32_DEV, REL9_0_ALPHA4_BRANCH
13:37
Fix race condition in preparing a transaction for two-phase commit. To lock a prepared transaction's shared memory entry, we used to mark it with the XID of the backend. When the XID was no longer ac... check-in: 71b0dd1272 user: heikki.linnakangas@iki.fi tags: trunk, WIN32_DEV, REL9_0_ALPHA4_BRANCH
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/backend/utils/adt/txid.c.

127
128
129
130
131
132
133
134

135
136
137
138
139
140
141



142

143













144
145
146
147
148
149
150
...
291
292
293
294
295
296
297
298
299
300


301
302
303
304
305
306
307
308
...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
...
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394

395





396



397
398
399
400
401
402
403
...
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483









484
485
486


487
488
489
490
491
492
493
		return -1;
	if (a > b)
		return 1;
	return 0;
}

/*
 * sort a snapshot's txids, so we can use bsearch() later.

 *
 * For consistency of on-disk representation, we always sort even if bsearch
 * will not be used.
 */
static void
sort_snapshot(TxidSnapshot *snap)
{



	if (snap->nxip > 1)

		qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);













}

/*
 * check txid visibility.
 */
static bool
is_visible_txid(txid value, const TxidSnapshot *snap)
................................................................................
	while (*str != '\0')
	{
		/* read next value */
		val = str2txid(str, &endp);
		str = endp;

		/* require the input to be in order */
		if (val < xmin || val >= xmax || val <= last_val)
			goto bad_format;



		buf_add_txid(buf, val);
		last_val = val;

		if (*str == ',')
			str++;
		else if (*str != '\0')
			goto bad_format;
	}
................................................................................
 * Note that only top-transaction XIDs are included in the snapshot.
 */
Datum
txid_current_snapshot(PG_FUNCTION_ARGS)
{
	TxidSnapshot *snap;
	uint32		nxip,
				i,
				size;
	TxidEpoch	state;
	Snapshot	cur;

	cur = GetActiveSnapshot();
	if (cur == NULL)
		elog(ERROR, "no active snapshot set");

................................................................................
	 * MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
	 */
	StaticAssertStmt(MAX_BACKENDS * 2 <= TXID_SNAPSHOT_MAX_NXIP,
					 "possible overflow in txid_current_snapshot()");

	/* allocate */
	nxip = cur->xcnt;
	size = TXID_SNAPSHOT_SIZE(nxip);
	snap = palloc(size);
	SET_VARSIZE(snap, size);

	/* fill */
	snap->xmin = convert_xid(cur->xmin, &state);
	snap->xmax = convert_xid(cur->xmax, &state);
	snap->nxip = nxip;
	for (i = 0; i < nxip; i++)
		snap->xip[i] = convert_xid(cur->xip[i], &state);


	/* we want them guaranteed to be in ascending order */





	sort_snapshot(snap);




	PG_RETURN_POINTER(snap);
}

/*
 * txid_snapshot_in(cstring) returns txid_snapshot
 *
................................................................................
	xmax = pq_getmsgint64(buf);
	if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
		goto bad_format;

	snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
	snap->xmin = xmin;
	snap->xmax = xmax;
	snap->nxip = nxip;
	SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));

	for (i = 0; i < nxip; i++)
	{
		txid		cur = pq_getmsgint64(buf);

		if (cur <= last || cur < xmin || cur >= xmax)
			goto bad_format;









		snap->xip[i] = cur;
		last = cur;
	}


	PG_RETURN_POINTER(snap);

bad_format:
	elog(ERROR, "invalid snapshot data");
	return (Datum) NULL;
}








|
>







>
>
>

>

>
>
>
>
>
>
>
>
>
>
>
>
>







 







|


>
>
|







 







|
<







 







|
<
<








>
|
>
>
>
>
>

>
>
>







 







<
<





|

>
>
>
>
>
>
>
>
>



>
>







127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
...
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
...
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
...
396
397
398
399
400
401
402
403


404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
...
494
495
496
497
498
499
500


501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
		return -1;
	if (a > b)
		return 1;
	return 0;
}

/*
 * 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
 * will not be used.
 */
static void
sort_snapshot(TxidSnapshot *snap)
{
	txid	last = 0;
	int		nxip, idx1, idx2;

	if (snap->nxip > 1)
	{
		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++;
		}
	}
}

/*
 * check txid visibility.
 */
static bool
is_visible_txid(txid value, const TxidSnapshot *snap)
................................................................................
	while (*str != '\0')
	{
		/* read next value */
		val = str2txid(str, &endp);
		str = endp;

		/* require the input to be in order */
		if (val < xmin || val >= xmax || val < last_val)
			goto bad_format;

		/* skip duplicates */
		if (val != last_val)
			buf_add_txid(buf, val);
		last_val = val;

		if (*str == ',')
			str++;
		else if (*str != '\0')
			goto bad_format;
	}
................................................................................
 * Note that only top-transaction XIDs are included in the snapshot.
 */
Datum
txid_current_snapshot(PG_FUNCTION_ARGS)
{
	TxidSnapshot *snap;
	uint32		nxip,
				i;

	TxidEpoch	state;
	Snapshot	cur;

	cur = GetActiveSnapshot();
	if (cur == NULL)
		elog(ERROR, "no active snapshot set");

................................................................................
	 * MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
	 */
	StaticAssertStmt(MAX_BACKENDS * 2 <= TXID_SNAPSHOT_MAX_NXIP,
					 "possible overflow in txid_current_snapshot()");

	/* allocate */
	nxip = cur->xcnt;
	snap = palloc(TXID_SNAPSHOT_SIZE(nxip));



	/* fill */
	snap->xmin = convert_xid(cur->xmin, &state);
	snap->xmax = convert_xid(cur->xmax, &state);
	snap->nxip = nxip;
	for (i = 0; i < nxip; i++)
		snap->xip[i] = convert_xid(cur->xip[i], &state);

	/*
	 * 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);

	/* set size after sorting, because it may have removed duplicate xips */
	SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(snap->nxip));

	PG_RETURN_POINTER(snap);
}

/*
 * txid_snapshot_in(cstring) returns txid_snapshot
 *
................................................................................
	xmax = pq_getmsgint64(buf);
	if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
		goto bad_format;

	snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
	snap->xmin = xmin;
	snap->xmax = xmax;



	for (i = 0; i < nxip; i++)
	{
		txid		cur = pq_getmsgint64(buf);

		if (cur < last || cur < xmin || cur >= xmax)
			goto bad_format;

		/* skip duplicate xips */
		if (cur == last)
		{
			i--;
			nxip--;
			continue;
		}

		snap->xip[i] = cur;
		last = cur;
	}
	snap->nxip = nxip;
	SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
	PG_RETURN_POINTER(snap);

bad_format:
	elog(ERROR, "invalid snapshot data");
	return (Datum) NULL;
}

Changes to src/test/regress/expected/txid.out.

7
8
9
10
11
12
13






14
15
16
17
18
19
20
..
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(1 row)

select '12:18:14,16'::txid_snapshot;
 txid_snapshot 
---------------
 12:18:14,16
(1 row)







-- errors
select '31:12:'::txid_snapshot;
ERROR:  invalid input for txid_snapshot: "31:12:"
LINE 1: select '31:12:'::txid_snapshot;
               ^
select '0:1:'::txid_snapshot;
................................................................................
ERROR:  invalid input for txid_snapshot: "12:13:0"
LINE 1: select '12:13:0'::txid_snapshot;
               ^
select '12:16:14,13'::txid_snapshot;
ERROR:  invalid input for txid_snapshot: "12:16:14,13"
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 (
	nr	integer,
	snap	txid_snapshot
);
insert into snapshot_test values (1, '12:13:');
insert into snapshot_test values (2, '12:20:13,15,18');
insert into snapshot_test values (3, '100001:100009:100005,100007,100008');







>
>
>
>
>
>







 







<
<
<
<







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
..
31
32
33
34
35
36
37




38
39
40
41
42
43
44
(1 row)

select '12:18:14,16'::txid_snapshot;
 txid_snapshot 
---------------
 12:18:14,16
(1 row)

select '12:16:14,14'::txid_snapshot;
 txid_snapshot 
---------------
 12:16:14
(1 row)

-- errors
select '31:12:'::txid_snapshot;
ERROR:  invalid input for txid_snapshot: "31:12:"
LINE 1: select '31:12:'::txid_snapshot;
               ^
select '0:1:'::txid_snapshot;
................................................................................
ERROR:  invalid input for txid_snapshot: "12:13:0"
LINE 1: select '12:13:0'::txid_snapshot;
               ^
select '12:16:14,13'::txid_snapshot;
ERROR:  invalid input for txid_snapshot: "12:16:14,13"
LINE 1: select '12:16:14,13'::txid_snapshot;
               ^




create temp table snapshot_test (
	nr	integer,
	snap	txid_snapshot
);
insert into snapshot_test values (1, '12:13:');
insert into snapshot_test values (2, '12:20:13,15,18');
insert into snapshot_test values (3, '100001:100009:100005,100007,100008');

Changes to src/test/regress/sql/txid.sql.

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- txid_snapshot data type and related functions

-- i/o
select '12:13:'::txid_snapshot;
select '12:18:14,16'::txid_snapshot;


-- errors
select '31:12:'::txid_snapshot;
select '0:1:'::txid_snapshot;
select '12:13:0'::txid_snapshot;
select '12:16:14,13'::txid_snapshot;
select '12:16:14,14'::txid_snapshot;

create temp table snapshot_test (
	nr	integer,
	snap	txid_snapshot
);

insert into snapshot_test values (1, '12:13:');





>






<







1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
-- txid_snapshot data type and related functions

-- i/o
select '12:13:'::txid_snapshot;
select '12:18:14,16'::txid_snapshot;
select '12:16:14,14'::txid_snapshot;

-- errors
select '31:12:'::txid_snapshot;
select '0:1:'::txid_snapshot;
select '12:13:0'::txid_snapshot;
select '12:16:14,13'::txid_snapshot;


create temp table snapshot_test (
	nr	integer,
	snap	txid_snapshot
);

insert into snapshot_test values (1, '12:13:');