Expand palloc/pg_malloc API

Started by Peter Eisentrautover 3 years ago22 messages
#1Peter Eisentraut
peter.eisentraut@enterprisedb.com
1 attachment(s)

This adds additional variants of palloc, pg_malloc, etc. that
encapsulate common usage patterns and provide more type safety.

Examples:

-   result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+   result = palloc_obj(IndexBuildResult);
-   collector->tuples = (IndexTuple *) palloc(sizeof(IndexTuple) *
                                               collector->lentuples);
+   collector->tuples = palloc_array(IndexTuple, collector->lentuples);

One common point is that the new interfaces all have a return type that
automatically matches what they are allocating, so you don't need any
casts nor have to manually make sure the size matches the expected
result. Besides the additional safety, the notation is also more
compact, as you can see above.

Inspired by the talloc library.

The interesting changes are in fe_memutils.h and palloc.h. The rest of
the patch is just randomly sprinkled examples to test/validate the new
additions.

Attachments:

0001-Expand-palloc-pg_malloc-API.patchtext/plain; charset=UTF-8; name=0001-Expand-palloc-pg_malloc-API.patchDownload
From 9ba5753228e0fe9c1eca779e92795a29b23146af Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 17 May 2022 13:28:58 +0200
Subject: [PATCH] Expand palloc/pg_malloc API

This adds additional variants of palloc, pg_malloc, etc. that
encapsulate common usage patterns and provide more type safety.
Inspired by talloc library.
---
 src/include/common/fe_memutils.h | 46 ++++++++++++++++++++++++++
 src/include/utils/palloc.h       | 33 ++++++++++++++++++
 src/backend/access/brin/brin.c   | 14 ++++----
 src/backend/access/gin/ginfast.c | 17 ++++------
 src/backend/commands/indexcmds.c | 44 ++++++++++++------------
 src/backend/executor/nodeHash.c  | 57 +++++++++++++-------------------
 src/bin/pg_dump/common.c         | 24 +++++---------
 src/bin/pg_dump/pg_backup_tar.c  | 10 +++---
 src/bin/psql/startup.c           |  6 ++--
 src/common/config_info.c         |  2 +-
 src/common/controldata_utils.c   |  2 +-
 contrib/dblink/dblink.c          | 12 +++----
 12 files changed, 163 insertions(+), 104 deletions(-)

diff --git a/src/include/common/fe_memutils.h b/src/include/common/fe_memutils.h
index 0a4c9126ea..993e0b07cd 100644
--- a/src/include/common/fe_memutils.h
+++ b/src/include/common/fe_memutils.h
@@ -29,6 +29,39 @@ extern void *pg_malloc_extended(size_t size, int flags);
 extern void *pg_realloc(void *pointer, size_t size);
 extern void pg_free(void *pointer);
 
+/*
+ * Variants with easier notation and more type safety
+ */
+
+/*
+ * Allocate space for one object of type "type"
+ */
+#define pg_malloc_obj(type) ((type *) pg_malloc(sizeof(type)))
+#define pg_malloc0_obj(type) ((type *) pg_malloc0(sizeof(type)))
+
+/*
+ * Allocate space for one object of what "ptr" points to
+ */
+#ifdef HAVE_TYPEOF
+#define pg_malloc_ptrtype(ptr) ((typeof(ptr)) pg_malloc(sizeof(*(ptr))))
+#define pg_malloc0_ptrtype(ptr) ((typeof(ptr)) pg_malloc0(sizeof(*(ptr))))
+#else
+#define pg_malloc_ptrtype(ptr) pg_malloc(sizeof(*(ptr)))
+#define pg_malloc0_ptrtype(ptr) pg_malloc0(sizeof(*(ptr)))
+#endif
+
+/*
+ * Allocate space for "count" objects of type "type"
+ */
+#define pg_malloc_array(type, count) ((type *) pg_malloc(sizeof(type) * (count)))
+#define pg_malloc0_array(type, count) ((type *) pg_malloc0(sizeof(type) * (count)))
+
+/*
+ * Change size of allocation pointed to by "pointer" to have space for "count"
+ * objects of type "type"
+ */
+#define pg_realloc_array(pointer, type, count) ((type *) pg_realloc(pointer, sizeof(type) * (count)))
+
 /* Equivalent functions, deliberately named the same as backend functions */
 extern char *pstrdup(const char *in);
 extern char *pnstrdup(const char *in, Size size);
@@ -38,6 +71,19 @@ extern void *palloc_extended(Size size, int flags);
 extern void *repalloc(void *pointer, Size size);
 extern void pfree(void *pointer);
 
+#define palloc_obj(type) ((type *) palloc(sizeof(type)))
+#define palloc0_obj(type) ((type *) palloc0(sizeof(type)))
+#ifdef HAVE_TYPEOF
+#define palloc_ptrtype(ptr) ((typeof(ptr)) palloc(sizeof(*(ptr))))
+#define palloc0_ptrtype(ptr) ((typeof(ptr)) palloc0(sizeof(*(ptr))))
+#else
+#define palloc_ptrtype(ptr) palloc(sizeof(*(ptr)))
+#define palloc0_ptrtype(ptr) palloc0(sizeof(*(ptr)))
+#endif
+#define palloc_array(type, count) ((type *) palloc(sizeof(type) * (count)))
+#define palloc0_array(type, count) ((type *) palloc0(sizeof(type) * (count)))
+#define repalloc_array(pointer, type, count) ((type *) repalloc(pointer, sizeof(type) * (count)))
+
 /* sprintf into a palloc'd buffer --- these are in psprintf.c */
 extern char *psprintf(const char *fmt,...) pg_attribute_printf(1, 2);
 extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0);
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index 332575f518..cbc9c11ffa 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -80,6 +80,39 @@ extern void *palloc_extended(Size size, int flags);
 extern pg_nodiscard void *repalloc(void *pointer, Size size);
 extern void pfree(void *pointer);
 
+/*
+ * Variants with easier notation and more type safety
+ */
+
+/*
+ * Allocate space for one object of type "type"
+ */
+#define palloc_obj(type) ((type *) palloc(sizeof(type)))
+#define palloc0_obj(type) ((type *) palloc0(sizeof(type)))
+
+/*
+ * Allocate space for one object of what "ptr" points to
+ */
+#ifdef HAVE_TYPEOF
+#define palloc_ptrtype(ptr) ((typeof(ptr)) palloc(sizeof(*(ptr))))
+#define palloc0_ptrtype(ptr) ((typeof(ptr)) palloc0(sizeof(*(ptr))))
+#else
+#define palloc_ptrtype(ptr) palloc(sizeof(*(ptr)))
+#define palloc0_ptrtype(ptr) palloc0(sizeof(*(ptr)))
+#endif
+
+/*
+ * Allocate space for "count" objects of type "type"
+ */
+#define palloc_array(type, count) ((type *) palloc(sizeof(type) * (count)))
+#define palloc0_array(type, count) ((type *) palloc0(sizeof(type) * (count)))
+
+/*
+ * Change size of allocation pointed to by "pointer" to have space for "count"
+ * objects of type "type"
+ */
+#define repalloc_array(pointer, type, count) ((type *) repalloc(pointer, sizeof(type) * (count)))
+
 /*
  * The result of palloc() is always word-aligned, so we can skip testing
  * alignment of the pointer when deciding which MemSet variant to use.
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 52f171772d..e759bd54fa 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -330,7 +330,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys)
 
 	scan = RelationGetIndexScan(r, nkeys, norderbys);
 
-	opaque = (BrinOpaque *) palloc(sizeof(BrinOpaque));
+	opaque = palloc_obj(BrinOpaque);
 	opaque->bo_rmAccess = brinRevmapInitialize(r, &opaque->bo_pagesPerRange,
 											   scan->xs_snapshot);
 	opaque->bo_bdesc = brin_build_desc(r);
@@ -396,7 +396,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
 	 * don't look them up here; we do that lazily the first time we see a scan
 	 * key reference each of them.  We rely on zeroing fn_oid to InvalidOid.
 	 */
-	consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts);
+	consistentFn = palloc0_array(FmgrInfo, bdesc->bd_tupdesc->natts);
 
 	/*
 	 * Make room for per-attribute lists of scan keys that we'll pass to the
@@ -883,7 +883,7 @@ brinbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	/*
 	 * Return statistics
 	 */
-	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+	result = palloc_obj(IndexBuildResult);
 
 	result->heap_tuples = reltuples;
 	result->index_tuples = idxtuples;
@@ -927,7 +927,7 @@ brinbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 {
 	/* allocate stats if first time through, else re-use existing struct */
 	if (stats == NULL)
-		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+		stats = palloc0_obj(IndexBulkDeleteResult);
 
 	return stats;
 }
@@ -946,7 +946,7 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 		return stats;
 
 	if (!stats)
-		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+		stats = palloc0_obj(IndexBulkDeleteResult);
 	stats->num_pages = RelationGetNumberOfBlocks(info->index);
 	/* rest of stats is initialized by zeroing */
 
@@ -1200,7 +1200,7 @@ brin_build_desc(Relation rel)
 	 * Obtain BrinOpcInfo for each indexed column.  While at it, accumulate
 	 * the number of columns stored, since the number is opclass-defined.
 	 */
-	opcinfo = (BrinOpcInfo **) palloc(sizeof(BrinOpcInfo *) * tupdesc->natts);
+	opcinfo = palloc_array(BrinOpcInfo*, tupdesc->natts);
 	for (keyno = 0; keyno < tupdesc->natts; keyno++)
 	{
 		FmgrInfo   *opcInfoFn;
@@ -1272,7 +1272,7 @@ initialize_brin_buildstate(Relation idxRel, BrinRevmap *revmap,
 {
 	BrinBuildState *state;
 
-	state = palloc(sizeof(BrinBuildState));
+	state = palloc_obj(BrinBuildState);
 
 	state->bs_irel = idxRel;
 	state->bs_numtuples = 0;
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index 7409fdc165..2999c94e2b 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -505,7 +505,7 @@ ginHeapTupleFastCollect(GinState *ginstate,
 		 * resizing (since palloc likes powers of 2).
 		 */
 		collector->lentuples = pg_nextpower2_32(Max(16, nentries));
-		collector->tuples = (IndexTuple *) palloc(sizeof(IndexTuple) * collector->lentuples);
+		collector->tuples = palloc_array(IndexTuple, collector->lentuples);
 	}
 	else if (collector->lentuples < collector->ntuples + nentries)
 	{
@@ -515,8 +515,8 @@ ginHeapTupleFastCollect(GinState *ginstate,
 		 * MaxAllocSize/sizeof(IndexTuple), causing an error in repalloc.
 		 */
 		collector->lentuples = pg_nextpower2_32(collector->ntuples + nentries);
-		collector->tuples = (IndexTuple *) repalloc(collector->tuples,
-													sizeof(IndexTuple) * collector->lentuples);
+		collector->tuples = repalloc_array(collector->tuples,
+										   IndexTuple, collector->lentuples);
 	}
 
 	/*
@@ -665,9 +665,8 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
 static void
 initKeyArray(KeyArray *keys, int32 maxvalues)
 {
-	keys->keys = (Datum *) palloc(sizeof(Datum) * maxvalues);
-	keys->categories = (GinNullCategory *)
-		palloc(sizeof(GinNullCategory) * maxvalues);
+	keys->keys = palloc_array(Datum, maxvalues);
+	keys->categories = palloc_array(GinNullCategory, maxvalues);
 	keys->nvalues = 0;
 	keys->maxvalues = maxvalues;
 }
@@ -679,10 +678,8 @@ addDatum(KeyArray *keys, Datum datum, GinNullCategory category)
 	if (keys->nvalues >= keys->maxvalues)
 	{
 		keys->maxvalues *= 2;
-		keys->keys = (Datum *)
-			repalloc(keys->keys, sizeof(Datum) * keys->maxvalues);
-		keys->categories = (GinNullCategory *)
-			repalloc(keys->categories, sizeof(GinNullCategory) * keys->maxvalues);
+		keys->keys = repalloc_array(keys->keys, Datum, keys->maxvalues);
+		keys->categories = repalloc_array(keys->categories, GinNullCategory, keys->maxvalues);
 	}
 
 	keys->keys[keys->nvalues] = datum;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index eac13ac0b7..25bd691ee0 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -227,10 +227,10 @@ CheckIndexCompatible(Oid oldId,
 	 */
 	indexInfo = makeIndexInfo(numberOfAttributes, numberOfAttributes,
 							  accessMethodId, NIL, NIL, false, false, false, false);
-	typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
+	typeObjectId = palloc_array(Oid, numberOfAttributes);
+	collationObjectId = palloc_array(Oid, numberOfAttributes);
+	classObjectId = palloc_array(Oid, numberOfAttributes);
+	coloptions = palloc_array(int16, numberOfAttributes);
 	ComputeIndexAttrs(indexInfo,
 					  typeObjectId, collationObjectId, classObjectId,
 					  coloptions, attributeList,
@@ -881,10 +881,10 @@ DefineIndex(Oid relationId,
 							  !concurrent,
 							  concurrent);
 
-	typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
+	typeObjectId = palloc_array(Oid, numberOfAttributes);
+	collationObjectId = palloc_array(Oid, numberOfAttributes);
+	classObjectId = palloc_array(Oid, numberOfAttributes);
+	coloptions = palloc_array(int16, numberOfAttributes);
 	ComputeIndexAttrs(indexInfo,
 					  typeObjectId, collationObjectId, classObjectId,
 					  coloptions, allIndexParams,
@@ -1198,7 +1198,7 @@ DefineIndex(Oid relationId,
 		if ((!stmt->relation || stmt->relation->inh) && partdesc->nparts > 0)
 		{
 			int			nparts = partdesc->nparts;
-			Oid		   *part_oids = palloc(sizeof(Oid) * nparts);
+			Oid		   *part_oids = palloc_array(Oid, nparts);
 			bool		invalidate_parent = false;
 			TupleDesc	parentDesc;
 			Oid		   *opfamOids;
@@ -1209,7 +1209,7 @@ DefineIndex(Oid relationId,
 			memcpy(part_oids, partdesc->oids, sizeof(Oid) * nparts);
 
 			parentDesc = RelationGetDescr(rel);
-			opfamOids = palloc(sizeof(Oid) * numberOfKeyAttributes);
+			opfamOids = palloc_array(Oid, numberOfKeyAttributes);
 			for (i = 0; i < numberOfKeyAttributes; i++)
 				opfamOids[i] = get_opclass_family(classObjectId[i]);
 
@@ -1754,9 +1754,9 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 	if (exclusionOpNames)
 	{
 		Assert(list_length(exclusionOpNames) == nkeycols);
-		indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * nkeycols);
-		indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * nkeycols);
-		indexInfo->ii_ExclusionStrats = (uint16 *) palloc(sizeof(uint16) * nkeycols);
+		indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
+		indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
+		indexInfo->ii_ExclusionStrats = palloc_array(uint16, nkeycols);
 		nextExclOp = list_head(exclusionOpNames);
 	}
 	else
@@ -2036,7 +2036,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 
 			if (!indexInfo->ii_OpclassOptions)
 				indexInfo->ii_OpclassOptions =
-					palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+					palloc0_array(Datum, indexInfo->ii_NumIndexAttrs);
 
 			indexInfo->ii_OpclassOptions[attn] =
 				transformRelOptions((Datum) 0, attribute->opclassopts,
@@ -3372,7 +3372,7 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 						/* Save the list of relation OIDs in private context */
 						oldcontext = MemoryContextSwitchTo(private_context);
 
-						idx = palloc(sizeof(ReindexIndexInfo));
+						idx = palloc_obj(ReindexIndexInfo);
 						idx->indexId = cellOid;
 						/* other fields set later */
 
@@ -3421,7 +3421,7 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 							 */
 							oldcontext = MemoryContextSwitchTo(private_context);
 
-							idx = palloc(sizeof(ReindexIndexInfo));
+							idx = palloc_obj(ReindexIndexInfo);
 							idx->indexId = cellOid;
 							indexIds = lappend(indexIds, idx);
 							/* other fields set later */
@@ -3502,7 +3502,7 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 				 * Save the list of relation OIDs in private context.  Note
 				 * that invalid indexes are allowed here.
 				 */
-				idx = palloc(sizeof(ReindexIndexInfo));
+				idx = palloc_obj(ReindexIndexInfo);
 				idx->indexId = relationOid;
 				indexIds = lappend(indexIds, idx);
 				/* other fields set later */
@@ -3647,7 +3647,7 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 		 */
 		oldcontext = MemoryContextSwitchTo(private_context);
 
-		newidx = palloc(sizeof(ReindexIndexInfo));
+		newidx = palloc_obj(ReindexIndexInfo);
 		newidx->indexId = newIndexId;
 		newidx->safe = idx->safe;
 		newidx->tableId = idx->tableId;
@@ -3661,10 +3661,10 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 		 * avoid multiple locks taken on the same relation, instead we rely on
 		 * parentRelationIds built earlier.
 		 */
-		lockrelid = palloc(sizeof(*lockrelid));
+		lockrelid = palloc_ptrtype(lockrelid);
 		*lockrelid = indexRel->rd_lockInfo.lockRelId;
 		relationLocks = lappend(relationLocks, lockrelid);
-		lockrelid = palloc(sizeof(*lockrelid));
+		lockrelid = palloc_ptrtype(lockrelid);
 		*lockrelid = newIndexRel->rd_lockInfo.lockRelId;
 		relationLocks = lappend(relationLocks, lockrelid);
 
@@ -3696,11 +3696,11 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 		oldcontext = MemoryContextSwitchTo(private_context);
 
 		/* Add lockrelid of heap relation to the list of locked relations */
-		lockrelid = palloc(sizeof(*lockrelid));
+		lockrelid = palloc_ptrtype(lockrelid);
 		*lockrelid = heapRelation->rd_lockInfo.lockRelId;
 		relationLocks = lappend(relationLocks, lockrelid);
 
-		heaplocktag = (LOCKTAG *) palloc(sizeof(LOCKTAG));
+		heaplocktag = palloc_obj(LOCKTAG);
 
 		/* Save the LOCKTAG for this parent relation for the wait phase */
 		SET_LOCKTAG_RELATION(*heaplocktag, lockrelid->dbId, lockrelid->relId);
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 3510a4247c..0535161dc1 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -479,7 +479,7 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
 	 * per-query memory context.  Everything else should be kept inside the
 	 * subsidiary hashCxt or batchCxt.
 	 */
-	hashtable = (HashJoinTable) palloc(sizeof(HashJoinTableData));
+	hashtable = palloc_obj(HashJoinTableData);
 	hashtable->nbuckets = nbuckets;
 	hashtable->nbuckets_original = nbuckets;
 	hashtable->nbuckets_optimal = nbuckets;
@@ -540,12 +540,10 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
 	 * remember whether the join operators are strict.
 	 */
 	nkeys = list_length(hashOperators);
-	hashtable->outer_hashfunctions =
-		(FmgrInfo *) palloc(nkeys * sizeof(FmgrInfo));
-	hashtable->inner_hashfunctions =
-		(FmgrInfo *) palloc(nkeys * sizeof(FmgrInfo));
-	hashtable->hashStrict = (bool *) palloc(nkeys * sizeof(bool));
-	hashtable->collations = (Oid *) palloc(nkeys * sizeof(Oid));
+	hashtable->outer_hashfunctions = palloc_array(FmgrInfo, nkeys);
+	hashtable->inner_hashfunctions = palloc_array(FmgrInfo, nkeys);
+	hashtable->hashStrict = palloc_array(bool, nkeys);
+	hashtable->collations = palloc_array(Oid, nkeys);
 	i = 0;
 	forboth(ho, hashOperators, hc, hashCollations)
 	{
@@ -569,10 +567,8 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
 		 * allocate and initialize the file arrays in hashCxt (not needed for
 		 * parallel case which uses shared tuplestores instead of raw files)
 		 */
-		hashtable->innerBatchFile = (BufFile **)
-			palloc0(nbatch * sizeof(BufFile *));
-		hashtable->outerBatchFile = (BufFile **)
-			palloc0(nbatch * sizeof(BufFile *));
+		hashtable->innerBatchFile = palloc0_array(BufFile *, nbatch);
+		hashtable->outerBatchFile = palloc0_array(BufFile *, nbatch);
 		/* The files will not be opened until needed... */
 		/* ... but make sure we have temp tablespaces established for them */
 		PrepareTempTablespaces();
@@ -636,8 +632,7 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
 		 */
 		MemoryContextSwitchTo(hashtable->batchCxt);
 
-		hashtable->buckets.unshared = (HashJoinTuple *)
-			palloc0(nbuckets * sizeof(HashJoinTuple));
+		hashtable->buckets.unshared = palloc0_array(HashJoinTuple, nbuckets);
 
 		/*
 		 * Set up for skew optimization, if possible and there's a need for
@@ -934,20 +929,16 @@ ExecHashIncreaseNumBatches(HashJoinTable hashtable)
 	if (hashtable->innerBatchFile == NULL)
 	{
 		/* we had no file arrays before */
-		hashtable->innerBatchFile = (BufFile **)
-			palloc0(nbatch * sizeof(BufFile *));
-		hashtable->outerBatchFile = (BufFile **)
-			palloc0(nbatch * sizeof(BufFile *));
+		hashtable->innerBatchFile = palloc0_array(BufFile *, nbatch);
+		hashtable->outerBatchFile = palloc0_array(BufFile *, nbatch);
 		/* time to establish the temp tablespaces, too */
 		PrepareTempTablespaces();
 	}
 	else
 	{
 		/* enlarge arrays and zero out added entries */
-		hashtable->innerBatchFile = (BufFile **)
-			repalloc(hashtable->innerBatchFile, nbatch * sizeof(BufFile *));
-		hashtable->outerBatchFile = (BufFile **)
-			repalloc(hashtable->outerBatchFile, nbatch * sizeof(BufFile *));
+		hashtable->innerBatchFile = repalloc_array(hashtable->innerBatchFile, BufFile *, nbatch);
+		hashtable->outerBatchFile = repalloc_array(hashtable->outerBatchFile, BufFile *, nbatch);
 		MemSet(hashtable->innerBatchFile + oldnbatch, 0,
 			   (nbatch - oldnbatch) * sizeof(BufFile *));
 		MemSet(hashtable->outerBatchFile + oldnbatch, 0,
@@ -974,8 +965,8 @@ ExecHashIncreaseNumBatches(HashJoinTable hashtable)
 		hashtable->log2_nbuckets = hashtable->log2_nbuckets_optimal;
 
 		hashtable->buckets.unshared =
-			repalloc(hashtable->buckets.unshared,
-					 sizeof(HashJoinTuple) * hashtable->nbuckets);
+			repalloc_array(hashtable->buckets.unshared,
+						   HashJoinTuple, hashtable->nbuckets);
 	}
 
 	/*
@@ -1369,7 +1360,7 @@ ExecParallelHashRepartitionRest(HashJoinTable hashtable)
 	/* Get our hands on the previous generation of batches. */
 	old_batches = (ParallelHashJoinBatch *)
 		dsa_get_address(hashtable->area, pstate->old_batches);
-	old_inner_tuples = palloc0(sizeof(SharedTuplestoreAccessor *) * old_nbatch);
+	old_inner_tuples = palloc0_array(SharedTuplestoreAccessor *, old_nbatch);
 	for (i = 1; i < old_nbatch; ++i)
 	{
 		ParallelHashJoinBatch *shared =
@@ -1475,8 +1466,8 @@ ExecHashIncreaseNumBuckets(HashJoinTable hashtable)
 	 * chunks)
 	 */
 	hashtable->buckets.unshared =
-		(HashJoinTuple *) repalloc(hashtable->buckets.unshared,
-								   hashtable->nbuckets * sizeof(HashJoinTuple));
+		repalloc_array(hashtable->buckets.unshared,
+					   HashJoinTuple, hashtable->nbuckets);
 
 	memset(hashtable->buckets.unshared, 0,
 		   hashtable->nbuckets * sizeof(HashJoinTuple));
@@ -2168,8 +2159,7 @@ ExecHashTableReset(HashJoinTable hashtable)
 	oldcxt = MemoryContextSwitchTo(hashtable->batchCxt);
 
 	/* Reallocate and reinitialize the hash bucket headers. */
-	hashtable->buckets.unshared = (HashJoinTuple *)
-		palloc0(nbuckets * sizeof(HashJoinTuple));
+	hashtable->buckets.unshared = palloc0_array(HashJoinTuple, nbuckets);
 
 	hashtable->spaceUsed = 0;
 
@@ -2662,8 +2652,7 @@ ExecShutdownHash(HashState *node)
 {
 	/* Allocate save space if EXPLAIN'ing and we didn't do so already */
 	if (node->ps.instrument && !node->hinstrument)
-		node->hinstrument = (HashInstrumentation *)
-			palloc0(sizeof(HashInstrumentation));
+		node->hinstrument = palloc0_obj(HashInstrumentation);
 	/* Now accumulate data for the current (final) hash table */
 	if (node->hinstrument && node->hashtable)
 		ExecHashAccumInstrumentation(node->hinstrument, node->hashtable);
@@ -2973,8 +2962,8 @@ ExecParallelHashJoinSetUpBatches(HashJoinTable hashtable, int nbatch)
 
 	/* Allocate this backend's accessor array. */
 	hashtable->nbatch = nbatch;
-	hashtable->batches = (ParallelHashJoinBatchAccessor *)
-		palloc0(sizeof(ParallelHashJoinBatchAccessor) * hashtable->nbatch);
+	hashtable->batches =
+		palloc0_array(ParallelHashJoinBatchAccessor, hashtable->nbatch);
 
 	/* Set up the shared state, tuplestores and backend-local accessors. */
 	for (i = 0; i < hashtable->nbatch; ++i)
@@ -3079,8 +3068,8 @@ ExecParallelHashEnsureBatchAccessors(HashJoinTable hashtable)
 
 	/* Allocate this backend's accessor array. */
 	hashtable->nbatch = pstate->nbatch;
-	hashtable->batches = (ParallelHashJoinBatchAccessor *)
-		palloc0(sizeof(ParallelHashJoinBatchAccessor) * hashtable->nbatch);
+	hashtable->batches =
+		palloc0_array(ParallelHashJoinBatchAccessor, hashtable->nbatch);
 
 	/* Find the base of the pseudo-array of ParallelHashJoinBatch objects. */
 	batches = (ParallelHashJoinBatch *)
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 794e6e7ce9..c799dc4300 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -400,7 +400,7 @@ flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 			if (parentidx == NULL)
 				continue;
 
-			attachinfo = (IndexAttachInfo *) pg_malloc(sizeof(IndexAttachInfo));
+			attachinfo = pg_malloc_obj(IndexAttachInfo);
 
 			attachinfo->dobj.objType = DO_INDEX_ATTACH;
 			attachinfo->dobj.catId.tableoid = 0;
@@ -530,7 +530,7 @@ flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables)
 			{
 				AttrDefInfo *attrDef;
 
-				attrDef = (AttrDefInfo *) pg_malloc(sizeof(AttrDefInfo));
+				attrDef = pg_malloc_obj(AttrDefInfo);
 				attrDef->dobj.objType = DO_ATTRDEF;
 				attrDef->dobj.catId.tableoid = 0;
 				attrDef->dobj.catId.oid = 0;
@@ -600,14 +600,12 @@ AssignDumpId(DumpableObject *dobj)
 		if (allocedDumpIds <= 0)
 		{
 			newAlloc = 256;
-			dumpIdMap = (DumpableObject **)
-				pg_malloc(newAlloc * sizeof(DumpableObject *));
+			dumpIdMap = pg_malloc_array(DumpableObject *, newAlloc);
 		}
 		else
 		{
 			newAlloc = allocedDumpIds * 2;
-			dumpIdMap = (DumpableObject **)
-				pg_realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *));
+			dumpIdMap = pg_realloc_array(dumpIdMap, DumpableObject *, newAlloc);
 		}
 		memset(dumpIdMap + allocedDumpIds, 0,
 			   (newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
@@ -700,8 +698,7 @@ getDumpableObjects(DumpableObject ***objs, int *numObjs)
 	int			i,
 				j;
 
-	*objs = (DumpableObject **)
-		pg_malloc(allocedDumpIds * sizeof(DumpableObject *));
+	*objs = pg_malloc_array(DumpableObject *, allocedDumpIds);
 	j = 0;
 	for (i = 1; i < allocedDumpIds; i++)
 	{
@@ -724,15 +721,13 @@ addObjectDependency(DumpableObject *dobj, DumpId refId)
 		if (dobj->allocDeps <= 0)
 		{
 			dobj->allocDeps = 16;
-			dobj->dependencies = (DumpId *)
-				pg_malloc(dobj->allocDeps * sizeof(DumpId));
+			dobj->dependencies = pg_malloc_array(DumpId, dobj->allocDeps);
 		}
 		else
 		{
 			dobj->allocDeps *= 2;
-			dobj->dependencies = (DumpId *)
-				pg_realloc(dobj->dependencies,
-						   dobj->allocDeps * sizeof(DumpId));
+			dobj->dependencies = pg_realloc_array(dobj->dependencies,
+												  DumpId, dobj->allocDeps);
 		}
 	}
 	dobj->dependencies[dobj->nDeps++] = refId;
@@ -990,8 +985,7 @@ findParentsByOid(TableInfo *self,
 
 	if (numParents > 0)
 	{
-		self->parents = (TableInfo **)
-			pg_malloc(sizeof(TableInfo *) * numParents);
+		self->parents = pg_malloc_array(TableInfo *, numParents);
 		j = 0;
 		for (i = 0; i < numInherits; i++)
 		{
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 39d71badb7..2443d99e69 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -151,7 +151,7 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
 	/*
 	 * Set up some special context used in compressing data.
 	 */
-	ctx = (lclContext *) pg_malloc0(sizeof(lclContext));
+	ctx = pg_malloc0_obj(lclContext);
 	AH->formatData = (void *) ctx;
 	ctx->filePos = 0;
 	ctx->isSpecialScript = 0;
@@ -240,7 +240,7 @@ _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
 	lclTocEntry *ctx;
 	char		fn[K_STD_BUF_SIZE];
 
-	ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
+	ctx = pg_malloc0_obj(lclTocEntry);
 	if (te->dataDumper != NULL)
 	{
 		snprintf(fn, sizeof(fn), "%d.dat", te->dumpId);
@@ -272,7 +272,7 @@ _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
 
 	if (ctx == NULL)
 	{
-		ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
+		ctx = pg_malloc0_obj(lclTocEntry);
 		te->formatData = (void *) ctx;
 	}
 
@@ -337,7 +337,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
 	{
 		int			old_umask;
 
-		tm = pg_malloc0(sizeof(TAR_MEMBER));
+		tm = pg_malloc0_obj(TAR_MEMBER);
 
 		/*
 		 * POSIX does not require, but permits, tmpfile() to restrict file
@@ -1053,7 +1053,7 @@ static TAR_MEMBER *
 _tarPositionTo(ArchiveHandle *AH, const char *filename)
 {
 	lclContext *ctx = (lclContext *) AH->formatData;
-	TAR_MEMBER *th = pg_malloc0(sizeof(TAR_MEMBER));
+	TAR_MEMBER *th = pg_malloc0_obj(TAR_MEMBER);
 	char		c;
 	char		header[TAR_BLOCK_SIZE];
 	size_t		i,
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index ddff903915..807be8bb19 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -247,8 +247,8 @@ main(int argc, char *argv[])
 	do
 	{
 #define PARAMS_ARRAY_SIZE	8
-		const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
-		const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
+		const char **keywords = pg_malloc_array(const char *, PARAMS_ARRAY_SIZE);
+		const char **values = pg_malloc_array(const char *, PARAMS_ARRAY_SIZE);
 
 		keywords[0] = "host";
 		values[0] = options.host;
@@ -743,7 +743,7 @@ simple_action_list_append(SimpleActionList *list,
 {
 	SimpleActionListCell *cell;
 
-	cell = (SimpleActionListCell *) pg_malloc(sizeof(SimpleActionListCell));
+	cell = pg_malloc_obj(SimpleActionListCell);
 
 	cell->next = NULL;
 	cell->action = action;
diff --git a/src/common/config_info.c b/src/common/config_info.c
index aa643b63fe..891a1505cc 100644
--- a/src/common/config_info.c
+++ b/src/common/config_info.c
@@ -39,7 +39,7 @@ get_configdata(const char *my_exec_path, size_t *configdata_len)
 
 	/* Adjust this to match the number of items filled below */
 	*configdata_len = 23;
-	configdata = (ConfigData *) palloc(*configdata_len * sizeof(ConfigData));
+	configdata = palloc_array(ConfigData, *configdata_len);
 
 	configdata[i].name = pstrdup("BINDIR");
 	strlcpy(path, my_exec_path, sizeof(path));
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index 4c0da6e124..d1cb819842 100644
--- a/src/common/controldata_utils.c
+++ b/src/common/controldata_utils.c
@@ -59,7 +59,7 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
 
 	AssertArg(crc_ok_p);
 
-	ControlFile = palloc(sizeof(ControlFileData));
+	ControlFile = palloc_obj(ControlFileData);
 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
 
 #ifndef FRONTEND
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index a561d1d652..9c5b9c4c26 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -973,7 +973,7 @@ materializeResult(FunctionCallInfo fcinfo, PGconn *conn, PGresult *res)
 			rsinfo->setDesc = tupdesc;
 			MemoryContextSwitchTo(oldcontext);
 
-			values = (char **) palloc(nfields * sizeof(char *));
+			values = palloc_array(char *, nfields);
 
 			/* put all tuples into the tuplestore */
 			for (row = 0; row < ntuples; row++)
@@ -1277,7 +1277,7 @@ storeRow(volatile storeInfo *sinfo, PGresult *res, bool first)
 		 */
 		if (sinfo->cstrs)
 			pfree(sinfo->cstrs);
-		sinfo->cstrs = (char **) palloc(nfields * sizeof(char *));
+		sinfo->cstrs = palloc_array(char *, nfields);
 	}
 
 	/* Should have a single-row result if we get here */
@@ -1619,7 +1619,7 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
 		HeapTuple	tuple;
 		Datum		result;
 
-		values = (char **) palloc(2 * sizeof(char *));
+		values = palloc_array(char *, 2);
 		values[0] = psprintf("%d", call_cntr + 1);
 		values[1] = results[call_cntr];
 
@@ -2084,7 +2084,7 @@ get_pkey_attnames(Relation rel, int16 *indnkeyatts)
 			*indnkeyatts = index->indnkeyatts;
 			if (*indnkeyatts > 0)
 			{
-				result = (char **) palloc(*indnkeyatts * sizeof(char *));
+				result = palloc_array(char *, *indnkeyatts);
 
 				for (i = 0; i < *indnkeyatts; i++)
 					result[i] = SPI_fname(tupdesc, index->indkey.values[i]);
@@ -2125,7 +2125,7 @@ get_text_array_contents(ArrayType *array, int *numitems)
 	get_typlenbyvalalign(ARR_ELEMTYPE(array),
 						 &typlen, &typbyval, &typalign);
 
-	values = (char **) palloc(nitems * sizeof(char *));
+	values = palloc_array(char *, nitems);
 
 	ptr = ARR_DATA_PTR(array);
 	bitmap = ARR_NULLBITMAP(array);
@@ -2930,7 +2930,7 @@ validate_pkattnums(Relation rel,
 				 errmsg("number of key attributes must be > 0")));
 
 	/* Allocate output array */
-	*pkattnums = (int *) palloc(pknumatts_arg * sizeof(int));
+	*pkattnums = palloc_array(int, pknumatts_arg);
 	*pknumatts = pknumatts_arg;
 
 	/* Validate attnums and convert to internal form */
-- 
2.35.1

#2Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Peter Eisentraut (#1)
Re: Expand palloc/pg_malloc API

On Tue, May 17, 2022 at 5:11 PM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:

This adds additional variants of palloc, pg_malloc, etc. that
encapsulate common usage patterns and provide more type safety.

Examples:

-   result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+   result = palloc_obj(IndexBuildResult);
-   collector->tuples = (IndexTuple *) palloc(sizeof(IndexTuple) *
collector->lentuples);
+   collector->tuples = palloc_array(IndexTuple, collector->lentuples);

One common point is that the new interfaces all have a return type that
automatically matches what they are allocating, so you don't need any
casts nor have to manually make sure the size matches the expected
result. Besides the additional safety, the notation is also more
compact, as you can see above.

Inspired by the talloc library.

The interesting changes are in fe_memutils.h and palloc.h. The rest of
the patch is just randomly sprinkled examples to test/validate the new
additions.

It seems interesting. Are we always type-casting explicitly the output
of palloc/palloc0? Does this mean the compiler takes care of
type-casting the returned void * to the target type?

I see lots of instances where there's no explicit type-casting to the
target variable type -
retval = palloc(sizeof(GISTENTRY));
Interval *p = palloc(sizeof(Interval));
macaddr *v = palloc0(sizeof(macaddr)); and so on.

Regards,
Bharath Rupireddy.

#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bharath Rupireddy (#2)
Re: Expand palloc/pg_malloc API

Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> writes:

On Tue, May 17, 2022 at 5:11 PM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:

This adds additional variants of palloc, pg_malloc, etc. that
encapsulate common usage patterns and provide more type safety.

I see lots of instances where there's no explicit type-casting to the
target variable type -
retval = palloc(sizeof(GISTENTRY));
Interval *p = palloc(sizeof(Interval));
macaddr *v = palloc0(sizeof(macaddr)); and so on.

Yeah. IMO the first of those is very poor style, because there's
basically nothing enforcing that you wrote the right thing in sizeof().
The others are a bit safer, in that at least a human can note that
the two types mentioned on the same line match --- but I doubt any
compiler would detect it if they don't. Our current preferred style

Interval *p = (Interval *) palloc(sizeof(Interval));

is really barely an improvement, because only two of the three types
mentioned are going to be checked against each other.

So I think Peter's got a good idea here (I might quibble with the details
of some of these macros). But it's not really going to move the
safety goalposts very far unless we make a concerted effort to make
these be the style everywhere. Are we willing to do that? What
will it do to back-patching difficulty? Dare I suggest back-patching
these changes?

regards, tom lane

#4Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#3)
Re: Expand palloc/pg_malloc API

On 17.05.22 20:43, Tom Lane wrote:

So I think Peter's got a good idea here (I might quibble with the details
of some of these macros). But it's not really going to move the
safety goalposts very far unless we make a concerted effort to make
these be the style everywhere. Are we willing to do that? What
will it do to back-patching difficulty? Dare I suggest back-patching
these changes?

I think it could go like the castNode() introduction: first we adopt it
sporadically for new code, then we change over some larger pieces of
code, then we backpatch the API, then someone sends in a big patch to
change the rest.

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#4)
Re: Expand palloc/pg_malloc API

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 17.05.22 20:43, Tom Lane wrote:

So I think Peter's got a good idea here (I might quibble with the details
of some of these macros). But it's not really going to move the
safety goalposts very far unless we make a concerted effort to make
these be the style everywhere. Are we willing to do that? What
will it do to back-patching difficulty? Dare I suggest back-patching
these changes?

I think it could go like the castNode() introduction: first we adopt it
sporadically for new code, then we change over some larger pieces of
code, then we backpatch the API, then someone sends in a big patch to
change the rest.

OK, that seems like a reasonable plan.

I've now studied this a little more closely, and I think the
functionality is fine, but I have naming quibbles.

1. Do we really want distinct names for the frontend and backend
versions of the macros? Particularly since you're layering the
frontend ones over pg_malloc, which has exit-on-OOM behavior?
I think we've found that notational discrepancies between frontend
and backend code are generally more a hindrance than a help,
so I'm inclined to drop the pg_malloc_xxx macros and just use
"palloc"-based names across the board.

2. I don't like the "palloc_ptrtype" name at all. I see that you
borrowed that name from talloc, but I doubt that's a precedent that
very many people are familiar with. To me it sounds like it might
allocate something that's the size of a pointer, not the size of the
pointed-to object. I have to confess though that I don't have an
obviously better name to suggest. "palloc_pointed_to" would be
clear perhaps, but it's kind of long.

3. Likewise, "palloc_obj" is perhaps less clear than it could be.
I find palloc_array just fine though. Maybe palloc_object or
palloc_struct? (If "_obj" can be traced to talloc, I'm not
seeing where.)

One thought that comes to mind is that palloc_ptrtype is almost
surely going to be used in the style

myvariable = palloc_ptrtype(myvariable);

and if it's not that it's very likely wrong. So maybe we should cut
out the middleman and write something like

#define palloc_instantiate(ptr) ((ptr) = (typeof(ptr)) palloc(sizeof(*(ptr))))
...
palloc_instantiate(myvariable);

I'm not wedded to "instantiate" here, there's probably better names.

regards, tom lane

#6David G. Johnston
david.g.johnston@gmail.com
In reply to: Tom Lane (#5)
Re: Expand palloc/pg_malloc API

On Tue, Jul 26, 2022 at 2:32 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

2. I don't like the "palloc_ptrtype" name at all. I see that you
borrowed that name from talloc, but I doubt that's a precedent that
very many people are familiar with.

To me it sounds like it might
allocate something that's the size of a pointer, not the size of the
pointed-to object. I have to confess though that I don't have an
obviously better name to suggest. "palloc_pointed_to" would be
clear perhaps, but it's kind of long.

I agree that ptrtype reads "the type of a pointer".

This may not be a C-idiom but the pointed-to thing is a "reference" (hence
pass by value vs pass by reference). So:

palloc_ref(myvariablepointer)

will allocate using the type of the referenced object. Just like _array
and _obj, which name the thing being used as a size template as opposed to
instantiate which seems more like another word for "allocate/palloc".

David J.
P.S.

Admittedly I'm still getting my head around reading pointer-using code (I
get the general concept but haven't had to code them)....

- lockrelid = palloc(sizeof(*lockrelid));
+ lockrelid = palloc_ptrtype(lockrelid);

// This definitely seems like an odd idiom until I remembered about
short-lived memory contexts and the lost pointers are soon destroyed there.

So lockrelid (no star) is a pointer that has an underlying reference that
the macro (and the orignal code) resolves via the *

I cannot reason out whether the following would be equivalent to the above:

lockrelid = palloc_obj(*lockrelid);

I assume not because: typeof(lockrelid) != (*lockrelid *)

#7Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#5)
Re: Expand palloc/pg_malloc API

On 26.07.22 23:32, Tom Lane wrote:

1. Do we really want distinct names for the frontend and backend
versions of the macros? Particularly since you're layering the
frontend ones over pg_malloc, which has exit-on-OOM behavior?
I think we've found that notational discrepancies between frontend
and backend code are generally more a hindrance than a help,
so I'm inclined to drop the pg_malloc_xxx macros and just use
"palloc"-based names across the board.

This seems like a question that is independent of this patch. Given
that both pg_malloc() and palloc() do exist in fe_memutils, I think it
would be confusing to only extend one part of that and not the other.
The amount of code is ultimately not a lot.

If we wanted to get rid of pg_malloc() altogether, maybe we could talk
about that.

(Personally, I have always been a bit suspicious about using the name
palloc() without memory context semantics in frontend code, but I guess
this is wide-spread now.)

3. Likewise, "palloc_obj" is perhaps less clear than it could be.
I find palloc_array just fine though. Maybe palloc_object or
palloc_struct? (If "_obj" can be traced to talloc, I'm not
seeing where.)

In talloc, the talloc() function itself allocates an object of a given
type. To allocate something of a specified size, you'd use
talloc_size(). So those names won't map exactly. I'm fine with
palloc_object() if that is clearer.

One thought that comes to mind is that palloc_ptrtype is almost
surely going to be used in the style

myvariable = palloc_ptrtype(myvariable);

and if it's not that it's very likely wrong. So maybe we should cut
out the middleman and write something like

#define palloc_instantiate(ptr) ((ptr) = (typeof(ptr)) palloc(sizeof(*(ptr))))
...
palloc_instantiate(myvariable);

Right, this is sort of what you'd want, really. But it looks like
strange C code, since you are modifying the variable even though you are
passing it by value.

I think the _ptrtype variant isn't that useful anyway, so if it's
confusing we can leave it out.

#8Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: David G. Johnston (#6)
Re: Expand palloc/pg_malloc API

On 27.07.22 01:58, David G. Johnston wrote:

Admittedly I'm still getting my head around reading pointer-using code
(I get the general concept but haven't had to code them)....

- lockrelid = palloc(sizeof(*lockrelid));
+ lockrelid = palloc_ptrtype(lockrelid);

// This definitely seems like an odd idiom until I remembered about
short-lived memory contexts and the lost pointers are soon destroyed there.

So lockrelid (no star) is a pointer that has an underlying reference
that the macro (and the orignal code) resolves via the *

I cannot reason out whether the following would be equivalent to the above:

lockrelid = palloc_obj(*lockrelid);

I think that would also work.

Ultimately, it would be more idiomatic (in Postgres), to write this as

lockrelid = palloc(sizeof(LockRelId));

and thus

lockrelid = palloc_obj(LockRelId);

#9Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Peter Eisentraut (#7)
2 attachment(s)
Re: Expand palloc/pg_malloc API

On 12.08.22 09:31, Peter Eisentraut wrote:

In talloc, the talloc() function itself allocates an object of a given
type.  To allocate something of a specified size, you'd use
talloc_size().  So those names won't map exactly.  I'm fine with
palloc_object() if that is clearer.

I think the _ptrtype variant isn't that useful anyway, so if it's
confusing we can leave it out.

I have updated this patch set to rename the _obj() functions to
_object(), and I have dropped the _ptrtype() variants.

I have also split the patch to put the new API and the example uses into
separate patches.

Attachments:

v2-0001-Expand-palloc-pg_malloc-API-for-more-type-safety.patchtext/plain; charset=UTF-8; name=v2-0001-Expand-palloc-pg_malloc-API-for-more-type-safety.patchDownload
From 250cc273063a6cd1cb7c7a73a59323844215260d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Sun, 28 Aug 2022 19:13:34 +0200
Subject: [PATCH v2 1/2] Expand palloc/pg_malloc API for more type safety

This adds additional variants of palloc, pg_malloc, etc. that
encapsulate common usage patterns and provide more type safety.
Inspired by the talloc library.

Discussion: https://www.postgresql.org/message-id/flat/bb755632-2a43-d523-36f8-a1e7a389a907@enterprisedb.com
---
 src/include/common/fe_memutils.h | 28 ++++++++++++++++++++++++++++
 src/include/utils/palloc.h       | 22 ++++++++++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/src/include/common/fe_memutils.h b/src/include/common/fe_memutils.h
index 0a4c9126ea..63f2b6a802 100644
--- a/src/include/common/fe_memutils.h
+++ b/src/include/common/fe_memutils.h
@@ -29,6 +29,28 @@ extern void *pg_malloc_extended(size_t size, int flags);
 extern void *pg_realloc(void *pointer, size_t size);
 extern void pg_free(void *pointer);
 
+/*
+ * Variants with easier notation and more type safety
+ */
+
+/*
+ * Allocate space for one object of type "type"
+ */
+#define pg_malloc_object(type) ((type *) pg_malloc(sizeof(type)))
+#define pg_malloc0_object(type) ((type *) pg_malloc0(sizeof(type)))
+
+/*
+ * Allocate space for "count" objects of type "type"
+ */
+#define pg_malloc_array(type, count) ((type *) pg_malloc(sizeof(type) * (count)))
+#define pg_malloc0_array(type, count) ((type *) pg_malloc0(sizeof(type) * (count)))
+
+/*
+ * Change size of allocation pointed to by "pointer" to have space for "count"
+ * objects of type "type"
+ */
+#define pg_realloc_array(pointer, type, count) ((type *) pg_realloc(pointer, sizeof(type) * (count)))
+
 /* Equivalent functions, deliberately named the same as backend functions */
 extern char *pstrdup(const char *in);
 extern char *pnstrdup(const char *in, Size size);
@@ -38,6 +60,12 @@ extern void *palloc_extended(Size size, int flags);
 extern void *repalloc(void *pointer, Size size);
 extern void pfree(void *pointer);
 
+#define palloc_object(type) ((type *) palloc(sizeof(type)))
+#define palloc0_object(type) ((type *) palloc0(sizeof(type)))
+#define palloc_array(type, count) ((type *) palloc(sizeof(type) * (count)))
+#define palloc0_array(type, count) ((type *) palloc0(sizeof(type) * (count)))
+#define repalloc_array(pointer, type, count) ((type *) repalloc(pointer, sizeof(type) * (count)))
+
 /* sprintf into a palloc'd buffer --- these are in psprintf.c */
 extern char *psprintf(const char *fmt,...) pg_attribute_printf(1, 2);
 extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0);
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index 332575f518..a0b62aa7b0 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -80,6 +80,28 @@ extern void *palloc_extended(Size size, int flags);
 extern pg_nodiscard void *repalloc(void *pointer, Size size);
 extern void pfree(void *pointer);
 
+/*
+ * Variants with easier notation and more type safety
+ */
+
+/*
+ * Allocate space for one object of type "type"
+ */
+#define palloc_object(type) ((type *) palloc(sizeof(type)))
+#define palloc0_object(type) ((type *) palloc0(sizeof(type)))
+
+/*
+ * Allocate space for "count" objects of type "type"
+ */
+#define palloc_array(type, count) ((type *) palloc(sizeof(type) * (count)))
+#define palloc0_array(type, count) ((type *) palloc0(sizeof(type) * (count)))
+
+/*
+ * Change size of allocation pointed to by "pointer" to have space for "count"
+ * objects of type "type"
+ */
+#define repalloc_array(pointer, type, count) ((type *) repalloc(pointer, sizeof(type) * (count)))
+
 /*
  * The result of palloc() is always word-aligned, so we can skip testing
  * alignment of the pointer when deciding which MemSet variant to use.
-- 
2.37.1

v2-0002-Assorted-examples-of-expanded-type-safer-palloc-p.patchtext/plain; charset=UTF-8; name=v2-0002-Assorted-examples-of-expanded-type-safer-palloc-p.patchDownload
From d22fe38b8d5dfee16e2794daeaa3834c8f5aa05b Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Sun, 28 Aug 2022 19:17:49 +0200
Subject: [PATCH v2 2/2] Assorted examples of expanded type-safer
 palloc/pg_malloc API

---
 contrib/dblink/dblink.c          | 12 +++----
 src/backend/access/brin/brin.c   | 14 ++++----
 src/backend/access/gin/ginfast.c | 17 ++++------
 src/backend/commands/indexcmds.c | 42 +++++++++++------------
 src/backend/executor/nodeHash.c  | 57 +++++++++++++-------------------
 src/bin/pg_dump/common.c         | 24 +++++---------
 src/bin/pg_dump/pg_backup_tar.c  | 10 +++---
 src/bin/psql/startup.c           |  6 ++--
 src/common/config_info.c         |  2 +-
 src/common/controldata_utils.c   |  2 +-
 10 files changed, 83 insertions(+), 103 deletions(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index e323fdd0e6..1d5f2ad436 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -972,7 +972,7 @@ materializeResult(FunctionCallInfo fcinfo, PGconn *conn, PGresult *res)
 			rsinfo->setDesc = tupdesc;
 			MemoryContextSwitchTo(oldcontext);
 
-			values = (char **) palloc(nfields * sizeof(char *));
+			values = palloc_array(char *, nfields);
 
 			/* put all tuples into the tuplestore */
 			for (row = 0; row < ntuples; row++)
@@ -1276,7 +1276,7 @@ storeRow(volatile storeInfo *sinfo, PGresult *res, bool first)
 		 */
 		if (sinfo->cstrs)
 			pfree(sinfo->cstrs);
-		sinfo->cstrs = (char **) palloc(nfields * sizeof(char *));
+		sinfo->cstrs = palloc_array(char *, nfields);
 	}
 
 	/* Should have a single-row result if we get here */
@@ -1618,7 +1618,7 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
 		HeapTuple	tuple;
 		Datum		result;
 
-		values = (char **) palloc(2 * sizeof(char *));
+		values = palloc_array(char *, 2);
 		values[0] = psprintf("%d", call_cntr + 1);
 		values[1] = results[call_cntr];
 
@@ -2083,7 +2083,7 @@ get_pkey_attnames(Relation rel, int16 *indnkeyatts)
 			*indnkeyatts = index->indnkeyatts;
 			if (*indnkeyatts > 0)
 			{
-				result = (char **) palloc(*indnkeyatts * sizeof(char *));
+				result = palloc_array(char *, *indnkeyatts);
 
 				for (i = 0; i < *indnkeyatts; i++)
 					result[i] = SPI_fname(tupdesc, index->indkey.values[i]);
@@ -2124,7 +2124,7 @@ get_text_array_contents(ArrayType *array, int *numitems)
 	get_typlenbyvalalign(ARR_ELEMTYPE(array),
 						 &typlen, &typbyval, &typalign);
 
-	values = (char **) palloc(nitems * sizeof(char *));
+	values = palloc_array(char *, nitems);
 
 	ptr = ARR_DATA_PTR(array);
 	bitmap = ARR_NULLBITMAP(array);
@@ -2928,7 +2928,7 @@ validate_pkattnums(Relation rel,
 				 errmsg("number of key attributes must be > 0")));
 
 	/* Allocate output array */
-	*pkattnums = (int *) palloc(pknumatts_arg * sizeof(int));
+	*pkattnums = palloc_array(int, pknumatts_arg);
 	*pknumatts = pknumatts_arg;
 
 	/* Validate attnums and convert to internal form */
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 69f21abfb5..dcf2d2c4b5 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -329,7 +329,7 @@ brinbeginscan(Relation r, int nkeys, int norderbys)
 
 	scan = RelationGetIndexScan(r, nkeys, norderbys);
 
-	opaque = (BrinOpaque *) palloc(sizeof(BrinOpaque));
+	opaque = palloc_object(BrinOpaque);
 	opaque->bo_rmAccess = brinRevmapInitialize(r, &opaque->bo_pagesPerRange,
 											   scan->xs_snapshot);
 	opaque->bo_bdesc = brin_build_desc(r);
@@ -394,7 +394,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
 	 * don't look them up here; we do that lazily the first time we see a scan
 	 * key reference each of them.  We rely on zeroing fn_oid to InvalidOid.
 	 */
-	consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts);
+	consistentFn = palloc0_array(FmgrInfo, bdesc->bd_tupdesc->natts);
 
 	/*
 	 * Make room for per-attribute lists of scan keys that we'll pass to the
@@ -881,7 +881,7 @@ brinbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	/*
 	 * Return statistics
 	 */
-	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+	result = palloc_object(IndexBuildResult);
 
 	result->heap_tuples = reltuples;
 	result->index_tuples = idxtuples;
@@ -925,7 +925,7 @@ brinbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 {
 	/* allocate stats if first time through, else re-use existing struct */
 	if (stats == NULL)
-		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+		stats = palloc0_object(IndexBulkDeleteResult);
 
 	return stats;
 }
@@ -944,7 +944,7 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 		return stats;
 
 	if (!stats)
-		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+		stats = palloc0_object(IndexBulkDeleteResult);
 	stats->num_pages = RelationGetNumberOfBlocks(info->index);
 	/* rest of stats is initialized by zeroing */
 
@@ -1204,7 +1204,7 @@ brin_build_desc(Relation rel)
 	 * Obtain BrinOpcInfo for each indexed column.  While at it, accumulate
 	 * the number of columns stored, since the number is opclass-defined.
 	 */
-	opcinfo = (BrinOpcInfo **) palloc(sizeof(BrinOpcInfo *) * tupdesc->natts);
+	opcinfo = palloc_array(BrinOpcInfo*, tupdesc->natts);
 	for (keyno = 0; keyno < tupdesc->natts; keyno++)
 	{
 		FmgrInfo   *opcInfoFn;
@@ -1276,7 +1276,7 @@ initialize_brin_buildstate(Relation idxRel, BrinRevmap *revmap,
 {
 	BrinBuildState *state;
 
-	state = palloc(sizeof(BrinBuildState));
+	state = palloc_object(BrinBuildState);
 
 	state->bs_irel = idxRel;
 	state->bs_numtuples = 0;
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index 6c677447a9..ab4bb67d4b 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -505,7 +505,7 @@ ginHeapTupleFastCollect(GinState *ginstate,
 		 * resizing (since palloc likes powers of 2).
 		 */
 		collector->lentuples = pg_nextpower2_32(Max(16, nentries));
-		collector->tuples = (IndexTuple *) palloc(sizeof(IndexTuple) * collector->lentuples);
+		collector->tuples = palloc_array(IndexTuple, collector->lentuples);
 	}
 	else if (collector->lentuples < collector->ntuples + nentries)
 	{
@@ -515,8 +515,8 @@ ginHeapTupleFastCollect(GinState *ginstate,
 		 * MaxAllocSize/sizeof(IndexTuple), causing an error in repalloc.
 		 */
 		collector->lentuples = pg_nextpower2_32(collector->ntuples + nentries);
-		collector->tuples = (IndexTuple *) repalloc(collector->tuples,
-													sizeof(IndexTuple) * collector->lentuples);
+		collector->tuples = repalloc_array(collector->tuples,
+										   IndexTuple, collector->lentuples);
 	}
 
 	/*
@@ -665,9 +665,8 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
 static void
 initKeyArray(KeyArray *keys, int32 maxvalues)
 {
-	keys->keys = (Datum *) palloc(sizeof(Datum) * maxvalues);
-	keys->categories = (GinNullCategory *)
-		palloc(sizeof(GinNullCategory) * maxvalues);
+	keys->keys = palloc_array(Datum, maxvalues);
+	keys->categories = palloc_array(GinNullCategory, maxvalues);
 	keys->nvalues = 0;
 	keys->maxvalues = maxvalues;
 }
@@ -679,10 +678,8 @@ addDatum(KeyArray *keys, Datum datum, GinNullCategory category)
 	if (keys->nvalues >= keys->maxvalues)
 	{
 		keys->maxvalues *= 2;
-		keys->keys = (Datum *)
-			repalloc(keys->keys, sizeof(Datum) * keys->maxvalues);
-		keys->categories = (GinNullCategory *)
-			repalloc(keys->categories, sizeof(GinNullCategory) * keys->maxvalues);
+		keys->keys = repalloc_array(keys->keys, Datum, keys->maxvalues);
+		keys->categories = repalloc_array(keys->categories, GinNullCategory, keys->maxvalues);
 	}
 
 	keys->keys[keys->nvalues] = datum;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 3c6e09815e..9167bab0c5 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -229,10 +229,10 @@ CheckIndexCompatible(Oid oldId,
 	 */
 	indexInfo = makeIndexInfo(numberOfAttributes, numberOfAttributes,
 							  accessMethodId, NIL, NIL, false, false, false, false);
-	typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
+	typeObjectId = palloc_array(Oid, numberOfAttributes);
+	collationObjectId = palloc_array(Oid, numberOfAttributes);
+	classObjectId = palloc_array(Oid, numberOfAttributes);
+	coloptions = palloc_array(int16, numberOfAttributes);
 	ComputeIndexAttrs(indexInfo,
 					  typeObjectId, collationObjectId, classObjectId,
 					  coloptions, attributeList,
@@ -895,10 +895,10 @@ DefineIndex(Oid relationId,
 							  !concurrent,
 							  concurrent);
 
-	typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
+	typeObjectId = palloc_array(Oid, numberOfAttributes);
+	collationObjectId = palloc_array(Oid, numberOfAttributes);
+	classObjectId = palloc_array(Oid, numberOfAttributes);
+	coloptions = palloc_array(int16, numberOfAttributes);
 	ComputeIndexAttrs(indexInfo,
 					  typeObjectId, collationObjectId, classObjectId,
 					  coloptions, allIndexParams,
@@ -1210,7 +1210,7 @@ DefineIndex(Oid relationId,
 		if ((!stmt->relation || stmt->relation->inh) && partdesc->nparts > 0)
 		{
 			int			nparts = partdesc->nparts;
-			Oid		   *part_oids = palloc(sizeof(Oid) * nparts);
+			Oid		   *part_oids = palloc_array(Oid, nparts);
 			bool		invalidate_parent = false;
 			Relation	parentIndex;
 			TupleDesc	parentDesc;
@@ -1786,9 +1786,9 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 	if (exclusionOpNames)
 	{
 		Assert(list_length(exclusionOpNames) == nkeycols);
-		indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * nkeycols);
-		indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * nkeycols);
-		indexInfo->ii_ExclusionStrats = (uint16 *) palloc(sizeof(uint16) * nkeycols);
+		indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
+		indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
+		indexInfo->ii_ExclusionStrats = palloc_array(uint16, nkeycols);
 		nextExclOp = list_head(exclusionOpNames);
 	}
 	else
@@ -2112,7 +2112,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 
 			if (!indexInfo->ii_OpclassOptions)
 				indexInfo->ii_OpclassOptions =
-					palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+					palloc0_array(Datum, indexInfo->ii_NumIndexAttrs);
 
 			indexInfo->ii_OpclassOptions[attn] =
 				transformRelOptions((Datum) 0, attribute->opclassopts,
@@ -3459,7 +3459,7 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 						/* Save the list of relation OIDs in private context */
 						oldcontext = MemoryContextSwitchTo(private_context);
 
-						idx = palloc(sizeof(ReindexIndexInfo));
+						idx = palloc_object(ReindexIndexInfo);
 						idx->indexId = cellOid;
 						/* other fields set later */
 
@@ -3508,7 +3508,7 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 							 */
 							oldcontext = MemoryContextSwitchTo(private_context);
 
-							idx = palloc(sizeof(ReindexIndexInfo));
+							idx = palloc_object(ReindexIndexInfo);
 							idx->indexId = cellOid;
 							indexIds = lappend(indexIds, idx);
 							/* other fields set later */
@@ -3589,7 +3589,7 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 				 * Save the list of relation OIDs in private context.  Note
 				 * that invalid indexes are allowed here.
 				 */
-				idx = palloc(sizeof(ReindexIndexInfo));
+				idx = palloc_object(ReindexIndexInfo);
 				idx->indexId = relationOid;
 				indexIds = lappend(indexIds, idx);
 				/* other fields set later */
@@ -3734,7 +3734,7 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 		 */
 		oldcontext = MemoryContextSwitchTo(private_context);
 
-		newidx = palloc(sizeof(ReindexIndexInfo));
+		newidx = palloc_object(ReindexIndexInfo);
 		newidx->indexId = newIndexId;
 		newidx->safe = idx->safe;
 		newidx->tableId = idx->tableId;
@@ -3748,10 +3748,10 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 		 * avoid multiple locks taken on the same relation, instead we rely on
 		 * parentRelationIds built earlier.
 		 */
-		lockrelid = palloc(sizeof(*lockrelid));
+		lockrelid = palloc_object(LockRelId);
 		*lockrelid = indexRel->rd_lockInfo.lockRelId;
 		relationLocks = lappend(relationLocks, lockrelid);
-		lockrelid = palloc(sizeof(*lockrelid));
+		lockrelid = palloc_object(LockRelId);
 		*lockrelid = newIndexRel->rd_lockInfo.lockRelId;
 		relationLocks = lappend(relationLocks, lockrelid);
 
@@ -3783,11 +3783,11 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
 		oldcontext = MemoryContextSwitchTo(private_context);
 
 		/* Add lockrelid of heap relation to the list of locked relations */
-		lockrelid = palloc(sizeof(*lockrelid));
+		lockrelid = palloc_object(LockRelId);
 		*lockrelid = heapRelation->rd_lockInfo.lockRelId;
 		relationLocks = lappend(relationLocks, lockrelid);
 
-		heaplocktag = (LOCKTAG *) palloc(sizeof(LOCKTAG));
+		heaplocktag = palloc_object(LOCKTAG);
 
 		/* Save the LOCKTAG for this parent relation for the wait phase */
 		SET_LOCKTAG_RELATION(*heaplocktag, lockrelid->dbId, lockrelid->relId);
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 841896c778..77dd1dae8b 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -479,7 +479,7 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
 	 * per-query memory context.  Everything else should be kept inside the
 	 * subsidiary hashCxt or batchCxt.
 	 */
-	hashtable = (HashJoinTable) palloc(sizeof(HashJoinTableData));
+	hashtable = palloc_object(HashJoinTableData);
 	hashtable->nbuckets = nbuckets;
 	hashtable->nbuckets_original = nbuckets;
 	hashtable->nbuckets_optimal = nbuckets;
@@ -540,12 +540,10 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
 	 * remember whether the join operators are strict.
 	 */
 	nkeys = list_length(hashOperators);
-	hashtable->outer_hashfunctions =
-		(FmgrInfo *) palloc(nkeys * sizeof(FmgrInfo));
-	hashtable->inner_hashfunctions =
-		(FmgrInfo *) palloc(nkeys * sizeof(FmgrInfo));
-	hashtable->hashStrict = (bool *) palloc(nkeys * sizeof(bool));
-	hashtable->collations = (Oid *) palloc(nkeys * sizeof(Oid));
+	hashtable->outer_hashfunctions = palloc_array(FmgrInfo, nkeys);
+	hashtable->inner_hashfunctions = palloc_array(FmgrInfo, nkeys);
+	hashtable->hashStrict = palloc_array(bool, nkeys);
+	hashtable->collations = palloc_array(Oid, nkeys);
 	i = 0;
 	forboth(ho, hashOperators, hc, hashCollations)
 	{
@@ -569,10 +567,8 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
 		 * allocate and initialize the file arrays in hashCxt (not needed for
 		 * parallel case which uses shared tuplestores instead of raw files)
 		 */
-		hashtable->innerBatchFile = (BufFile **)
-			palloc0(nbatch * sizeof(BufFile *));
-		hashtable->outerBatchFile = (BufFile **)
-			palloc0(nbatch * sizeof(BufFile *));
+		hashtable->innerBatchFile = palloc0_array(BufFile *, nbatch);
+		hashtable->outerBatchFile = palloc0_array(BufFile *, nbatch);
 		/* The files will not be opened until needed... */
 		/* ... but make sure we have temp tablespaces established for them */
 		PrepareTempTablespaces();
@@ -636,8 +632,7 @@ ExecHashTableCreate(HashState *state, List *hashOperators, List *hashCollations,
 		 */
 		MemoryContextSwitchTo(hashtable->batchCxt);
 
-		hashtable->buckets.unshared = (HashJoinTuple *)
-			palloc0(nbuckets * sizeof(HashJoinTuple));
+		hashtable->buckets.unshared = palloc0_array(HashJoinTuple, nbuckets);
 
 		/*
 		 * Set up for skew optimization, if possible and there's a need for
@@ -937,20 +932,16 @@ ExecHashIncreaseNumBatches(HashJoinTable hashtable)
 	if (hashtable->innerBatchFile == NULL)
 	{
 		/* we had no file arrays before */
-		hashtable->innerBatchFile = (BufFile **)
-			palloc0(nbatch * sizeof(BufFile *));
-		hashtable->outerBatchFile = (BufFile **)
-			palloc0(nbatch * sizeof(BufFile *));
+		hashtable->innerBatchFile = palloc0_array(BufFile *, nbatch);
+		hashtable->outerBatchFile = palloc0_array(BufFile *, nbatch);
 		/* time to establish the temp tablespaces, too */
 		PrepareTempTablespaces();
 	}
 	else
 	{
 		/* enlarge arrays and zero out added entries */
-		hashtable->innerBatchFile = (BufFile **)
-			repalloc(hashtable->innerBatchFile, nbatch * sizeof(BufFile *));
-		hashtable->outerBatchFile = (BufFile **)
-			repalloc(hashtable->outerBatchFile, nbatch * sizeof(BufFile *));
+		hashtable->innerBatchFile = repalloc_array(hashtable->innerBatchFile, BufFile *, nbatch);
+		hashtable->outerBatchFile = repalloc_array(hashtable->outerBatchFile, BufFile *, nbatch);
 		MemSet(hashtable->innerBatchFile + oldnbatch, 0,
 			   (nbatch - oldnbatch) * sizeof(BufFile *));
 		MemSet(hashtable->outerBatchFile + oldnbatch, 0,
@@ -977,8 +968,8 @@ ExecHashIncreaseNumBatches(HashJoinTable hashtable)
 		hashtable->log2_nbuckets = hashtable->log2_nbuckets_optimal;
 
 		hashtable->buckets.unshared =
-			repalloc(hashtable->buckets.unshared,
-					 sizeof(HashJoinTuple) * hashtable->nbuckets);
+			repalloc_array(hashtable->buckets.unshared,
+						   HashJoinTuple, hashtable->nbuckets);
 	}
 
 	/*
@@ -1371,7 +1362,7 @@ ExecParallelHashRepartitionRest(HashJoinTable hashtable)
 	/* Get our hands on the previous generation of batches. */
 	old_batches = (ParallelHashJoinBatch *)
 		dsa_get_address(hashtable->area, pstate->old_batches);
-	old_inner_tuples = palloc0(sizeof(SharedTuplestoreAccessor *) * old_nbatch);
+	old_inner_tuples = palloc0_array(SharedTuplestoreAccessor *, old_nbatch);
 	for (i = 1; i < old_nbatch; ++i)
 	{
 		ParallelHashJoinBatch *shared =
@@ -1477,8 +1468,8 @@ ExecHashIncreaseNumBuckets(HashJoinTable hashtable)
 	 * chunks)
 	 */
 	hashtable->buckets.unshared =
-		(HashJoinTuple *) repalloc(hashtable->buckets.unshared,
-								   hashtable->nbuckets * sizeof(HashJoinTuple));
+		repalloc_array(hashtable->buckets.unshared,
+					   HashJoinTuple, hashtable->nbuckets);
 
 	memset(hashtable->buckets.unshared, 0,
 		   hashtable->nbuckets * sizeof(HashJoinTuple));
@@ -2170,8 +2161,7 @@ ExecHashTableReset(HashJoinTable hashtable)
 	oldcxt = MemoryContextSwitchTo(hashtable->batchCxt);
 
 	/* Reallocate and reinitialize the hash bucket headers. */
-	hashtable->buckets.unshared = (HashJoinTuple *)
-		palloc0(nbuckets * sizeof(HashJoinTuple));
+	hashtable->buckets.unshared = palloc0_array(HashJoinTuple, nbuckets);
 
 	hashtable->spaceUsed = 0;
 
@@ -2666,8 +2656,7 @@ ExecShutdownHash(HashState *node)
 {
 	/* Allocate save space if EXPLAIN'ing and we didn't do so already */
 	if (node->ps.instrument && !node->hinstrument)
-		node->hinstrument = (HashInstrumentation *)
-			palloc0(sizeof(HashInstrumentation));
+		node->hinstrument = palloc0_object(HashInstrumentation);
 	/* Now accumulate data for the current (final) hash table */
 	if (node->hinstrument && node->hashtable)
 		ExecHashAccumInstrumentation(node->hinstrument, node->hashtable);
@@ -2977,8 +2966,8 @@ ExecParallelHashJoinSetUpBatches(HashJoinTable hashtable, int nbatch)
 
 	/* Allocate this backend's accessor array. */
 	hashtable->nbatch = nbatch;
-	hashtable->batches = (ParallelHashJoinBatchAccessor *)
-		palloc0(sizeof(ParallelHashJoinBatchAccessor) * hashtable->nbatch);
+	hashtable->batches =
+		palloc0_array(ParallelHashJoinBatchAccessor, hashtable->nbatch);
 
 	/* Set up the shared state, tuplestores and backend-local accessors. */
 	for (i = 0; i < hashtable->nbatch; ++i)
@@ -3083,8 +3072,8 @@ ExecParallelHashEnsureBatchAccessors(HashJoinTable hashtable)
 
 	/* Allocate this backend's accessor array. */
 	hashtable->nbatch = pstate->nbatch;
-	hashtable->batches = (ParallelHashJoinBatchAccessor *)
-		palloc0(sizeof(ParallelHashJoinBatchAccessor) * hashtable->nbatch);
+	hashtable->batches =
+		palloc0_array(ParallelHashJoinBatchAccessor, hashtable->nbatch);
 
 	/* Find the base of the pseudo-array of ParallelHashJoinBatch objects. */
 	batches = (ParallelHashJoinBatch *)
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 794e6e7ce9..395f817fa8 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -400,7 +400,7 @@ flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 			if (parentidx == NULL)
 				continue;
 
-			attachinfo = (IndexAttachInfo *) pg_malloc(sizeof(IndexAttachInfo));
+			attachinfo = pg_malloc_object(IndexAttachInfo);
 
 			attachinfo->dobj.objType = DO_INDEX_ATTACH;
 			attachinfo->dobj.catId.tableoid = 0;
@@ -530,7 +530,7 @@ flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables)
 			{
 				AttrDefInfo *attrDef;
 
-				attrDef = (AttrDefInfo *) pg_malloc(sizeof(AttrDefInfo));
+				attrDef = pg_malloc_object(AttrDefInfo);
 				attrDef->dobj.objType = DO_ATTRDEF;
 				attrDef->dobj.catId.tableoid = 0;
 				attrDef->dobj.catId.oid = 0;
@@ -600,14 +600,12 @@ AssignDumpId(DumpableObject *dobj)
 		if (allocedDumpIds <= 0)
 		{
 			newAlloc = 256;
-			dumpIdMap = (DumpableObject **)
-				pg_malloc(newAlloc * sizeof(DumpableObject *));
+			dumpIdMap = pg_malloc_array(DumpableObject *, newAlloc);
 		}
 		else
 		{
 			newAlloc = allocedDumpIds * 2;
-			dumpIdMap = (DumpableObject **)
-				pg_realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *));
+			dumpIdMap = pg_realloc_array(dumpIdMap, DumpableObject *, newAlloc);
 		}
 		memset(dumpIdMap + allocedDumpIds, 0,
 			   (newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
@@ -700,8 +698,7 @@ getDumpableObjects(DumpableObject ***objs, int *numObjs)
 	int			i,
 				j;
 
-	*objs = (DumpableObject **)
-		pg_malloc(allocedDumpIds * sizeof(DumpableObject *));
+	*objs = pg_malloc_array(DumpableObject *, allocedDumpIds);
 	j = 0;
 	for (i = 1; i < allocedDumpIds; i++)
 	{
@@ -724,15 +721,13 @@ addObjectDependency(DumpableObject *dobj, DumpId refId)
 		if (dobj->allocDeps <= 0)
 		{
 			dobj->allocDeps = 16;
-			dobj->dependencies = (DumpId *)
-				pg_malloc(dobj->allocDeps * sizeof(DumpId));
+			dobj->dependencies = pg_malloc_array(DumpId, dobj->allocDeps);
 		}
 		else
 		{
 			dobj->allocDeps *= 2;
-			dobj->dependencies = (DumpId *)
-				pg_realloc(dobj->dependencies,
-						   dobj->allocDeps * sizeof(DumpId));
+			dobj->dependencies = pg_realloc_array(dobj->dependencies,
+												  DumpId, dobj->allocDeps);
 		}
 	}
 	dobj->dependencies[dobj->nDeps++] = refId;
@@ -990,8 +985,7 @@ findParentsByOid(TableInfo *self,
 
 	if (numParents > 0)
 	{
-		self->parents = (TableInfo **)
-			pg_malloc(sizeof(TableInfo *) * numParents);
+		self->parents = pg_malloc_array(TableInfo *, numParents);
 		j = 0;
 		for (i = 0; i < numInherits; i++)
 		{
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index bfc49b66d2..7960b81c0e 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -151,7 +151,7 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
 	/*
 	 * Set up some special context used in compressing data.
 	 */
-	ctx = (lclContext *) pg_malloc0(sizeof(lclContext));
+	ctx = pg_malloc0_object(lclContext);
 	AH->formatData = (void *) ctx;
 	ctx->filePos = 0;
 	ctx->isSpecialScript = 0;
@@ -240,7 +240,7 @@ _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
 	lclTocEntry *ctx;
 	char		fn[K_STD_BUF_SIZE];
 
-	ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
+	ctx = pg_malloc0_object(lclTocEntry);
 	if (te->dataDumper != NULL)
 	{
 		snprintf(fn, sizeof(fn), "%d.dat", te->dumpId);
@@ -272,7 +272,7 @@ _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
 
 	if (ctx == NULL)
 	{
-		ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
+		ctx = pg_malloc0_object(lclTocEntry);
 		te->formatData = (void *) ctx;
 	}
 
@@ -337,7 +337,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
 	{
 		int			old_umask;
 
-		tm = pg_malloc0(sizeof(TAR_MEMBER));
+		tm = pg_malloc0_object(TAR_MEMBER);
 
 		/*
 		 * POSIX does not require, but permits, tmpfile() to restrict file
@@ -1052,7 +1052,7 @@ static TAR_MEMBER *
 _tarPositionTo(ArchiveHandle *AH, const char *filename)
 {
 	lclContext *ctx = (lclContext *) AH->formatData;
-	TAR_MEMBER *th = pg_malloc0(sizeof(TAR_MEMBER));
+	TAR_MEMBER *th = pg_malloc0_object(TAR_MEMBER);
 	char		c;
 	char		header[TAR_BLOCK_SIZE];
 	size_t		i,
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index e2e5678e2d..f5b9e268f2 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -247,8 +247,8 @@ main(int argc, char *argv[])
 	do
 	{
 #define PARAMS_ARRAY_SIZE	8
-		const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
-		const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
+		const char **keywords = pg_malloc_array(const char *, PARAMS_ARRAY_SIZE);
+		const char **values = pg_malloc_array(const char *, PARAMS_ARRAY_SIZE);
 
 		keywords[0] = "host";
 		values[0] = options.host;
@@ -750,7 +750,7 @@ simple_action_list_append(SimpleActionList *list,
 {
 	SimpleActionListCell *cell;
 
-	cell = (SimpleActionListCell *) pg_malloc(sizeof(SimpleActionListCell));
+	cell = pg_malloc_object(SimpleActionListCell);
 
 	cell->next = NULL;
 	cell->action = action;
diff --git a/src/common/config_info.c b/src/common/config_info.c
index aa643b63fe..891a1505cc 100644
--- a/src/common/config_info.c
+++ b/src/common/config_info.c
@@ -39,7 +39,7 @@ get_configdata(const char *my_exec_path, size_t *configdata_len)
 
 	/* Adjust this to match the number of items filled below */
 	*configdata_len = 23;
-	configdata = (ConfigData *) palloc(*configdata_len * sizeof(ConfigData));
+	configdata = palloc_array(ConfigData, *configdata_len);
 
 	configdata[i].name = pstrdup("BINDIR");
 	strlcpy(path, my_exec_path, sizeof(path));
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index 4c0da6e124..66168543a6 100644
--- a/src/common/controldata_utils.c
+++ b/src/common/controldata_utils.c
@@ -59,7 +59,7 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
 
 	AssertArg(crc_ok_p);
 
-	ControlFile = palloc(sizeof(ControlFileData));
+	ControlFile = palloc_object(ControlFileData);
 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
 
 #ifndef FRONTEND
-- 
2.37.1

#10Robert Haas
robertmhaas@gmail.com
In reply to: Peter Eisentraut (#7)
Re: Expand palloc/pg_malloc API

On Fri, Aug 12, 2022 at 3:31 AM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:

(Personally, I have always been a bit suspicious about using the name
palloc() without memory context semantics in frontend code, but I guess
this is wide-spread now.)

I think it would be a good thing to add memory context support to the
frontend. We could just put everything in a single context for
starters, and then frontend utilities that wanted to create other
contexts could do so.

There are difficulties, though. For instance, memory contexts are
nodes, and have a NodeTag. And I'm pretty sure we don't want frontend
code to know about all the backend node types. My suspicion is that
memory context types really shouldn't be node types, but right now,
they are. Whether that's the correct view or not, this kind of problem
means it's not a simple lift-and-shift to move the memory context code
into src/common. Someone would need to spend some time thinking about
how to engineer it.

--
Robert Haas
EDB: http://www.enterprisedb.com

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#9)
Re: Expand palloc/pg_malloc API

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

I have updated this patch set to rename the _obj() functions to
_object(), and I have dropped the _ptrtype() variants.

I have also split the patch to put the new API and the example uses into
separate patches.

This patch set seems fine to me, so I've marked it Ready for Committer.

I think serious consideration should be given to back-patching the
0001 part (that is, addition of the macros). Otherwise we'll have
to remember not to use these macros in code intended for back-patch,
and that'll be mighty annoying once we are used to them.

regards, tom lane

#12Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#10)
Re: Expand palloc/pg_malloc API

Robert Haas <robertmhaas@gmail.com> writes:

On Fri, Aug 12, 2022 at 3:31 AM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:

(Personally, I have always been a bit suspicious about using the name
palloc() without memory context semantics in frontend code, but I guess
this is wide-spread now.)

I think it would be a good thing to add memory context support to the
frontend. We could just put everything in a single context for
starters, and then frontend utilities that wanted to create other
contexts could do so.

Perhaps, but I think we should have at least one immediate use-case
for multiple contexts in frontend. Otherwise it's just useless extra
code. The whole point of memory contexts in the backend is that we
have well-defined lifespans for certain types of allocations (executor
state, function results, etc); but it's not very clear to me that the
same concept will be helpful in any of our frontend programs.

There are difficulties, though. For instance, memory contexts are
nodes, and have a NodeTag. And I'm pretty sure we don't want frontend
code to know about all the backend node types. My suspicion is that
memory context types really shouldn't be node types, but right now,
they are. Whether that's the correct view or not, this kind of problem
means it's not a simple lift-and-shift to move the memory context code
into src/common. Someone would need to spend some time thinking about
how to engineer it.

I don't really think that's much of an issue. We could replace the
nodetag fields with some sort of magic number and have just as much
wrong-pointer safety as in the backend. What I do take issue with
is moving the code into src/common. I think we'd be better off
just writing a distinct implementation for frontend. For one thing,
it's not apparent to me that aset.c is a good allocator for frontend
(and the other two surely are not).

This is all pretty off-topic for Peter's patch, though.

regards, tom lane

#13Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#11)
Re: Expand palloc/pg_malloc API

On 09.09.22 22:13, Tom Lane wrote:

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

I have updated this patch set to rename the _obj() functions to
_object(), and I have dropped the _ptrtype() variants.

I have also split the patch to put the new API and the example uses into
separate patches.

This patch set seems fine to me, so I've marked it Ready for Committer.

committed

I think serious consideration should be given to back-patching the
0001 part (that is, addition of the macros). Otherwise we'll have
to remember not to use these macros in code intended for back-patch,
and that'll be mighty annoying once we are used to them.

Yes, the 0001 patch is kept separate so that we can do that when we feel
the time is right.

#14Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#13)
Re: Expand palloc/pg_malloc API

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 09.09.22 22:13, Tom Lane wrote:

I think serious consideration should be given to back-patching the
0001 part (that is, addition of the macros). Otherwise we'll have
to remember not to use these macros in code intended for back-patch,
and that'll be mighty annoying once we are used to them.

Yes, the 0001 patch is kept separate so that we can do that when we feel
the time is right.

I think the right time is now, or at least as soon as you're
satisfied that the buildfarm is happy.

regards, tom lane

#15Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#14)
Re: Expand palloc/pg_malloc API

On 12.09.22 15:49, Tom Lane wrote:

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 09.09.22 22:13, Tom Lane wrote:

I think serious consideration should be given to back-patching the
0001 part (that is, addition of the macros). Otherwise we'll have
to remember not to use these macros in code intended for back-patch,
and that'll be mighty annoying once we are used to them.

Yes, the 0001 patch is kept separate so that we can do that when we feel
the time is right.

I think the right time is now, or at least as soon as you're
satisfied that the buildfarm is happy.

This has been done.

#16Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Peter Eisentraut (#13)
1 attachment(s)
Re: Expand palloc/pg_malloc API

I have another little idea that fits well here: repalloc0 and
repalloc0_array. These zero out the space added by repalloc. This is a
common pattern in the backend code that is quite hairy to code by hand.
See attached patch.

Attachments:

0001-Add-repalloc0-and-repalloc0_array.patchtext/plain; charset=UTF-8; name=0001-Add-repalloc0-and-repalloc0_array.patchDownload
From fa611ecf7c8a7b99d37c77da66b421d2f9ebfec3 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Wed, 14 Sep 2022 06:05:46 +0200
Subject: [PATCH] Add repalloc0 and repalloc0_array

These zero out the space added by repalloc.  This is a common pattern
that is quite hairy to code by hand.
---
 src/backend/executor/nodeHash.c          |  8 ++-----
 src/backend/libpq/be-fsstubs.c           |  9 +++-----
 src/backend/optimizer/util/placeholder.c | 13 ++++-------
 src/backend/optimizer/util/relnode.c     | 29 +++++++-----------------
 src/backend/parser/parse_param.c         | 10 +++-----
 src/backend/storage/lmgr/lwlock.c        |  9 ++------
 src/backend/utils/adt/ruleutils.c        |  9 ++------
 src/backend/utils/cache/typcache.c       | 10 ++------
 src/backend/utils/mmgr/mcxt.c            | 15 ++++++++++++
 src/include/utils/palloc.h               |  2 ++
 10 files changed, 43 insertions(+), 71 deletions(-)

diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 77dd1dae8b..674802a4ff 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -940,12 +940,8 @@ ExecHashIncreaseNumBatches(HashJoinTable hashtable)
 	else
 	{
 		/* enlarge arrays and zero out added entries */
-		hashtable->innerBatchFile = repalloc_array(hashtable->innerBatchFile, BufFile *, nbatch);
-		hashtable->outerBatchFile = repalloc_array(hashtable->outerBatchFile, BufFile *, nbatch);
-		MemSet(hashtable->innerBatchFile + oldnbatch, 0,
-			   (nbatch - oldnbatch) * sizeof(BufFile *));
-		MemSet(hashtable->outerBatchFile + oldnbatch, 0,
-			   (nbatch - oldnbatch) * sizeof(BufFile *));
+		hashtable->innerBatchFile = repalloc0_array(hashtable->innerBatchFile, BufFile *, nbatch, oldnbatch);
+		hashtable->outerBatchFile = repalloc0_array(hashtable->outerBatchFile, BufFile *, nbatch, oldnbatch);
 	}
 
 	MemoryContextSwitchTo(oldcxt);
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
index 3e5cada7eb..bf901a9cdc 100644
--- a/src/backend/libpq/be-fsstubs.c
+++ b/src/backend/libpq/be-fsstubs.c
@@ -696,19 +696,16 @@ newLOfd(void)
 		newsize = 64;
 		cookies = (LargeObjectDesc **)
 			MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
-		cookies_size = newsize;
 	}
 	else
 	{
 		/* Double size of array */
 		i = cookies_size;
 		newsize = cookies_size * 2;
-		cookies = (LargeObjectDesc **)
-			repalloc(cookies, newsize * sizeof(LargeObjectDesc *));
-		MemSet(cookies + cookies_size, 0,
-			   (newsize - cookies_size) * sizeof(LargeObjectDesc *));
-		cookies_size = newsize;
+		cookies =
+			repalloc0_array(cookies, LargeObjectDesc *, newsize, cookies_size);
 	}
+	cookies_size = newsize;
 
 	return i;
 }
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c
index c7bfa293c9..e40b3a12fd 100644
--- a/src/backend/optimizer/util/placeholder.c
+++ b/src/backend/optimizer/util/placeholder.c
@@ -133,16 +133,11 @@ find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
 		while (phinfo->phid >= new_size)
 			new_size *= 2;
 		if (root->placeholder_array)
-		{
-			root->placeholder_array = (PlaceHolderInfo **)
-				repalloc(root->placeholder_array,
-						 sizeof(PlaceHolderInfo *) * new_size);
-			MemSet(root->placeholder_array + root->placeholder_array_size, 0,
-				   sizeof(PlaceHolderInfo *) * (new_size - root->placeholder_array_size));
-		}
+			root->placeholder_array =
+				repalloc0_array(root->placeholder_array, PlaceHolderInfo *, new_size, root->placeholder_array_size);
 		else
-			root->placeholder_array = (PlaceHolderInfo **)
-				palloc0(new_size * sizeof(PlaceHolderInfo *));
+			root->placeholder_array =
+				palloc0_array(PlaceHolderInfo *, new_size);
 		root->placeholder_array_size = new_size;
 	}
 	root->placeholder_array[phinfo->phid] = phinfo;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index edcdd0a360..3ea40ff1b6 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -157,31 +157,18 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 
 	new_size = root->simple_rel_array_size + add_size;
 
-	root->simple_rel_array = (RelOptInfo **)
-		repalloc(root->simple_rel_array,
-				 sizeof(RelOptInfo *) * new_size);
-	MemSet(root->simple_rel_array + root->simple_rel_array_size,
-		   0, sizeof(RelOptInfo *) * add_size);
+	root->simple_rel_array =
+		repalloc0_array(root->simple_rel_array, RelOptInfo *, new_size, root->simple_rel_array_size);
 
-	root->simple_rte_array = (RangeTblEntry **)
-		repalloc(root->simple_rte_array,
-				 sizeof(RangeTblEntry *) * new_size);
-	MemSet(root->simple_rte_array + root->simple_rel_array_size,
-		   0, sizeof(RangeTblEntry *) * add_size);
+	root->simple_rte_array =
+		repalloc0_array(root->simple_rte_array, RangeTblEntry *, new_size, root->simple_rel_array_size);
 
 	if (root->append_rel_array)
-	{
-		root->append_rel_array = (AppendRelInfo **)
-			repalloc(root->append_rel_array,
-					 sizeof(AppendRelInfo *) * new_size);
-		MemSet(root->append_rel_array + root->simple_rel_array_size,
-			   0, sizeof(AppendRelInfo *) * add_size);
-	}
+		root->append_rel_array =
+			repalloc0_array(root->append_rel_array, AppendRelInfo *, new_size, root->simple_rel_array_size);
 	else
-	{
-		root->append_rel_array = (AppendRelInfo **)
-			palloc0(sizeof(AppendRelInfo *) * new_size);
-	}
+		root->append_rel_array =
+			palloc0_array(AppendRelInfo *, new_size);
 
 	root->simple_rel_array_size = new_size;
 }
diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c
index f668abfcb3..4efec211c5 100644
--- a/src/backend/parser/parse_param.c
+++ b/src/backend/parser/parse_param.c
@@ -145,14 +145,10 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref)
 	{
 		/* Need to enlarge param array */
 		if (*parstate->paramTypes)
-			*parstate->paramTypes = (Oid *) repalloc(*parstate->paramTypes,
-													 paramno * sizeof(Oid));
+			*parstate->paramTypes = repalloc0_array(*parstate->paramTypes, Oid,
+													paramno, *parstate->numParams);
 		else
-			*parstate->paramTypes = (Oid *) palloc(paramno * sizeof(Oid));
-		/* Zero out the previously-unreferenced slots */
-		MemSet(*parstate->paramTypes + *parstate->numParams,
-			   0,
-			   (paramno - *parstate->numParams) * sizeof(Oid));
+			*parstate->paramTypes = palloc0_array(Oid, paramno);
 		*parstate->numParams = paramno;
 	}
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 38317edaf9..a36dcf1a00 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -668,13 +668,8 @@ LWLockRegisterTranche(int tranche_id, const char *tranche_name)
 				MemoryContextAllocZero(TopMemoryContext,
 									   newalloc * sizeof(char *));
 		else
-		{
-			LWLockTrancheNames = (const char **)
-				repalloc(LWLockTrancheNames, newalloc * sizeof(char *));
-			memset(LWLockTrancheNames + LWLockTrancheNamesAllocated,
-				   0,
-				   (newalloc - LWLockTrancheNamesAllocated) * sizeof(char *));
-		}
+			LWLockTrancheNames =
+				repalloc0_array(LWLockTrancheNames, const char *, newalloc, LWLockTrancheNamesAllocated);
 		LWLockTrancheNamesAllocated = newalloc;
 	}
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 2b7b1b0c0f..0cd63383ec 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -4855,14 +4855,9 @@ expand_colnames_array_to(deparse_columns *colinfo, int n)
 	if (n > colinfo->num_cols)
 	{
 		if (colinfo->colnames == NULL)
-			colinfo->colnames = (char **) palloc0(n * sizeof(char *));
+			colinfo->colnames = palloc0_array(char *, n);
 		else
-		{
-			colinfo->colnames = (char **) repalloc(colinfo->colnames,
-												   n * sizeof(char *));
-			memset(colinfo->colnames + colinfo->num_cols, 0,
-				   (n - colinfo->num_cols) * sizeof(char *));
-		}
+			colinfo->colnames = repalloc0_array(colinfo->colnames, char *, n, colinfo->num_cols);
 		colinfo->num_cols = n;
 	}
 }
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 808f9ebd0d..739765cb09 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -1714,14 +1714,8 @@ ensure_record_cache_typmod_slot_exists(int32 typmod)
 	{
 		int32		newlen = pg_nextpower2_32(typmod + 1);
 
-		RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
-												  newlen * sizeof(TupleDesc));
-		memset(RecordCacheArray + RecordCacheArrayLen, 0,
-			   (newlen - RecordCacheArrayLen) * sizeof(TupleDesc));
-		RecordIdentifierArray = (uint64 *) repalloc(RecordIdentifierArray,
-													newlen * sizeof(uint64));
-		memset(RecordIdentifierArray + RecordCacheArrayLen, 0,
-			   (newlen - RecordCacheArrayLen) * sizeof(uint64));
+		RecordCacheArray = repalloc0_array(RecordCacheArray, TupleDesc, newlen, RecordCacheArrayLen);
+		RecordIdentifierArray = repalloc0_array(RecordIdentifierArray, uint64, newlen, RecordCacheArrayLen);
 		RecordCacheArrayLen = newlen;
 	}
 }
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 115a64cfe4..027af1e5de 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -1297,6 +1297,21 @@ repalloc(void *pointer, Size size)
 	return ret;
 }
 
+/*
+ * repalloc
+ *		Adjust the size of a previously allocated chunk and zero out the added
+ *		space.
+ */
+void *
+repalloc0(void *pointer, Size size, Size oldsize)
+{
+	void	   *ret;
+
+	ret = repalloc(pointer, size);
+	memset((char *) ret + oldsize, 0, (size - oldsize));
+	return ret;
+}
+
 /*
  * MemoryContextAllocHuge
  *		Allocate (possibly-expansive) space within the specified context.
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index a0b62aa7b0..af7a4a2311 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -78,6 +78,7 @@ extern void *palloc(Size size);
 extern void *palloc0(Size size);
 extern void *palloc_extended(Size size, int flags);
 extern pg_nodiscard void *repalloc(void *pointer, Size size);
+extern pg_nodiscard void *repalloc0(void *pointer, Size size, Size oldsize);
 extern void pfree(void *pointer);
 
 /*
@@ -101,6 +102,7 @@ extern void pfree(void *pointer);
  * objects of type "type"
  */
 #define repalloc_array(pointer, type, count) ((type *) repalloc(pointer, sizeof(type) * (count)))
+#define repalloc0_array(pointer, type, count, oldcount) ((type *) repalloc0(pointer, sizeof(type) * (count), sizeof(type) * (oldcount)))
 
 /*
  * The result of palloc() is always word-aligned, so we can skip testing
-- 
2.37.3

#17Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#16)
Re: Expand palloc/pg_malloc API

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

I have another little idea that fits well here: repalloc0 and
repalloc0_array. These zero out the space added by repalloc. This is a
common pattern in the backend code that is quite hairy to code by hand.
See attached patch.

+1 in general --- you've put your finger on something I felt was
missing, but couldn't quite identify.

However, I'm a bit bothered by the proposed API:

+extern pg_nodiscard void *repalloc0(void *pointer, Size size, Size oldsize);

It kind of feels that the argument order should be pointer, oldsize, size.
It feels even more strongly that people will get the ordering wrong,
whichever we choose. Is there a way to make that more bulletproof?

The only thought that comes to mind offhand is that the only plausible
use-case is with size >= oldsize, so maybe an assertion or even a
runtime check would help to catch getting it backwards. (I notice
that your proposed coding will fail rather catastrophically if the
caller gets it backwards. An assertion failure would be better.)

regards, tom lane

#18Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#17)
Re: Expand palloc/pg_malloc API

I wrote:

It kind of feels that the argument order should be pointer, oldsize, size.
It feels even more strongly that people will get the ordering wrong,
whichever we choose. Is there a way to make that more bulletproof?

Actually ... an even-more-terrifyingly-plausible misuse is that the
supplied oldsize is different from the actual previous allocation.
We should try to check that. In MEMORY_CONTEXT_CHECKING builds
it should be possible to assert that oldsize == requested_size.
We don't have that data if !MEMORY_CONTEXT_CHECKING, but we could
at least assert that oldsize <= allocated chunk size.

regards, tom lane

#19Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#18)
Re: Expand palloc/pg_malloc API

On 14.09.22 06:53, Tom Lane wrote:

I wrote:

It kind of feels that the argument order should be pointer, oldsize, size.
It feels even more strongly that people will get the ordering wrong,
whichever we choose. Is there a way to make that more bulletproof?

Actually ... an even-more-terrifyingly-plausible misuse is that the
supplied oldsize is different from the actual previous allocation.
We should try to check that. In MEMORY_CONTEXT_CHECKING builds
it should be possible to assert that oldsize == requested_size.
We don't have that data if !MEMORY_CONTEXT_CHECKING, but we could
at least assert that oldsize <= allocated chunk size.

I'm not very familiar with MEMORY_CONTEXT_CHECKING. Where would one get
these values?

#20Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#19)
Re: Expand palloc/pg_malloc API

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 14.09.22 06:53, Tom Lane wrote:

Actually ... an even-more-terrifyingly-plausible misuse is that the
supplied oldsize is different from the actual previous allocation.
We should try to check that. In MEMORY_CONTEXT_CHECKING builds
it should be possible to assert that oldsize == requested_size.
We don't have that data if !MEMORY_CONTEXT_CHECKING, but we could
at least assert that oldsize <= allocated chunk size.

I'm not very familiar with MEMORY_CONTEXT_CHECKING. Where would one get
these values?

Hmm ... the individual allocators have that info, but mcxt.c doesn't
have access to it. I guess we could invent an additional "method"
to return the requested size of a chunk, which is only available in
MEMORY_CONTEXT_CHECKING builds, or maybe in !MEMORY_CONTEXT_CHECKING
it returns the allocated size instead.

regards, tom lane

#21Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#20)
1 attachment(s)
Re: Expand palloc/pg_malloc API

On 11.10.22 18:04, Tom Lane wrote:

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 14.09.22 06:53, Tom Lane wrote:

Actually ... an even-more-terrifyingly-plausible misuse is that the
supplied oldsize is different from the actual previous allocation.
We should try to check that. In MEMORY_CONTEXT_CHECKING builds
it should be possible to assert that oldsize == requested_size.
We don't have that data if !MEMORY_CONTEXT_CHECKING, but we could
at least assert that oldsize <= allocated chunk size.

I'm not very familiar with MEMORY_CONTEXT_CHECKING. Where would one get
these values?

Hmm ... the individual allocators have that info, but mcxt.c doesn't
have access to it. I guess we could invent an additional "method"
to return the requested size of a chunk, which is only available in
MEMORY_CONTEXT_CHECKING builds, or maybe in !MEMORY_CONTEXT_CHECKING
it returns the allocated size instead.

I'm not sure whether that amount of additional work would be useful
relative to the size of this patch. Is the patch as it stands now
making the code less robust than what the code is doing now?

In the meantime, here is an updated patch with the argument order
swapped and an additional assertion, as previously discussed.

Attachments:

v2-0001-Add-repalloc0-and-repalloc0_array.patchtext/plain; charset=UTF-8; name=v2-0001-Add-repalloc0-and-repalloc0_array.patchDownload
From cc0069bf2b0b3d40dda462d441af5d8b07ef1f57 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 31 Oct 2022 09:17:57 +0100
Subject: [PATCH v2] Add repalloc0 and repalloc0_array

These zero out the space added by repalloc.  This is a common pattern
that is quite hairy to code by hand.

Discussion: https://www.postgresql.org/message-id/b66dfc89-9365-cb57-4e1f-b7d31813eeec@enterprisedb.com
---
 src/backend/executor/nodeHash.c          |  8 ++-----
 src/backend/libpq/be-fsstubs.c           |  9 +++-----
 src/backend/optimizer/util/placeholder.c | 13 ++++-------
 src/backend/optimizer/util/relnode.c     | 29 +++++++-----------------
 src/backend/parser/parse_param.c         | 10 +++-----
 src/backend/storage/lmgr/lwlock.c        |  9 ++------
 src/backend/utils/adt/ruleutils.c        |  9 ++------
 src/backend/utils/cache/typcache.c       | 10 ++------
 src/backend/utils/mmgr/mcxt.c            | 18 +++++++++++++++
 src/include/utils/palloc.h               |  2 ++
 10 files changed, 46 insertions(+), 71 deletions(-)

diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 6622b202c229..2e6cce4802e5 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -940,12 +940,8 @@ ExecHashIncreaseNumBatches(HashJoinTable hashtable)
 	else
 	{
 		/* enlarge arrays and zero out added entries */
-		hashtable->innerBatchFile = repalloc_array(hashtable->innerBatchFile, BufFile *, nbatch);
-		hashtable->outerBatchFile = repalloc_array(hashtable->outerBatchFile, BufFile *, nbatch);
-		MemSet(hashtable->innerBatchFile + oldnbatch, 0,
-			   (nbatch - oldnbatch) * sizeof(BufFile *));
-		MemSet(hashtable->outerBatchFile + oldnbatch, 0,
-			   (nbatch - oldnbatch) * sizeof(BufFile *));
+		hashtable->innerBatchFile = repalloc0_array(hashtable->innerBatchFile, BufFile *, oldnbatch, nbatch);
+		hashtable->outerBatchFile = repalloc0_array(hashtable->outerBatchFile, BufFile *, oldnbatch, nbatch);
 	}
 
 	MemoryContextSwitchTo(oldcxt);
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
index 3e5cada7eb53..106fdcdf817b 100644
--- a/src/backend/libpq/be-fsstubs.c
+++ b/src/backend/libpq/be-fsstubs.c
@@ -696,19 +696,16 @@ newLOfd(void)
 		newsize = 64;
 		cookies = (LargeObjectDesc **)
 			MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
-		cookies_size = newsize;
 	}
 	else
 	{
 		/* Double size of array */
 		i = cookies_size;
 		newsize = cookies_size * 2;
-		cookies = (LargeObjectDesc **)
-			repalloc(cookies, newsize * sizeof(LargeObjectDesc *));
-		MemSet(cookies + cookies_size, 0,
-			   (newsize - cookies_size) * sizeof(LargeObjectDesc *));
-		cookies_size = newsize;
+		cookies =
+			repalloc0_array(cookies, LargeObjectDesc *, cookies_size, newsize);
 	}
+	cookies_size = newsize;
 
 	return i;
 }
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c
index c7bfa293c9ff..c55027377fe7 100644
--- a/src/backend/optimizer/util/placeholder.c
+++ b/src/backend/optimizer/util/placeholder.c
@@ -133,16 +133,11 @@ find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
 		while (phinfo->phid >= new_size)
 			new_size *= 2;
 		if (root->placeholder_array)
-		{
-			root->placeholder_array = (PlaceHolderInfo **)
-				repalloc(root->placeholder_array,
-						 sizeof(PlaceHolderInfo *) * new_size);
-			MemSet(root->placeholder_array + root->placeholder_array_size, 0,
-				   sizeof(PlaceHolderInfo *) * (new_size - root->placeholder_array_size));
-		}
+			root->placeholder_array =
+				repalloc0_array(root->placeholder_array, PlaceHolderInfo *, root->placeholder_array_size, new_size);
 		else
-			root->placeholder_array = (PlaceHolderInfo **)
-				palloc0(new_size * sizeof(PlaceHolderInfo *));
+			root->placeholder_array =
+				palloc0_array(PlaceHolderInfo *, new_size);
 		root->placeholder_array_size = new_size;
 	}
 	root->placeholder_array[phinfo->phid] = phinfo;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 1786a3daddc8..d7b4434e7f4a 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -157,31 +157,18 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 
 	new_size = root->simple_rel_array_size + add_size;
 
-	root->simple_rel_array = (RelOptInfo **)
-		repalloc(root->simple_rel_array,
-				 sizeof(RelOptInfo *) * new_size);
-	MemSet(root->simple_rel_array + root->simple_rel_array_size,
-		   0, sizeof(RelOptInfo *) * add_size);
+	root->simple_rel_array =
+		repalloc0_array(root->simple_rel_array, RelOptInfo *, root->simple_rel_array_size, new_size);
 
-	root->simple_rte_array = (RangeTblEntry **)
-		repalloc(root->simple_rte_array,
-				 sizeof(RangeTblEntry *) * new_size);
-	MemSet(root->simple_rte_array + root->simple_rel_array_size,
-		   0, sizeof(RangeTblEntry *) * add_size);
+	root->simple_rte_array =
+		repalloc0_array(root->simple_rte_array, RangeTblEntry *, root->simple_rel_array_size, new_size);
 
 	if (root->append_rel_array)
-	{
-		root->append_rel_array = (AppendRelInfo **)
-			repalloc(root->append_rel_array,
-					 sizeof(AppendRelInfo *) * new_size);
-		MemSet(root->append_rel_array + root->simple_rel_array_size,
-			   0, sizeof(AppendRelInfo *) * add_size);
-	}
+		root->append_rel_array =
+			repalloc0_array(root->append_rel_array, AppendRelInfo *, root->simple_rel_array_size, new_size);
 	else
-	{
-		root->append_rel_array = (AppendRelInfo **)
-			palloc0(sizeof(AppendRelInfo *) * new_size);
-	}
+		root->append_rel_array =
+			palloc0_array(AppendRelInfo *, new_size);
 
 	root->simple_rel_array_size = new_size;
 }
diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c
index f668abfcb336..e80876aa25e0 100644
--- a/src/backend/parser/parse_param.c
+++ b/src/backend/parser/parse_param.c
@@ -145,14 +145,10 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref)
 	{
 		/* Need to enlarge param array */
 		if (*parstate->paramTypes)
-			*parstate->paramTypes = (Oid *) repalloc(*parstate->paramTypes,
-													 paramno * sizeof(Oid));
+			*parstate->paramTypes = repalloc0_array(*parstate->paramTypes, Oid,
+													*parstate->numParams, paramno);
 		else
-			*parstate->paramTypes = (Oid *) palloc(paramno * sizeof(Oid));
-		/* Zero out the previously-unreferenced slots */
-		MemSet(*parstate->paramTypes + *parstate->numParams,
-			   0,
-			   (paramno - *parstate->numParams) * sizeof(Oid));
+			*parstate->paramTypes = palloc0_array(Oid, paramno);
 		*parstate->numParams = paramno;
 	}
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 0fc0cf6ebbd9..532cd67f4e3c 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -668,13 +668,8 @@ LWLockRegisterTranche(int tranche_id, const char *tranche_name)
 				MemoryContextAllocZero(TopMemoryContext,
 									   newalloc * sizeof(char *));
 		else
-		{
-			LWLockTrancheNames = (const char **)
-				repalloc(LWLockTrancheNames, newalloc * sizeof(char *));
-			memset(LWLockTrancheNames + LWLockTrancheNamesAllocated,
-				   0,
-				   (newalloc - LWLockTrancheNamesAllocated) * sizeof(char *));
-		}
+			LWLockTrancheNames =
+				repalloc0_array(LWLockTrancheNames, const char *, LWLockTrancheNamesAllocated, newalloc);
 		LWLockTrancheNamesAllocated = newalloc;
 	}
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 70d723e80cac..c5a49d0be342 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -4855,14 +4855,9 @@ expand_colnames_array_to(deparse_columns *colinfo, int n)
 	if (n > colinfo->num_cols)
 	{
 		if (colinfo->colnames == NULL)
-			colinfo->colnames = (char **) palloc0(n * sizeof(char *));
+			colinfo->colnames = palloc0_array(char *, n);
 		else
-		{
-			colinfo->colnames = (char **) repalloc(colinfo->colnames,
-												   n * sizeof(char *));
-			memset(colinfo->colnames + colinfo->num_cols, 0,
-				   (n - colinfo->num_cols) * sizeof(char *));
-		}
+			colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
 		colinfo->num_cols = n;
 	}
 }
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 808f9ebd0d2f..b69366fa29de 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -1714,14 +1714,8 @@ ensure_record_cache_typmod_slot_exists(int32 typmod)
 	{
 		int32		newlen = pg_nextpower2_32(typmod + 1);
 
-		RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
-												  newlen * sizeof(TupleDesc));
-		memset(RecordCacheArray + RecordCacheArrayLen, 0,
-			   (newlen - RecordCacheArrayLen) * sizeof(TupleDesc));
-		RecordIdentifierArray = (uint64 *) repalloc(RecordIdentifierArray,
-													newlen * sizeof(uint64));
-		memset(RecordIdentifierArray + RecordCacheArrayLen, 0,
-			   (newlen - RecordCacheArrayLen) * sizeof(uint64));
+		RecordCacheArray = repalloc0_array(RecordCacheArray, TupleDesc, RecordCacheArrayLen, newlen);
+		RecordIdentifierArray = repalloc0_array(RecordIdentifierArray, uint64, RecordCacheArrayLen, newlen);
 		RecordCacheArrayLen = newlen;
 	}
 }
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index f526ca82c15d..9595a148d1ba 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -1395,6 +1395,24 @@ repalloc_extended(void *pointer, Size size, int flags)
 	return ret;
 }
 
+/*
+ * repalloc0
+ *		Adjust the size of a previously allocated chunk and zero out the added
+ *		space.
+ */
+void *
+repalloc0(void *pointer, Size oldsize, Size size)
+{
+	void	   *ret;
+
+	/* catch wrong argument order */
+	Assert(size >= oldsize);
+
+	ret = repalloc(pointer, size);
+	memset((char *) ret + oldsize, 0, (size - oldsize));
+	return ret;
+}
+
 /*
  * MemoryContextAllocHuge
  *		Allocate (possibly-expansive) space within the specified context.
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index 8eee0e293857..72d4e70dc649 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -80,6 +80,7 @@ extern void *palloc_extended(Size size, int flags);
 extern pg_nodiscard void *repalloc(void *pointer, Size size);
 extern pg_nodiscard void *repalloc_extended(void *pointer,
 											Size size, int flags);
+extern pg_nodiscard void *repalloc0(void *pointer, Size oldsize, Size size);
 extern void pfree(void *pointer);
 
 /*
@@ -103,6 +104,7 @@ extern void pfree(void *pointer);
  * objects of type "type"
  */
 #define repalloc_array(pointer, type, count) ((type *) repalloc(pointer, sizeof(type) * (count)))
+#define repalloc0_array(pointer, type, oldcount, count) ((type *) repalloc0(pointer, sizeof(type) * (oldcount), sizeof(type) * (count)))
 
 /*
  * The result of palloc() is always word-aligned, so we can skip testing
-- 
2.37.3

#22Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#21)
Re: Expand palloc/pg_malloc API

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 11.10.22 18:04, Tom Lane wrote:

Hmm ... the individual allocators have that info, but mcxt.c doesn't
have access to it. I guess we could invent an additional "method"
to return the requested size of a chunk, which is only available in
MEMORY_CONTEXT_CHECKING builds, or maybe in !MEMORY_CONTEXT_CHECKING
it returns the allocated size instead.

I'm not sure whether that amount of additional work would be useful
relative to the size of this patch. Is the patch as it stands now
making the code less robust than what the code is doing now?

No. It's slightly annoying that the call sites still have to track
the old size of the allocation, but I guess that they have to have
that information in order to know that they need to repalloc in the
first place. I agree that this patch does make things easier to
read and a bit less error-prone.

Also, I realized that what I proposed above doesn't really work
anyway for this purpose. Consider

ptr = palloc(size);
... fill all "size" bytes ...
ptr = repalloc0(ptr, size, newsize);

where the initial size request isn't a power of 2. If production builds
rely on the initial allocated size not requested size to decide where to
start zeroing, this would work (no uninitialized holes) in a debug build,
but leave some uninitialized bytes in a production build, which is
absolutely horrible. So I guess we have to rely on the callers to
track their requests.

In the meantime, here is an updated patch with the argument order
swapped and an additional assertion, as previously discussed.

I think it'd be worth expending an actual runtime test in repalloc0,
that is

if (unlikely(oldsize > size))
elog(ERROR, "invalid repalloc0 call: oldsize %zu, new size %zu",
oldsize, size);

This seems cheap compared to the cost of the repalloc+memset, and the
consequences of not detecting the error seem pretty catastrophic ---
the memset would try to zero almost your whole address space.

No objections beyond that. I've marked this RFC.

regards, tom lane