2626#include "catalog/pg_am.h"
2727#include "miscadmin.h"
2828#include "pgstat.h"
29+ #include "postmaster/autovacuum.h"
2930#include "storage/bufmgr.h"
3031#include "storage/freespace.h"
3132#include "utils/builtins.h"
@@ -60,10 +61,12 @@ typedef struct BrinOpaque
6061 BrinDesc * bo_bdesc ;
6162} BrinOpaque ;
6263
64+ #define BRIN_ALL_BLOCKRANGES InvalidBlockNumber
65+
6366static BrinBuildState * initialize_brin_buildstate (Relation idxRel ,
6467 BrinRevmap * revmap , BlockNumber pagesPerRange );
6568static void terminate_brin_buildstate (BrinBuildState * state );
66- static void brinsummarize (Relation index , Relation heapRel ,
69+ static void brinsummarize (Relation index , Relation heapRel , BlockNumber pageRange ,
6770 double * numSummarized , double * numExisting );
6871static void form_and_insert_tuple (BrinBuildState * state );
6972static void union_tuples (BrinDesc * bdesc , BrinMemTuple * a ,
@@ -126,8 +129,11 @@ brinhandler(PG_FUNCTION_ARGS)
126129 * with those of the new tuple. If the tuple values are not consistent with
127130 * the summary tuple, we need to update the index tuple.
128131 *
132+ * If autosummarization is enabled, check if we need to summarize the previous
133+ * page range.
134+ *
129135 * If the range is not currently summarized (i.e. the revmap returns NULL for
130- * it), there's nothing to do.
136+ * it), there's nothing to do for this tuple .
131137 */
132138bool
133139brininsert (Relation idxRel , Datum * values , bool * nulls ,
@@ -136,30 +142,59 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
136142 IndexInfo * indexInfo )
137143{
138144 BlockNumber pagesPerRange ;
145+ BlockNumber origHeapBlk ;
146+ BlockNumber heapBlk ;
139147 BrinDesc * bdesc = (BrinDesc * ) indexInfo -> ii_AmCache ;
140148 BrinRevmap * revmap ;
141149 Buffer buf = InvalidBuffer ;
142150 MemoryContext tupcxt = NULL ;
143151 MemoryContext oldcxt = CurrentMemoryContext ;
152+ bool autosummarize = BrinGetAutoSummarize (idxRel );
144153
145154 revmap = brinRevmapInitialize (idxRel , & pagesPerRange , NULL );
146155
156+ /*
157+ * origHeapBlk is the block number where the insertion occurred. heapBlk
158+ * is the first block in the corresponding page range.
159+ */
160+ origHeapBlk = ItemPointerGetBlockNumber (heaptid );
161+ heapBlk = (origHeapBlk / pagesPerRange ) * pagesPerRange ;
162+
147163 for (;;)
148164 {
149165 bool need_insert = false;
150166 OffsetNumber off ;
151167 BrinTuple * brtup ;
152168 BrinMemTuple * dtup ;
153- BlockNumber heapBlk ;
154169 int keyno ;
155170
156171 CHECK_FOR_INTERRUPTS ();
157172
158- heapBlk = ItemPointerGetBlockNumber (heaptid );
159- /* normalize the block number to be the first block in the range */
160- heapBlk = (heapBlk / pagesPerRange ) * pagesPerRange ;
161- brtup = brinGetTupleForHeapBlock (revmap , heapBlk , & buf , & off , NULL ,
162- BUFFER_LOCK_SHARE , NULL );
173+ /*
174+ * If auto-summarization is enabled and we just inserted the first
175+ * tuple into the first block of a new non-first page range, request a
176+ * summarization run of the previous range.
177+ */
178+ if (autosummarize &&
179+ heapBlk > 0 &&
180+ heapBlk == origHeapBlk &&
181+ ItemPointerGetOffsetNumber (heaptid ) == FirstOffsetNumber )
182+ {
183+ BlockNumber lastPageRange = heapBlk - 1 ;
184+ BrinTuple * lastPageTuple ;
185+
186+ lastPageTuple =
187+ brinGetTupleForHeapBlock (revmap , lastPageRange , & buf , & off ,
188+ NULL , BUFFER_LOCK_SHARE , NULL );
189+ if (!lastPageTuple )
190+ AutoVacuumRequestWork (AVW_BRINSummarizeRange ,
191+ RelationGetRelid (idxRel ),
192+ lastPageRange );
193+ brin_free_tuple (lastPageTuple );
194+ }
195+
196+ brtup = brinGetTupleForHeapBlock (revmap , heapBlk , & buf , & off ,
197+ NULL , BUFFER_LOCK_SHARE , NULL );
163198
164199 /* if range is unsummarized, there's nothing to do */
165200 if (!brtup )
@@ -747,7 +782,7 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
747782
748783 brin_vacuum_scan (info -> index , info -> strategy );
749784
750- brinsummarize (info -> index , heapRel ,
785+ brinsummarize (info -> index , heapRel , BRIN_ALL_BLOCKRANGES ,
751786 & stats -> num_index_tuples , & stats -> num_index_tuples );
752787
753788 heap_close (heapRel , AccessShareLock );
@@ -765,7 +800,8 @@ brinoptions(Datum reloptions, bool validate)
765800 BrinOptions * rdopts ;
766801 int numoptions ;
767802 static const relopt_parse_elt tab [] = {
768- {"pages_per_range" , RELOPT_TYPE_INT , offsetof(BrinOptions , pagesPerRange )}
803+ {"pages_per_range" , RELOPT_TYPE_INT , offsetof(BrinOptions , pagesPerRange )},
804+ {"autosummarize" , RELOPT_TYPE_BOOL , offsetof(BrinOptions , autosummarize )}
769805 };
770806
771807 options = parseRelOptions (reloptions , validate , RELOPT_KIND_BRIN ,
@@ -791,13 +827,40 @@ brinoptions(Datum reloptions, bool validate)
791827 */
792828Datum
793829brin_summarize_new_values (PG_FUNCTION_ARGS )
830+ {
831+ Datum relation = PG_GETARG_DATUM (0 );
832+
833+ return DirectFunctionCall2 (brin_summarize_range ,
834+ relation ,
835+ Int64GetDatum ((int64 ) BRIN_ALL_BLOCKRANGES ));
836+ }
837+
838+ /*
839+ * SQL-callable function to summarize the indicated page range, if not already
840+ * summarized. If the second argument is BRIN_ALL_BLOCKRANGES, all
841+ * unsummarized ranges are summarized.
842+ */
843+ Datum
844+ brin_summarize_range (PG_FUNCTION_ARGS )
794845{
795846 Oid indexoid = PG_GETARG_OID (0 );
847+ int64 heapBlk64 = PG_GETARG_INT64 (1 );
848+ BlockNumber heapBlk ;
796849 Oid heapoid ;
797850 Relation indexRel ;
798851 Relation heapRel ;
799852 double numSummarized = 0 ;
800853
854+ if (heapBlk64 > BRIN_ALL_BLOCKRANGES || heapBlk64 < 0 )
855+ {
856+ char * blk = psprintf (INT64_FORMAT , heapBlk64 );
857+
858+ ereport (ERROR ,
859+ (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
860+ errmsg ("block number out of range: %s" , blk )));
861+ }
862+ heapBlk = (BlockNumber ) heapBlk64 ;
863+
801864 /*
802865 * We must lock table before index to avoid deadlocks. However, if the
803866 * passed indexoid isn't an index then IndexGetRelation() will fail.
@@ -837,7 +900,7 @@ brin_summarize_new_values(PG_FUNCTION_ARGS)
837900 RelationGetRelationName (indexRel ))));
838901
839902 /* OK, do it */
840- brinsummarize (indexRel , heapRel , & numSummarized , NULL );
903+ brinsummarize (indexRel , heapRel , heapBlk , & numSummarized , NULL );
841904
842905 relation_close (indexRel , ShareUpdateExclusiveLock );
843906 relation_close (heapRel , ShareUpdateExclusiveLock );
@@ -1063,17 +1126,17 @@ summarize_range(IndexInfo *indexInfo, BrinBuildState *state, Relation heapRel,
10631126}
10641127
10651128/*
1066- * Scan a complete BRIN index, and summarize each page range that's not already
1067- * summarized. The index and heap must have been locked by caller in at
1068- * least ShareUpdateExclusiveLock mode .
1129+ * Summarize page ranges that are not already summarized. If pageRange is
1130+ * BRIN_ALL_BLOCKRANGES then the whole table is scanned; otherwise, only the
1131+ * page range containing the given heap page number is scanned .
10691132 *
10701133 * For each new index tuple inserted, *numSummarized (if not NULL) is
10711134 * incremented; for each existing tuple, *numExisting (if not NULL) is
10721135 * incremented.
10731136 */
10741137static void
1075- brinsummarize (Relation index , Relation heapRel , double * numSummarized ,
1076- double * numExisting )
1138+ brinsummarize (Relation index , Relation heapRel , BlockNumber pageRange ,
1139+ double * numSummarized , double * numExisting )
10771140{
10781141 BrinRevmap * revmap ;
10791142 BrinBuildState * state = NULL ;
@@ -1082,15 +1145,40 @@ brinsummarize(Relation index, Relation heapRel, double *numSummarized,
10821145 BlockNumber heapBlk ;
10831146 BlockNumber pagesPerRange ;
10841147 Buffer buf ;
1148+ BlockNumber startBlk ;
1149+ BlockNumber endBlk ;
1150+
1151+ /* determine range of pages to process; nothing to do for an empty table */
1152+ heapNumBlocks = RelationGetNumberOfBlocks (heapRel );
1153+ if (heapNumBlocks == 0 )
1154+ return ;
10851155
10861156 revmap = brinRevmapInitialize (index , & pagesPerRange , NULL );
10871157
1158+ if (pageRange == BRIN_ALL_BLOCKRANGES )
1159+ {
1160+ startBlk = 0 ;
1161+ endBlk = heapNumBlocks ;
1162+ }
1163+ else
1164+ {
1165+ startBlk = (pageRange / pagesPerRange ) * pagesPerRange ;
1166+ /* Nothing to do if start point is beyond end of table */
1167+ if (startBlk > heapNumBlocks )
1168+ {
1169+ brinRevmapTerminate (revmap );
1170+ return ;
1171+ }
1172+ endBlk = startBlk + pagesPerRange ;
1173+ if (endBlk > heapNumBlocks )
1174+ endBlk = heapNumBlocks ;
1175+ }
1176+
10881177 /*
10891178 * Scan the revmap to find unsummarized items.
10901179 */
10911180 buf = InvalidBuffer ;
1092- heapNumBlocks = RelationGetNumberOfBlocks (heapRel );
1093- for (heapBlk = 0 ; heapBlk < heapNumBlocks ; heapBlk += pagesPerRange )
1181+ for (heapBlk = startBlk ; heapBlk < endBlk ; heapBlk += pagesPerRange )
10941182 {
10951183 BrinTuple * tup ;
10961184 OffsetNumber off ;
0 commit comments