diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6b2b9e3..96f1d47 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -126,6 +126,7 @@ blhandler(PG_FUNCTION_ARGS)
 	amroutine->ambuild = blbuild;
 	amroutine->ambuildempty = blbuildempty;
 	amroutine->aminsert = blinsert;
+	amroutine->amtargetdelete = NULL;
 	amroutine->ambulkdelete = blbulkdelete;
 	amroutine->amvacuumcleanup = blvacuumcleanup;
 	amroutine->amcanreturn = NULL;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index e95fbbc..a0e06bd 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -103,6 +103,7 @@ brinhandler(PG_FUNCTION_ARGS)
 	amroutine->ambuild = brinbuild;
 	amroutine->ambuildempty = brinbuildempty;
 	amroutine->aminsert = brininsert;
+	amroutine->amtargetdelete = NULL;
 	amroutine->ambulkdelete = brinbulkdelete;
 	amroutine->amvacuumcleanup = brinvacuumcleanup;
 	amroutine->amcanreturn = NULL;
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 0a32182..acf14e7 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -58,6 +58,7 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->ambuild = ginbuild;
 	amroutine->ambuildempty = ginbuildempty;
 	amroutine->aminsert = gininsert;
+	amroutine->amtargetdelete = NULL;
 	amroutine->ambulkdelete = ginbulkdelete;
 	amroutine->amvacuumcleanup = ginvacuumcleanup;
 	amroutine->amcanreturn = NULL;
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8a42eff..d7a53d2 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -80,6 +80,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->ambuild = gistbuild;
 	amroutine->ambuildempty = gistbuildempty;
 	amroutine->aminsert = gistinsert;
+	amroutine->amtargetdelete = NULL;
 	amroutine->ambulkdelete = gistbulkdelete;
 	amroutine->amvacuumcleanup = gistvacuumcleanup;
 	amroutine->amcanreturn = gistcanreturn;
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 0002df3..5fb32d6 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -76,6 +76,7 @@ hashhandler(PG_FUNCTION_ARGS)
 	amroutine->ambuild = hashbuild;
 	amroutine->ambuildempty = hashbuildempty;
 	amroutine->aminsert = hashinsert;
+	amroutine->amtargetdelete = NULL;
 	amroutine->ambulkdelete = hashbulkdelete;
 	amroutine->amvacuumcleanup = hashvacuumcleanup;
 	amroutine->amcanreturn = NULL;
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 22b5cc9..a25d844 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -730,6 +730,23 @@ index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
 	return ntids;
 }
 
+IndexBulkDeleteResult *
+index_target_delete(IndexVacuumInfo *info,
+					IndexBulkDeleteResult *stats,
+					Relation hrel,
+					Datum *values,
+					bool *isnull,
+					ItemPointer htid)
+{
+	Relation indexRelation = info->index;
+
+	RELATION_CHECKS;
+
+	CHECK_REL_PROCEDURE(amtargetdelete);
+
+	return indexRelation->rd_amroutine->amtargetdelete(info, stats, hrel, values, isnull, htid);
+}
+
 /* ----------------
  *		index_bulk_delete - do mass deletion of index entries
  *
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 27a3032..dd87a05 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -34,7 +34,9 @@
 #include "storage/smgr.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
+#include "utils/lsyscache.h"
 #include "utils/memutils.h"
+#include "utils/syscache.h"
 
 
 /* Working state needed by btvacuumpage */
@@ -127,6 +129,7 @@ bthandler(PG_FUNCTION_ARGS)
 	amroutine->ambuild = btbuild;
 	amroutine->ambuildempty = btbuildempty;
 	amroutine->aminsert = btinsert;
+	amroutine->amtargetdelete = bttargetdelete;
 	amroutine->ambulkdelete = btbulkdelete;
 	amroutine->amvacuumcleanup = btvacuumcleanup;
 	amroutine->amcanreturn = btcanreturn;
@@ -884,6 +887,195 @@ btbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	return stats;
 }
 
+static void
+cleanup_block(IndexVacuumInfo *info,
+		   IndexBulkDeleteResult *stats, BlockNumber blkno)
+{
+	BTVacState	vstate;
+
+	vstate.info = info;
+	vstate.stats = stats;
+	vstate.callback = NULL;
+	vstate.callback_state = NULL;
+	vstate.cycleid = 0;
+	vstate.lastBlockVacuumed = BTREE_METAPAGE;	/* Initialise at first block */
+	vstate.lastBlockLocked = BTREE_METAPAGE;
+	vstate.totFreePages = 0;
+	vstate.oldestBtpoXact = InvalidTransactionId;
+
+	/* Create a temporary memory context to run _bt_pagedel in */
+	vstate.pagedelcontext = AllocSetContextCreate(CurrentMemoryContext,
+												  "_bt_pagedel",
+												  ALLOCSET_DEFAULT_SIZES);
+	btvacuumpage(&vstate, blkno, blkno);
+
+	if (vstate.totFreePages > 0)
+		IndexFreeSpaceMapVacuum(info->index);
+
+	stats->pages_free += vstate.totFreePages;
+
+	MemoryContextDelete(vstate.pagedelcontext);
+}
+
+/*
+ * Build scan key for an index relation by a heap tuple TID
+ */
+static int
+build_scan_key(ScanKey skey, Relation rel, Relation irel, Datum *values, bool *nulls)
+{
+	TupleDesc	desc = RelationGetDescr(rel);
+	HeapTuple	tuple;
+	Datum		indclassDatum;
+	int			attoff;
+	oidvector*	opclass;
+	int2vector*	indkey = &irel->rd_index->indkey;
+	bool 		isnull;
+
+	tuple = heap_form_tuple(desc, values, nulls);
+	indclassDatum = SysCacheGetAttr(INDEXRELID, irel->rd_indextuple,
+										Anum_pg_index_indclass, &isnull);
+
+	opclass = (oidvector *) DatumGetPointer(indclassDatum);
+	for (attoff = 0; attoff < IndexRelationGetNumberOfKeyAttributes(irel); attoff++)
+	{
+		Oid				operator;
+		Oid				opfamily;
+		RegProcedure	regop;
+		int				pkattno = attoff + 1;
+		int				mainattno = indkey->values[attoff];
+		Oid				optype = get_opclass_input_type(opclass->values[attoff]);
+		Datum			scanvalue;
+		StrategyNumber	strategy;
+
+		if (mainattno > 0)
+			scanvalue = heap_getattr(tuple, mainattno, desc, &isnull);
+		else
+			return -1;
+
+		/*
+		 * Load the operator info.  We need this to get the equality operator
+		 * function for the scan key.
+		 */
+		opfamily = get_opclass_family(opclass->values[attoff]);
+
+		if (irel->rd_exclstrats != NULL)
+			strategy = irel->rd_exclstrats[attoff];
+		else
+			strategy = BTEqualStrategyNumber;
+
+		operator = get_opfamily_member(opfamily, optype, optype, strategy);
+		if (!OidIsValid(operator))
+			return -1;
+
+		regop = get_opcode(operator);
+
+		/* Initialize the scan key. */
+		ScanKeyInit(&skey[attoff],
+				pkattno,
+				strategy,
+				regop,
+				scanvalue);
+
+		/* Check for null value. */
+		if (isnull)
+			skey[attoff].sk_flags |= SK_ISNULL;
+
+		/* Use index collation */
+		if (irel->rd_indcollation != NULL)
+			skey[attoff].sk_collation = irel->rd_indcollation[attoff];
+
+		/* Use index procedure */
+		if (irel->rd_exclprocs != NULL)
+			fmgr_info(irel->rd_exclprocs[attoff], &(skey[attoff].sk_func));
+	}
+
+	return 0;
+}
+
+IndexBulkDeleteResult*
+bttargetdelete(IndexVacuumInfo *info,
+			   IndexBulkDeleteResult *stats,
+			   Relation hrel,
+			   Datum *values,
+			   bool *isnull,
+			   ItemPointer htid)
+{
+	Relation		irel = info->index;
+	ScanKeyData		skey[INDEX_MAX_KEYS];
+	SnapshotData	snap;
+	IndexScanDesc	scan;
+	ItemPointer		tid;
+	bool			found = false;
+	OffsetNumber	offnum;
+	BlockNumber		blkno;
+
+	if (stats == NULL)
+	{
+		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+		stats->estimated_count = false;
+		stats->num_index_tuples = 0;
+		stats->pages_deleted = 0;
+	}
+
+	if (build_scan_key(skey, hrel, irel, values, isnull) != 0)
+	{
+		pfree(stats);
+		return NULL;
+	}
+
+	InitDirtySnapshot(snap);
+	scan = index_beginscan(hrel, irel, &snap, IndexRelationGetNumberOfKeyAttributes(irel), 0);
+	index_rescan(scan, skey, IndexRelationGetNumberOfKeyAttributes(irel), NULL, 0);
+
+	/* Pass along index entries array with equal scan key value */
+	while ((!found) && ((tid = index_getnext_tid(scan, ForwardScanDirection)) != NULL))
+	{
+		if (ItemPointerEquals(tid, htid))
+		{
+			BTScanPos	idx_pos = (BTScanPos) &(((BTScanOpaque) scan->opaque)->currPos);
+			blkno = idx_pos->currPage;
+			offnum = idx_pos->items[idx_pos->itemIndex].indexOffset;
+			found = true;
+		}
+	}
+	index_endscan(scan);
+
+	/* Delete index tuple */
+	if (found)
+	{
+		Buffer		buf;
+		Page		page;
+		bool		needLock;
+		int			npages;
+		IndexTuple	itup;
+
+		needLock = !RELATION_IS_LOCAL(irel);
+		if (needLock)
+			LockRelationForExtension(irel, ExclusiveLock);
+			npages = RelationGetNumberOfBlocks(irel);
+		if (needLock)
+			UnlockRelationForExtension(irel, ExclusiveLock);
+		if (blkno >= npages)
+			return stats;
+
+		buf = ReadBuffer(irel, blkno);
+		page = BufferGetPage(buf);
+		LockBufferForCleanup(buf);
+
+		itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
+
+		if (ItemPointerEquals(htid, &(itup->t_tid)))
+		{
+			_bt_delitems_vacuum(irel, buf, &offnum, 1, blkno);
+			stats->tuples_removed++;
+			stats->num_pages = npages;
+		}
+		_bt_relbuf(irel, buf);
+		cleanup_block(info, stats, blkno);
+	}
+	return stats;
+}
+
 /*
  * Post-VACUUM cleanup.
  *
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 4a9b5da..3257281 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -56,6 +56,7 @@ spghandler(PG_FUNCTION_ARGS)
 	amroutine->ambuild = spgbuild;
 	amroutine->ambuildempty = spgbuildempty;
 	amroutine->aminsert = spginsert;
+	amroutine->amtargetdelete = NULL;
 	amroutine->ambulkdelete = spgbulkdelete;
 	amroutine->amvacuumcleanup = spgvacuumcleanup;
 	amroutine->amcanreturn = spgcanreturn;
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 14526a6..411bb43 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -76,6 +76,13 @@ typedef bool (*aminsert_function) (Relation indexRelation,
 								   IndexUniqueCheck checkUnique,
 								   struct IndexInfo *indexInfo);
 
+/* target delete */
+typedef IndexBulkDeleteResult *(*amtargetdelete_function) (IndexVacuumInfo *info,
+														   IndexBulkDeleteResult *stats,
+														   Relation hrel,
+														   Datum *values,
+														   bool *isnull,
+														   ItemPointer htid);
 /* bulk delete */
 typedef IndexBulkDeleteResult *(*ambulkdelete_function) (IndexVacuumInfo *info,
 														 IndexBulkDeleteResult *stats,
@@ -207,6 +214,7 @@ typedef struct IndexAmRoutine
 	ambuild_function ambuild;
 	ambuildempty_function ambuildempty;
 	aminsert_function aminsert;
+	amtargetdelete_function amtargetdelete;
 	ambulkdelete_function ambulkdelete;
 	amvacuumcleanup_function amvacuumcleanup;
 	amcanreturn_function amcanreturn;	/* can be NULL */
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 24c720b..cdcc6db 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -163,6 +163,12 @@ extern HeapTuple index_fetch_heap(IndexScanDesc scan);
 extern HeapTuple index_getnext(IndexScanDesc scan, ScanDirection direction);
 extern int64 index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap);
 
+extern IndexBulkDeleteResult *index_target_delete(IndexVacuumInfo *info,
+												IndexBulkDeleteResult *stats,
+												Relation hrel,
+												Datum *values,
+												bool *isnull,
+												ItemPointer htid);
 extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info,
 				  IndexBulkDeleteResult *stats,
 				  IndexBulkDeleteCallback callback,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 04ecb4c..0de5a24 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -508,6 +508,12 @@ extern void btparallelrescan(IndexScanDesc scan);
 extern void btendscan(IndexScanDesc scan);
 extern void btmarkpos(IndexScanDesc scan);
 extern void btrestrpos(IndexScanDesc scan);
+extern IndexBulkDeleteResult *bttargetdelete(IndexVacuumInfo *info,
+		   IndexBulkDeleteResult *stats,
+		   Relation hrel,
+		   Datum *values,
+		   bool *isnull,
+		   ItemPointer htid);
 extern IndexBulkDeleteResult *btbulkdelete(IndexVacuumInfo *info,
 			 IndexBulkDeleteResult *stats,
 			 IndexBulkDeleteCallback callback,
