Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite79aaeb

Browse files
committed
Fix deletion of speculatively inserted TOAST on conflict
INSERT .. ON CONFLICT runs a pre-check of the possible conflictingconstraints before performing the actual speculative insertion. In casethe inserted tuple included TOASTed columns the ON CONFLICT conditionwould be handled correctly in case the conflict was caught by thepre-check, but if two transactions entered the speculative insertionphase at the same time, one would have to re-try, and the code foraborting a speculative insertion did not handle deleting thespeculatively inserted TOAST datums correctly.TOAST deletion would fail with "ERROR: attempted to delete invisibletuple" as we attempted to remove the TOAST tuples usingsimple_heap_delete which reasoned that the given tuples should not bevisible to the command that wrote them.This commit updates the heap_abort_speculative() function which abortsthe conflicting tuple to use itself, via toast_delete, for deletingassociated TOAST datums. Like before, the inserted toast rows are notmarked as being speculative.This commit also adds a isolationtester spec test, exercising therelevant code path. Unfortunately 9.5 cannot handle two waitingsessions, and thus cannot execute this test.Reported-By: Viren Negi, Oskari SaarenmaaAuthor: Oskari Saarenmaa, edited a bit by meBug: #14150Discussion: <20160519123338.12513.20271@wrigleys.postgresql.org>Backpatch: 9.5, where ON CONFLICT was introduced
1 parent8cb23db commite79aaeb

File tree

7 files changed

+87
-13
lines changed

7 files changed

+87
-13
lines changed

‎src/backend/access/heap/heapam.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3335,7 +3335,7 @@ heap_delete(Relation relation, ItemPointer tid,
33353335
Assert(!HeapTupleHasExternal(&tp));
33363336
}
33373337
elseif (HeapTupleHasExternal(&tp))
3338-
toast_delete(relation,&tp);
3338+
toast_delete(relation,&tp, false);
33393339

33403340
/*
33413341
* Mark tuple for invalidation from system caches at next command
@@ -6057,7 +6057,8 @@ heap_finish_speculative(Relation relation, HeapTuple tuple)
60576057
* could deadlock with each other, which would not be acceptable.
60586058
*
60596059
* This is somewhat redundant with heap_delete, but we prefer to have a
6060-
* dedicated routine with stripped down requirements.
6060+
* dedicated routine with stripped down requirements. Note that this is also
6061+
* used to delete the TOAST tuples created during speculative insertion.
60616062
*
60626063
* This routine does not affect logical decoding as it only looks at
60636064
* confirmation records.
@@ -6101,7 +6102,7 @@ heap_abort_speculative(Relation relation, HeapTuple tuple)
61016102
*/
61026103
if (tp.t_data->t_choice.t_heap.t_xmin!=xid)
61036104
elog(ERROR,"attempted to kill a tuple inserted by another transaction");
6104-
if (!HeapTupleHeaderIsSpeculative(tp.t_data))
6105+
if (!(IsToastRelation(relation)||HeapTupleHeaderIsSpeculative(tp.t_data)))
61056106
elog(ERROR,"attempted to kill a non-speculative tuple");
61066107
Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
61076108

@@ -6171,7 +6172,10 @@ heap_abort_speculative(Relation relation, HeapTuple tuple)
61716172
LockBuffer(buffer,BUFFER_LOCK_UNLOCK);
61726173

61736174
if (HeapTupleHasExternal(&tp))
6174-
toast_delete(relation,&tp);
6175+
{
6176+
Assert(!IsToastRelation(relation));
6177+
toast_delete(relation,&tp, true);
6178+
}
61756179

61766180
/*
61776181
* Never need to mark tuple for invalidation, since catalogs don't support

‎src/backend/access/heap/tuptoaster.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ typedef struct toast_compress_header
6767
#defineTOAST_COMPRESS_SET_RAWSIZE(ptr,len) \
6868
(((toast_compress_header *) (ptr))->rawsize = (len))
6969

70-
staticvoidtoast_delete_datum(Relationrel,Datumvalue);
70+
staticvoidtoast_delete_datum(Relationrel,Datumvalue,boolis_speculative);
7171
staticDatumtoast_save_datum(Relationrel,Datumvalue,
7272
structvarlena*oldexternal,intoptions);
7373
staticbooltoastrel_valueid_exists(Relationtoastrel,Oidvalueid);
@@ -461,7 +461,7 @@ toast_datum_size(Datum value)
461461
* ----------
462462
*/
463463
void
464-
toast_delete(Relationrel,HeapTupleoldtup)
464+
toast_delete(Relationrel,HeapTupleoldtup,boolis_speculative)
465465
{
466466
TupleDesctupleDesc;
467467
Form_pg_attribute*att;
@@ -508,7 +508,7 @@ toast_delete(Relation rel, HeapTuple oldtup)
508508
if (toast_isnull[i])
509509
continue;
510510
elseif (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
511-
toast_delete_datum(rel,value);
511+
toast_delete_datum(rel,value,is_speculative);
512512
}
513513
}
514514
}
@@ -1064,7 +1064,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
10641064
if (need_delold)
10651065
for (i=0;i<numAttrs;i++)
10661066
if (toast_delold[i])
1067-
toast_delete_datum(rel,toast_oldvalues[i]);
1067+
toast_delete_datum(rel,toast_oldvalues[i], false);
10681068

