@@ -2387,26 +2387,31 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
23872387	 */ 
23882388	heaptup  =  heap_prepare_insert (relation , tup , xid , cid , options );
23892389
2390+ 	/* 
2391+ 	 * Find buffer to insert this tuple into.  If the page is all visible, 
2392+ 	 * this will also pin the requisite visibility map page. 
2393+ 	 */ 
2394+ 	buffer  =  RelationGetBufferForTuple (relation , heaptup -> t_len ,
2395+ 									   InvalidBuffer , options , bistate ,
2396+ 									   & vmbuffer , NULL );
2397+ 
23902398	/* 
23912399	 * We're about to do the actual insert -- but check for conflict first, to 
23922400	 * avoid possibly having to roll back work we've just done. 
23932401	 * 
2402+ 	 * This is safe without a recheck as long as there is no possibility of 
2403+ 	 * another process scanning the page between this check and the insert 
2404+ 	 * being visible to the scan (i.e., an exclusive buffer content lock is 
2405+ 	 * continuously held from this point until the tuple insert is visible). 
2406+ 	 * 
23942407	 * For a heap insert, we only need to check for table-level SSI locks. Our 
23952408	 * new tuple can't possibly conflict with existing tuple locks, and heap 
23962409	 * page locks are only consolidated versions of tuple locks; they do not 
2397- 	 * lock "gaps" as index page locks do.  So we don't need to identify  a 
2398- 	 * buffer before  making the call. 
2410+ 	 * lock "gaps" as index page locks do.  So we don't need to specify  a 
2411+ 	 * buffer when  making the call, which makes for a faster check . 
23992412	 */ 
24002413	CheckForSerializableConflictIn (relation , NULL , InvalidBuffer );
24012414
2402- 	/* 
2403- 	 * Find buffer to insert this tuple into.  If the page is all visible, 
2404- 	 * this will also pin the requisite visibility map page. 
2405- 	 */ 
2406- 	buffer  =  RelationGetBufferForTuple (relation , heaptup -> t_len ,
2407- 									   InvalidBuffer , options , bistate ,
2408- 									   & vmbuffer , NULL );
2409- 
24102415	/* NO EREPORT(ERROR) from here till changes are logged */ 
24112416	START_CRIT_SECTION ();
24122417
@@ -2660,13 +2665,26 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
26602665
26612666	/* 
26622667	 * We're about to do the actual inserts -- but check for conflict first, 
2663- 	 * to avoid possibly having to roll back work we've just done. 
2668+ 	 * to minimize the possibility of having to roll back work we've just 
2669+ 	 * done. 
26642670	 * 
2665- 	 * For a heap insert, we only need to check for table-level SSI locks. Our 
2666- 	 * new tuple can't possibly conflict with existing tuple locks, and heap 
2671+ 	 * A check here does not definitively prevent a serialization anomaly; 
2672+ 	 * that check MUST be done at least past the point of acquiring an 
2673+ 	 * exclusive buffer content lock on every buffer that will be affected, 
2674+ 	 * and MAY be done after all inserts are reflected in the buffers and 
2675+ 	 * those locks are released; otherwise there race condition.  Since 
2676+ 	 * multiple buffers can be locked and unlocked in the loop below, and it 
2677+ 	 * would not be feasible to identify and lock all of those buffers before 
2678+ 	 * the loop, we must do a final check at the end. 
2679+ 	 * 
2680+ 	 * The check here could be omitted with no loss of correctness; it is 
2681+ 	 * present strictly as an optimization. 
2682+ 	 * 
2683+ 	 * For heap inserts, we only need to check for table-level SSI locks. Our 
2684+ 	 * new tuples can't possibly conflict with existing tuple locks, and heap 
26672685	 * page locks are only consolidated versions of tuple locks; they do not 
2668- 	 * lock "gaps" as index page locks do.  So we don't need to identify  a 
2669- 	 * buffer before  making the call. 
2686+ 	 * lock "gaps" as index page locks do.  So we don't need to specify  a 
2687+ 	 * buffer when  making the call, which makes for a faster check . 
26702688	 */ 
26712689	CheckForSerializableConflictIn (relation , NULL , InvalidBuffer );
26722690
@@ -2845,6 +2863,22 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
28452863		ndone  +=  nthispage ;
28462864	}
28472865
2866+ 	/* 
2867+ 	 * We're done with the actual inserts.  Check for conflicts again, to 
2868+ 	 * ensure that all rw-conflicts in to these inserts are detected.  Without 
2869+ 	 * this final check, a sequential scan of the heap may have locked the 
2870+ 	 * table after the "before" check, missing one opportunity to detect the 
2871+ 	 * conflict, and then scanned the table before the new tuples were there, 
2872+ 	 * missing the other chance to detect the conflict. 
2873+ 	 * 
2874+ 	 * For heap inserts, we only need to check for table-level SSI locks. Our 
2875+ 	 * new tuples can't possibly conflict with existing tuple locks, and heap 
2876+ 	 * page locks are only consolidated versions of tuple locks; they do not 
2877+ 	 * lock "gaps" as index page locks do.  So we don't need to specify a 
2878+ 	 * buffer when making the call. 
2879+ 	 */ 
2880+ 	CheckForSerializableConflictIn (relation , NULL , InvalidBuffer );
2881+ 
28482882	/* 
28492883	 * If tuples are cachable, mark them for invalidation from the caches in 
28502884	 * case we abort.  Note it is OK to do this after releasing the buffer, 
@@ -3158,6 +3192,11 @@ heap_delete(Relation relation, ItemPointer tid,
31583192	/* 
31593193	 * We're about to do the actual delete -- check for conflict first, to 
31603194	 * avoid possibly having to roll back work we've just done. 
3195+ 	 * 
3196+ 	 * This is safe without a recheck as long as there is no possibility of 
3197+ 	 * another process scanning the page between this check and the delete 
3198+ 	 * being visible to the scan (i.e., an exclusive buffer content lock is 
3199+ 	 * continuously held from this point until the tuple delete is visible). 
31613200	 */ 
31623201	CheckForSerializableConflictIn (relation , & tp , buffer );
31633202
@@ -3785,12 +3824,6 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
37853824		goto l2 ;
37863825	}
37873826
3788- 	/* 
3789- 	 * We're about to do the actual update -- check for conflict first, to 
3790- 	 * avoid possibly having to roll back work we've just done. 
3791- 	 */ 
3792- 	CheckForSerializableConflictIn (relation , & oldtup , buffer );
3793- 
37943827	/* Fill in transaction status data */ 
37953828
37963829	/* 
@@ -3979,14 +4012,20 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
39794012	}
39804013
39814014	/* 
3982- 	 * We're about to create  the new tuple  -- check for conflict first, to 
4015+ 	 * We're about to do  the actual update  -- check for conflict first, to 
39834016	 * avoid possibly having to roll back work we've just done. 
39844017	 * 
3985- 	 * NOTE: For a tuple insert, we only need to check for table locks, since 
3986- 	 * predicate locking at the index level will cover ranges for anything 
3987- 	 * except a table scan.  Therefore, only provide the relation. 
4018+ 	 * This is safe without a recheck as long as there is no possibility of 
4019+ 	 * another process scanning the pages between this check and the update 
4020+ 	 * being visible to the scan (i.e., exclusive buffer content lock(s) are 
4021+ 	 * continuously held from this point until the tuple update is visible). 
4022+ 	 * 
4023+ 	 * For the new tuple the only check needed is at the relation level, but 
4024+ 	 * since both tuples are in the same relation and the check for oldtup 
4025+ 	 * will include checking the relation level, there is no benefit to a 
4026+ 	 * separate check for the new tuple. 
39884027	 */ 
3989- 	CheckForSerializableConflictIn (relation , NULL ,  InvalidBuffer );
4028+ 	CheckForSerializableConflictIn (relation , & oldtup ,  buffer );
39904029
39914030	/* 
39924031	 * At this point newbuf and buffer are both pinned and locked, and newbuf 
0 commit comments