Reuse BrinDesc and BrinRevmap in brininsert
The brininsert code used to initialize (and destroy) BrinDesc and BrinRevmap for each tuple, which is not free. This patch initializes these structures only once, and reuses them for all inserts in the same command. The data is passed through indexInfo->ii_AmCache. This also introduces an optional AM callback "aminsertcleanup" that allows performing custom cleanup in case simply pfree-ing ii_AmCache is not sufficient (which is the case when the cache contains TupleDesc, Buffers, and so on). Author: Soumyadeep Chakraborty Reviewed-by: Alvaro Herrera, Matthias van de Meent, Tomas Vondra Discussion: https://postgr.es/m/CAE-ML%2B9r2%3DaO1wwji1sBN9gvPz2xRAtFUGfnffpd0ZqyuzjamA%40mail.gmail.com
This commit is contained in:
parent
6ec8262a02
commit
c1ec02be1d
@ -131,6 +131,7 @@ blhandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->ambuild = blbuild;
|
amroutine->ambuild = blbuild;
|
||||||
amroutine->ambuildempty = blbuildempty;
|
amroutine->ambuildempty = blbuildempty;
|
||||||
amroutine->aminsert = blinsert;
|
amroutine->aminsert = blinsert;
|
||||||
|
amroutine->aminsertcleanup = NULL;
|
||||||
amroutine->ambulkdelete = blbulkdelete;
|
amroutine->ambulkdelete = blbulkdelete;
|
||||||
amroutine->amvacuumcleanup = blvacuumcleanup;
|
amroutine->amvacuumcleanup = blvacuumcleanup;
|
||||||
amroutine->amcanreturn = NULL;
|
amroutine->amcanreturn = NULL;
|
||||||
|
@ -139,6 +139,7 @@ typedef struct IndexAmRoutine
|
|||||||
ambuild_function ambuild;
|
ambuild_function ambuild;
|
||||||
ambuildempty_function ambuildempty;
|
ambuildempty_function ambuildempty;
|
||||||
aminsert_function aminsert;
|
aminsert_function aminsert;
|
||||||
|
aminsertcleanup_function aminsertcleanup;
|
||||||
ambulkdelete_function ambulkdelete;
|
ambulkdelete_function ambulkdelete;
|
||||||
amvacuumcleanup_function amvacuumcleanup;
|
amvacuumcleanup_function amvacuumcleanup;
|
||||||
amcanreturn_function amcanreturn; /* can be NULL */
|
amcanreturn_function amcanreturn; /* can be NULL */
|
||||||
@ -359,7 +360,21 @@ aminsert (Relation indexRelation,
|
|||||||
within an SQL statement, it can allocate space
|
within an SQL statement, it can allocate space
|
||||||
in <literal>indexInfo->ii_Context</literal> and store a pointer to the
|
in <literal>indexInfo->ii_Context</literal> and store a pointer to the
|
||||||
data in <literal>indexInfo->ii_AmCache</literal> (which will be NULL
|
data in <literal>indexInfo->ii_AmCache</literal> (which will be NULL
|
||||||
initially).
|
initially). After the index insertions complete, the memory will be freed
|
||||||
|
automatically. If additional cleanup is required (e.g. if the cache contains
|
||||||
|
buffers and tuple descriptors), the AM may define an optional function
|
||||||
|
<literal>indexinsertcleanup</literal>, called before the memory is released.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<programlisting>
|
||||||
|
void
|
||||||
|
aminsertcleanup (IndexInfo *indexInfo);
|
||||||
|
</programlisting>
|
||||||
|
Clean up state that was maintained across successive inserts in
|
||||||
|
<literal>indexInfo->ii_AmCache</literal>. This is useful if the data
|
||||||
|
requires additional cleanup steps, and simply releasing the memory is not
|
||||||
|
sufficient.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -58,6 +58,17 @@ typedef struct BrinBuildState
|
|||||||
BrinMemTuple *bs_dtuple;
|
BrinMemTuple *bs_dtuple;
|
||||||
} BrinBuildState;
|
} BrinBuildState;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use a BrinInsertState to capture running state spanning multiple
|
||||||
|
* brininsert invocations, within the same command.
|
||||||
|
*/
|
||||||
|
typedef struct BrinInsertState
|
||||||
|
{
|
||||||
|
BrinRevmap *bis_rmAccess;
|
||||||
|
BrinDesc *bis_desc;
|
||||||
|
BlockNumber bis_pages_per_range;
|
||||||
|
} BrinInsertState;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Struct used as "opaque" during index scans
|
* Struct used as "opaque" during index scans
|
||||||
*/
|
*/
|
||||||
@ -72,6 +83,7 @@ typedef struct BrinOpaque
|
|||||||
|
|
||||||
static BrinBuildState *initialize_brin_buildstate(Relation idxRel,
|
static BrinBuildState *initialize_brin_buildstate(Relation idxRel,
|
||||||
BrinRevmap *revmap, BlockNumber pagesPerRange);
|
BrinRevmap *revmap, BlockNumber pagesPerRange);
|
||||||
|
static BrinInsertState *initialize_brin_insertstate(Relation idxRel, IndexInfo *indexInfo);
|
||||||
static void terminate_brin_buildstate(BrinBuildState *state);
|
static void terminate_brin_buildstate(BrinBuildState *state);
|
||||||
static void brinsummarize(Relation index, Relation heapRel, BlockNumber pageRange,
|
static void brinsummarize(Relation index, Relation heapRel, BlockNumber pageRange,
|
||||||
bool include_partial, double *numSummarized, double *numExisting);
|
bool include_partial, double *numSummarized, double *numExisting);
|
||||||
@ -117,6 +129,7 @@ brinhandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->ambuild = brinbuild;
|
amroutine->ambuild = brinbuild;
|
||||||
amroutine->ambuildempty = brinbuildempty;
|
amroutine->ambuildempty = brinbuildempty;
|
||||||
amroutine->aminsert = brininsert;
|
amroutine->aminsert = brininsert;
|
||||||
|
amroutine->aminsertcleanup = brininsertcleanup;
|
||||||
amroutine->ambulkdelete = brinbulkdelete;
|
amroutine->ambulkdelete = brinbulkdelete;
|
||||||
amroutine->amvacuumcleanup = brinvacuumcleanup;
|
amroutine->amvacuumcleanup = brinvacuumcleanup;
|
||||||
amroutine->amcanreturn = NULL;
|
amroutine->amcanreturn = NULL;
|
||||||
@ -140,6 +153,27 @@ brinhandler(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_POINTER(amroutine);
|
PG_RETURN_POINTER(amroutine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize a BrinInsertState to maintain state to be used across multiple
|
||||||
|
* tuple inserts, within the same command.
|
||||||
|
*/
|
||||||
|
static BrinInsertState *
|
||||||
|
initialize_brin_insertstate(Relation idxRel, IndexInfo *indexInfo)
|
||||||
|
{
|
||||||
|
BrinInsertState *bistate;
|
||||||
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
|
oldcxt = MemoryContextSwitchTo(indexInfo->ii_Context);
|
||||||
|
bistate = palloc0(sizeof(BrinInsertState));
|
||||||
|
bistate->bis_desc = brin_build_desc(idxRel);
|
||||||
|
bistate->bis_rmAccess = brinRevmapInitialize(idxRel,
|
||||||
|
&bistate->bis_pages_per_range);
|
||||||
|
indexInfo->ii_AmCache = bistate;
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
|
||||||
|
return bistate;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A tuple in the heap is being inserted. To keep a brin index up to date,
|
* A tuple in the heap is being inserted. To keep a brin index up to date,
|
||||||
* we need to obtain the relevant index tuple and compare its stored values
|
* we need to obtain the relevant index tuple and compare its stored values
|
||||||
@ -162,14 +196,24 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
|
|||||||
BlockNumber pagesPerRange;
|
BlockNumber pagesPerRange;
|
||||||
BlockNumber origHeapBlk;
|
BlockNumber origHeapBlk;
|
||||||
BlockNumber heapBlk;
|
BlockNumber heapBlk;
|
||||||
BrinDesc *bdesc = (BrinDesc *) indexInfo->ii_AmCache;
|
BrinInsertState *bistate = (BrinInsertState *) indexInfo->ii_AmCache;
|
||||||
BrinRevmap *revmap;
|
BrinRevmap *revmap;
|
||||||
|
BrinDesc *bdesc;
|
||||||
Buffer buf = InvalidBuffer;
|
Buffer buf = InvalidBuffer;
|
||||||
MemoryContext tupcxt = NULL;
|
MemoryContext tupcxt = NULL;
|
||||||
MemoryContext oldcxt = CurrentMemoryContext;
|
MemoryContext oldcxt = CurrentMemoryContext;
|
||||||
bool autosummarize = BrinGetAutoSummarize(idxRel);
|
bool autosummarize = BrinGetAutoSummarize(idxRel);
|
||||||
|
|
||||||
revmap = brinRevmapInitialize(idxRel, &pagesPerRange);
|
/*
|
||||||
|
* If firt time through in this statement, initialize the insert state
|
||||||
|
* that we keep for all the inserts in the command.
|
||||||
|
*/
|
||||||
|
if (!bistate)
|
||||||
|
bistate = initialize_brin_insertstate(idxRel, indexInfo);
|
||||||
|
|
||||||
|
revmap = bistate->bis_rmAccess;
|
||||||
|
bdesc = bistate->bis_desc;
|
||||||
|
pagesPerRange = bistate->bis_pages_per_range;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* origHeapBlk is the block number where the insertion occurred. heapBlk
|
* origHeapBlk is the block number where the insertion occurred. heapBlk
|
||||||
@ -228,14 +272,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
|
|||||||
if (!brtup)
|
if (!brtup)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* First time through in this statement? */
|
|
||||||
if (bdesc == NULL)
|
|
||||||
{
|
|
||||||
MemoryContextSwitchTo(indexInfo->ii_Context);
|
|
||||||
bdesc = brin_build_desc(idxRel);
|
|
||||||
indexInfo->ii_AmCache = (void *) bdesc;
|
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
}
|
|
||||||
/* First time through in this brininsert call? */
|
/* First time through in this brininsert call? */
|
||||||
if (tupcxt == NULL)
|
if (tupcxt == NULL)
|
||||||
{
|
{
|
||||||
@ -306,7 +342,6 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
brinRevmapTerminate(revmap);
|
|
||||||
if (BufferIsValid(buf))
|
if (BufferIsValid(buf))
|
||||||
ReleaseBuffer(buf);
|
ReleaseBuffer(buf);
|
||||||
MemoryContextSwitchTo(oldcxt);
|
MemoryContextSwitchTo(oldcxt);
|
||||||
@ -316,6 +351,24 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback to clean up the BrinInsertState once all tuple inserts are done.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
brininsertcleanup(IndexInfo *indexInfo)
|
||||||
|
{
|
||||||
|
BrinInsertState *bistate = (BrinInsertState *) indexInfo->ii_AmCache;
|
||||||
|
|
||||||
|
Assert(bistate);
|
||||||
|
/*
|
||||||
|
* Clean up the revmap. Note that the brinDesc has already been cleaned up
|
||||||
|
* as part of its own memory context.
|
||||||
|
*/
|
||||||
|
brinRevmapTerminate(bistate->bis_rmAccess);
|
||||||
|
bistate->bis_rmAccess = NULL;
|
||||||
|
bistate->bis_desc = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize state for a BRIN index scan.
|
* Initialize state for a BRIN index scan.
|
||||||
*
|
*
|
||||||
|
@ -64,6 +64,7 @@ ginhandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->ambuild = ginbuild;
|
amroutine->ambuild = ginbuild;
|
||||||
amroutine->ambuildempty = ginbuildempty;
|
amroutine->ambuildempty = ginbuildempty;
|
||||||
amroutine->aminsert = gininsert;
|
amroutine->aminsert = gininsert;
|
||||||
|
amroutine->aminsertcleanup = NULL;
|
||||||
amroutine->ambulkdelete = ginbulkdelete;
|
amroutine->ambulkdelete = ginbulkdelete;
|
||||||
amroutine->amvacuumcleanup = ginvacuumcleanup;
|
amroutine->amvacuumcleanup = ginvacuumcleanup;
|
||||||
amroutine->amcanreturn = NULL;
|
amroutine->amcanreturn = NULL;
|
||||||
|
@ -86,6 +86,7 @@ gisthandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->ambuild = gistbuild;
|
amroutine->ambuild = gistbuild;
|
||||||
amroutine->ambuildempty = gistbuildempty;
|
amroutine->ambuildempty = gistbuildempty;
|
||||||
amroutine->aminsert = gistinsert;
|
amroutine->aminsert = gistinsert;
|
||||||
|
amroutine->aminsertcleanup = NULL;
|
||||||
amroutine->ambulkdelete = gistbulkdelete;
|
amroutine->ambulkdelete = gistbulkdelete;
|
||||||
amroutine->amvacuumcleanup = gistvacuumcleanup;
|
amroutine->amvacuumcleanup = gistvacuumcleanup;
|
||||||
amroutine->amcanreturn = gistcanreturn;
|
amroutine->amcanreturn = gistcanreturn;
|
||||||
|
@ -83,6 +83,7 @@ hashhandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->ambuild = hashbuild;
|
amroutine->ambuild = hashbuild;
|
||||||
amroutine->ambuildempty = hashbuildempty;
|
amroutine->ambuildempty = hashbuildempty;
|
||||||
amroutine->aminsert = hashinsert;
|
amroutine->aminsert = hashinsert;
|
||||||
|
amroutine->aminsertcleanup = NULL;
|
||||||
amroutine->ambulkdelete = hashbulkdelete;
|
amroutine->ambulkdelete = hashbulkdelete;
|
||||||
amroutine->amvacuumcleanup = hashvacuumcleanup;
|
amroutine->amvacuumcleanup = hashvacuumcleanup;
|
||||||
amroutine->amcanreturn = NULL;
|
amroutine->amcanreturn = NULL;
|
||||||
|
@ -196,6 +196,21 @@ index_insert(Relation indexRelation,
|
|||||||
indexInfo);
|
indexInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------
|
||||||
|
* index_insert_cleanup - clean up after all index inserts are done
|
||||||
|
* -------------------------
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
index_insert_cleanup(Relation indexRelation,
|
||||||
|
IndexInfo *indexInfo)
|
||||||
|
{
|
||||||
|
RELATION_CHECKS;
|
||||||
|
Assert(indexInfo);
|
||||||
|
|
||||||
|
if (indexRelation->rd_indam->aminsertcleanup)
|
||||||
|
indexRelation->rd_indam->aminsertcleanup(indexInfo);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* index_beginscan - start a scan of an index with amgettuple
|
* index_beginscan - start a scan of an index with amgettuple
|
||||||
*
|
*
|
||||||
|
@ -122,6 +122,7 @@ bthandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->ambuild = btbuild;
|
amroutine->ambuild = btbuild;
|
||||||
amroutine->ambuildempty = btbuildempty;
|
amroutine->ambuildempty = btbuildempty;
|
||||||
amroutine->aminsert = btinsert;
|
amroutine->aminsert = btinsert;
|
||||||
|
amroutine->aminsertcleanup = NULL;
|
||||||
amroutine->ambulkdelete = btbulkdelete;
|
amroutine->ambulkdelete = btbulkdelete;
|
||||||
amroutine->amvacuumcleanup = btvacuumcleanup;
|
amroutine->amvacuumcleanup = btvacuumcleanup;
|
||||||
amroutine->amcanreturn = btcanreturn;
|
amroutine->amcanreturn = btcanreturn;
|
||||||
|
@ -70,6 +70,7 @@ spghandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->ambuild = spgbuild;
|
amroutine->ambuild = spgbuild;
|
||||||
amroutine->ambuildempty = spgbuildempty;
|
amroutine->ambuildempty = spgbuildempty;
|
||||||
amroutine->aminsert = spginsert;
|
amroutine->aminsert = spginsert;
|
||||||
|
amroutine->aminsertcleanup = NULL;
|
||||||
amroutine->ambulkdelete = spgbulkdelete;
|
amroutine->ambulkdelete = spgbulkdelete;
|
||||||
amroutine->amvacuumcleanup = spgvacuumcleanup;
|
amroutine->amvacuumcleanup = spgvacuumcleanup;
|
||||||
amroutine->amcanreturn = spgcanreturn;
|
amroutine->amcanreturn = spgcanreturn;
|
||||||
|
@ -233,15 +233,20 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
|
|||||||
int i;
|
int i;
|
||||||
int numIndices;
|
int numIndices;
|
||||||
RelationPtr indexDescs;
|
RelationPtr indexDescs;
|
||||||
|
IndexInfo **indexInfos;
|
||||||
|
|
||||||
numIndices = resultRelInfo->ri_NumIndices;
|
numIndices = resultRelInfo->ri_NumIndices;
|
||||||
indexDescs = resultRelInfo->ri_IndexRelationDescs;
|
indexDescs = resultRelInfo->ri_IndexRelationDescs;
|
||||||
|
indexInfos = resultRelInfo->ri_IndexRelationInfo;
|
||||||
|
|
||||||
for (i = 0; i < numIndices; i++)
|
for (i = 0; i < numIndices; i++)
|
||||||
{
|
{
|
||||||
if (indexDescs[i] == NULL)
|
if (indexDescs[i] == NULL)
|
||||||
continue; /* shouldn't happen? */
|
continue; /* shouldn't happen? */
|
||||||
|
|
||||||
|
/* Give the index a chance to do some post-insert cleanup */
|
||||||
|
index_insert_cleanup(indexDescs[i], indexInfos[i]);
|
||||||
|
|
||||||
/* Drop lock acquired by ExecOpenIndices */
|
/* Drop lock acquired by ExecOpenIndices */
|
||||||
index_close(indexDescs[i], RowExclusiveLock);
|
index_close(indexDescs[i], RowExclusiveLock);
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,9 @@ typedef bool (*aminsert_function) (Relation indexRelation,
|
|||||||
bool indexUnchanged,
|
bool indexUnchanged,
|
||||||
struct IndexInfo *indexInfo);
|
struct IndexInfo *indexInfo);
|
||||||
|
|
||||||
|
/* cleanup after insert */
|
||||||
|
typedef void (*aminsertcleanup_function) (struct IndexInfo *indexInfo);
|
||||||
|
|
||||||
/* bulk delete */
|
/* bulk delete */
|
||||||
typedef IndexBulkDeleteResult *(*ambulkdelete_function) (IndexVacuumInfo *info,
|
typedef IndexBulkDeleteResult *(*ambulkdelete_function) (IndexVacuumInfo *info,
|
||||||
IndexBulkDeleteResult *stats,
|
IndexBulkDeleteResult *stats,
|
||||||
@ -261,6 +264,7 @@ typedef struct IndexAmRoutine
|
|||||||
ambuild_function ambuild;
|
ambuild_function ambuild;
|
||||||
ambuildempty_function ambuildempty;
|
ambuildempty_function ambuildempty;
|
||||||
aminsert_function aminsert;
|
aminsert_function aminsert;
|
||||||
|
aminsertcleanup_function aminsertcleanup;
|
||||||
ambulkdelete_function ambulkdelete;
|
ambulkdelete_function ambulkdelete;
|
||||||
amvacuumcleanup_function amvacuumcleanup;
|
amvacuumcleanup_function amvacuumcleanup;
|
||||||
amcanreturn_function amcanreturn; /* can be NULL */
|
amcanreturn_function amcanreturn; /* can be NULL */
|
||||||
|
@ -96,6 +96,7 @@ extern bool brininsert(Relation idxRel, Datum *values, bool *nulls,
|
|||||||
IndexUniqueCheck checkUnique,
|
IndexUniqueCheck checkUnique,
|
||||||
bool indexUnchanged,
|
bool indexUnchanged,
|
||||||
struct IndexInfo *indexInfo);
|
struct IndexInfo *indexInfo);
|
||||||
|
extern void brininsertcleanup(struct IndexInfo *indexInfo);
|
||||||
extern IndexScanDesc brinbeginscan(Relation r, int nkeys, int norderbys);
|
extern IndexScanDesc brinbeginscan(Relation r, int nkeys, int norderbys);
|
||||||
extern int64 bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
|
extern int64 bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
|
||||||
extern void brinrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
|
extern void brinrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
|
||||||
|
@ -148,6 +148,8 @@ extern bool index_insert(Relation indexRelation,
|
|||||||
IndexUniqueCheck checkUnique,
|
IndexUniqueCheck checkUnique,
|
||||||
bool indexUnchanged,
|
bool indexUnchanged,
|
||||||
struct IndexInfo *indexInfo);
|
struct IndexInfo *indexInfo);
|
||||||
|
extern void index_insert_cleanup(Relation indexRelation,
|
||||||
|
struct IndexInfo *indexInfo);
|
||||||
|
|
||||||
extern IndexScanDesc index_beginscan(Relation heapRelation,
|
extern IndexScanDesc index_beginscan(Relation heapRelation,
|
||||||
Relation indexRelation,
|
Relation indexRelation,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user