10691069
returnresult_tuple;
10701070
}
@@ -1656,7 +1656,7 @@ toast_save_datum(Relation rel, Datum value,
16561656
* ----------
16571657
*/
16581658
staticvoid
1659-
toast_delete_datum(Relationrel,Datumvalue)
1659+
toast_delete_datum(Relationrel,Datumvalue,boolis_speculative)
16601660
{
16611661
structvarlena*attr= (structvarlena*)DatumGetPointer(value);
16621662
structvaratt_externaltoast_pointer;
@@ -1707,7 +1707,10 @@ toast_delete_datum(Relation rel, Datum value)
17071707
/*
17081708
* Have a chunk, delete it
17091709
*/
1710-
simple_heap_delete(toastrel,&toasttup->t_self);
1710+
if (is_speculative)
1711+
heap_abort_speculative(toastrel,toasttup);
1712+
else
1713+
simple_heap_delete(toastrel,&toasttup->t_self);
17111714
}
17121715

17131716
/*

‎src/backend/utils/time/tqual.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,8 @@ HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
418418

419419
/*
420420
* An invalid Xmin can be left behind by a speculative insertion that
421-
* is canceled by super-deleting the tuple.We shouldn't see any of
422-
*those inTOASTtables, but better safe than sorry.
421+
* is canceled by super-deleting the tuple.This also applies to
422+
* TOASTtuples created during speculative insertion.
423423
*/
424424
elseif (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
425425
return false;

‎src/include/access/tuptoaster.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ extern HeapTuple toast_insert_or_update(Relation rel,
142142
*Called by heap_delete().
143143
* ----------
144144
*/
145-
externvoidtoast_delete(Relationrel,HeapTupleoldtup);
145+
externvoidtoast_delete(Relationrel,HeapTupleoldtup,boolis_speculative);
146146

147147
/* ----------
148148
* heap_tuple_fetch_attr() -
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Parsed test spec with 3 sessions
2+
3+
starting permutation: s2insert s3insert s1commit
4+
pg_advisory_xact_lock
5+
6+
7+
step s2insert:
8+
INSERT INTO ctoast (key, val) VALUES (1, ctoast_large_val()) ON CONFLICT DO NOTHING;
9+
<waiting ...>
10+
step s3insert:
11+
INSERT INTO ctoast (key, val) VALUES (1, ctoast_large_val()) ON CONFLICT DO NOTHING;
12+
<waiting ...>
13+
step s1commit: COMMIT;
14+
step s2insert: <... completed>
15+
step s3insert: <... completed>

‎src/test/isolation/isolation_schedule

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ test: insert-conflict-do-nothing
2828
test: insert-conflict-do-update
2929
test: insert-conflict-do-update-2
3030
test: insert-conflict-do-update-3
31+
test: insert-conflict-toast
3132
test: delete-abort-savept
3233
test: delete-abort-savept-2
3334
test: aborted-keyrevoke
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# INSERT...ON CONFLICT test on table with TOAST
2+
#
3+
# This test verifies that speculatively inserted toast rows do not
4+
# cause conflicts. It does so by using expression index over a
5+
# function which acquires an advisory lock, triggering two index
6+
# insertions to happen almost at the same time. This is not guaranteed
7+
# to lead to a failed speculative insertion, but makes one quite
8+
# likely.
9+
10+
setup
11+
{
12+
CREATETABLEctoast (keyintprimarykey,valtext);
13+
CREATEORREPLACEFUNCTIONctoast_lock_func(int)RETURNSINTIMMUTABLELANGUAGESQLAS'select pg_advisory_xact_lock_shared(1); select $1;';
14+
CREATEORREPLACEFUNCTIONctoast_large_val()RETURNSTEXTLANGUAGESQLAS'select array_agg(md5(g::text))::text from generate_series(1, 256) g';
15+
CREATEUNIQUEINDEXctoast_lock_idxONctoast (ctoast_lock_func(key));
16+
}
17+
18+
teardown
19+
{
20+
DROPTABLEctoast;
21+
DROPFUNCTIONctoast_lock_func(int);
22+
DROPFUNCTIONctoast_large_val();
23+
}
24+
25+
session"s1"
26+
setup
27+
{
28+
BEGINISOLATIONLEVELREADCOMMITTED;
29+
SELECTpg_advisory_xact_lock(1);
30+
}
31+
step"s1commit" {COMMIT; }
32+
33+
session"s2"
34+
setup
35+
{
36+
SETdefault_transaction_isolation='read committed';
37+
}
38+
step"s2insert" {
39+
INSERTINTOctoast (key,val)VALUES (1,ctoast_large_val())ONCONFLICTDONOTHING;
40+
}
41+
42+
session"s3"
43+
setup
44+
{
45+
SETdefault_transaction_isolation='read committed';
46+
}
47+
step"s3insert" {
48+
INSERTINTOctoast (key,val)VALUES (1,ctoast_large_val())ONCONFLICTDONOTHING;
49+
}
50+
51+
permutation"s2insert""s3insert""s1commit"

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp