В письме от пятница, 30 ноября 2018 г. 23:57:21 MSK пользователь Dmitry Dolgov 
написал:

> Looks like there are some problems with this patch on windows:
 
> src/backend/access/common/reloptions.c(1469): error C2059: syntax error :
> '}'
> 
> https://ci.appveyor.com/project/postgresql-cfbot/postgresql/build/1.0.22359
Phew... It took me a while to get proper windows building environment... But 
finally I did it...
This was some MSVC specific error, that happens when you create empty array or 
something like this.  I've rewritten that function to remove this array at 
all. Now MSVC successfully builds it.

I also did some codestyle improvements, and rebased the patch against the 
current master.

The new patch is in attachment.

> > I get that, but I strongly suggest not creating 10 loosely related
> > threads, but keeping it as a patch series in one thread. It's really
> > hard to follow for people not continually paying otherwise.
> 
> Totally agree. Probably this also makes it harder to see the big picture
> behind this patch - which is in turn probably preventing it from getting
> some more review. I hope it doesn't sounds ridiculous, taking into account
> your efforts by splitting the patch, but maybe it makes sense to gather
> these pieces together (as a separate commits, of course) in one thread?

The big picture is following. I started from the task: Add possibility to set 
up opclass parameters. (Nikita Glukhov now doing it)

I found an reloptions code, that does almost same thing, but it is not flexible 
at all, and I can't reuse it for opclass parameters as it is now.

So I came to decision to rewrite reloptions code, so it can be used for 
reloptions opclass options and any other kind of options we may need in 
future.

While rewriting the code, I found some places in the code that goes from what 
seems to be a very long time, and also need refreshing.

This is one of the things. 

It is not 100% necessary. Postgres will work with it as it is for ten years or  
more. But since I've touched this part of the code, I want to make this code 
more consistent, and more neat. 

That's what I am doing. That is what all this patches about. 

I'd be happy if we move this task at last.
diff --git a/.gitignore b/.gitignore
index 794e35b..37331c2 100644
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index eece89a..833d084 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -22,7 +22,7 @@
 #include "access/htup_details.h"
 #include "access/nbtree.h"
 #include "access/reloptions.h"
-#include "access/spgist.h"
+#include "access/spgist_private.h"
 #include "access/tuptoaster.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
@@ -46,9 +46,8 @@
  * upper and lower bounds (if applicable); for strings, consider a validation
  * routine.
  * (ii) add a record below (or use add_<type>_reloption).
- * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
- * (iv) add it to the appropriate handling routine (perhaps
- * default_reloptions)
+ * (iii) add it to the appropriate options struct
+ * (iv) add it to the appropriate handling routine
  * (v) make sure the lock level is set correctly for that operation
  * (vi) don't forget to document the option
  *
@@ -1019,7 +1018,7 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 		case RELKIND_TOASTVALUE:
 		case RELKIND_MATVIEW:
 		case RELKIND_PARTITIONED_TABLE:
-			options = heap_reloptions(classForm->relkind, datum, false);
+			options = relation_reloptions(classForm->relkind, datum, false);
 			break;
 		case RELKIND_VIEW:
 			options = view_reloptions(datum, false);
@@ -1352,63 +1351,69 @@ fillRelOptions(void *rdopts, Size basesize,
 
 
 /*
- * Option parser for anything that uses StdRdOptions.
+ * Option parsing definition for autovacuum. Used in toast and heap options.
+ */
+
+#define AUTOVACUUM_RELOPTIONS(OFFSET)                                \
+		{"autovacuum_enabled", RELOPT_TYPE_BOOL,                     \
+		OFFSET + offsetof(AutoVacOpts, enabled)},                    \
+		{"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,             \
+		OFFSET + offsetof(AutoVacOpts, vacuum_threshold)},           \
+		{"autovacuum_analyze_threshold", RELOPT_TYPE_INT,            \
+		OFFSET + offsetof(AutoVacOpts, analyze_threshold)},          \
+		{"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,            \
+		OFFSET + offsetof(AutoVacOpts, vacuum_cost_delay)},          \
+		{"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,            \
+		OFFSET + offsetof(AutoVacOpts, vacuum_cost_limit)},          \
+		{"autovacuum_freeze_min_age", RELOPT_TYPE_INT,               \
+		OFFSET + offsetof(AutoVacOpts, freeze_min_age)},             \
+		{"autovacuum_freeze_max_age", RELOPT_TYPE_INT,               \
+		OFFSET + offsetof(AutoVacOpts, freeze_max_age)},             \
+		{"autovacuum_freeze_table_age", RELOPT_TYPE_INT,             \
+		OFFSET + offsetof(AutoVacOpts, freeze_table_age)},           \
+		{"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,     \
+		OFFSET + offsetof(AutoVacOpts, multixact_freeze_min_age)},   \
+		{"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,     \
+		OFFSET + offsetof(AutoVacOpts, multixact_freeze_max_age)},   \
+		{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,   \
+		OFFSET + offsetof(AutoVacOpts, multixact_freeze_table_age)}, \
+		{"log_autovacuum_min_duration", RELOPT_TYPE_INT,             \
+		OFFSET + offsetof(AutoVacOpts, log_min_duration)},           \
+		{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,         \
+		OFFSET + offsetof(AutoVacOpts, vacuum_scale_factor)},        \
+		{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,        \
+		OFFSET + offsetof(AutoVacOpts, analyze_scale_factor)}
+
+/*
+ * Option parser for heap
  */
 bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
+heap_reloptions(Datum reloptions, bool validate)
 {
 	relopt_value *options;
-	StdRdOptions *rdopts;
+	HeapRelOptions *rdopts;
 	int			numoptions;
 	static const relopt_parse_elt tab[] = {
-		{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
-		{"autovacuum_enabled", RELOPT_TYPE_BOOL,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
-		{"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
-		{"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
-		{"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
-		{"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
-		{"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
-		{"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
-		{"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
-		{"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
-		{"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
-		{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
-		{"log_autovacuum_min_duration", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
+		{"fillfactor", RELOPT_TYPE_INT, offsetof(HeapRelOptions, fillfactor)},
+		AUTOVACUUM_RELOPTIONS(offsetof(HeapRelOptions, autovacuum)),
 		{"toast_tuple_target", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, toast_tuple_target)},
-		{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
-		{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
-		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
+		offsetof(HeapRelOptions, toast_tuple_target)},
 		{"user_catalog_table", RELOPT_TYPE_BOOL,
-		offsetof(StdRdOptions, user_catalog_table)},
+		offsetof(HeapRelOptions, user_catalog_table)},
 		{"parallel_workers", RELOPT_TYPE_INT,
-		offsetof(StdRdOptions, parallel_workers)},
-		{"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
-		offsetof(StdRdOptions, vacuum_cleanup_index_scale_factor)}
+		offsetof(HeapRelOptions, parallel_workers)}
 	};
 
-	options = parseRelOptions(reloptions, validate, kind, &numoptions);
+	options = parseRelOptions(reloptions, validate, RELOPT_KIND_HEAP,
+							  &numoptions);
 
 	/* if none set, we're done */
 	if (numoptions == 0)
 		return NULL;
 
-	rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
+	rdopts = allocateReloptStruct(sizeof(HeapRelOptions), options, numoptions);
 
-	fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
+	fillRelOptions((void *) rdopts, sizeof(HeapRelOptions), options, numoptions,
 				   validate, tab, lengthof(tab));
 
 	pfree(options);
@@ -1417,6 +1422,60 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 }
 
 /*
+ * Option parser for toast
+ */
+bytea *
+toast_reloptions(Datum reloptions, bool validate)
+{
+	relopt_value *options;
+	ToastRelOptions *rdopts;
+	int			numoptions;
+	static const relopt_parse_elt tab[] = {
+		AUTOVACUUM_RELOPTIONS(offsetof(ToastRelOptions, autovacuum)),
+	};
+
+	options = parseRelOptions(reloptions, validate, RELOPT_KIND_TOAST,
+							  &numoptions);
+
+	/* if none set, we're done */
+	if (numoptions == 0)
+		return NULL;
+
+	rdopts = allocateReloptStruct(sizeof(ToastRelOptions), options, numoptions);
+
+	fillRelOptions((void *) rdopts, sizeof(ToastRelOptions), options,
+				   numoptions, validate, tab, lengthof(tab));
+
+	/* adjust default-only parameters for TOAST relations */
+	rdopts->autovacuum.analyze_threshold = -1;
+	rdopts->autovacuum.analyze_scale_factor = -1;
+
+	pfree(options);
+
+	return (bytea *) rdopts;
+}
+
+/*
+ * Option parser for partitioned relations
+ */
+bytea *
+partitioned_reloptions(Datum reloptions, bool validate)
+{
+	int	  numoptions;
+
+  /*
+   * Since there is no options for patitioned table for now, we just do
+   * validation to report incorrect option error and leave.
+   */
+
+  if (validate)
+		parseRelOptions(reloptions, validate, RELOPT_KIND_PARTITIONED,
+						&numoptions);
+
+	return NULL;
+}
+
+/*
  * Option parser for views
  */
 bytea *
@@ -1449,39 +1508,26 @@ view_reloptions(Datum reloptions, bool validate)
 }
 
 /*
- * Parse options for heaps, views and toast tables.
+ * Parse options for heaps, toast, views and partitioned tables.
  */
 bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+relation_reloptions(char relkind, Datum reloptions, bool validate)
 {
-	StdRdOptions *rdopts;
-
 	switch (relkind)
 	{
 		case RELKIND_TOASTVALUE:
-			rdopts = (StdRdOptions *)
-				default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
-			if (rdopts != NULL)
-			{
-				/* adjust default-only parameters for TOAST relations */
-				rdopts->fillfactor = 100;
-				rdopts->autovacuum.analyze_threshold = -1;
-				rdopts->autovacuum.analyze_scale_factor = -1;
-			}
-			return (bytea *) rdopts;
+			return toast_reloptions(reloptions, validate);
 		case RELKIND_RELATION:
 		case RELKIND_MATVIEW:
-			return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
+			return heap_reloptions(reloptions, validate);
 		case RELKIND_PARTITIONED_TABLE:
-			return default_reloptions(reloptions, validate,
-									  RELOPT_KIND_PARTITIONED);
+			return partitioned_reloptions(reloptions, validate);
 		default:
 			/* other relkinds are not supported */
 			return NULL;
 	}
 }
 
-
 /*
  * Parse options for indexes.
  *
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 6825c14..e5a6dd8 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -369,7 +369,7 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
 	data_width = sizeof(uint32);
 	item_width = MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) +
 		sizeof(ItemIdData);		/* include the line pointer */
-	ffactor = RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / item_width;
+	ffactor = (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width;
 	/* keep to a sane range */
 	if (ffactor < 10)
 		ffactor = 10;
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 7c9b2cf..8cc1bd1 100644
--- a/src/backend/access/hash/hashutil.c
+++ b/src/backend/access/hash/hashutil.c
@@ -289,7 +289,28 @@ _hash_checkpage(Relation rel, Buffer buf, int flags)
 bytea *
 hashoptions(Datum reloptions, bool validate)
 {
-	return default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
+	relopt_value *options;
+	HashRelOptions *rdopts;
+	int			numoptions;
+	static const relopt_parse_elt tab[] = {
+		{"fillfactor", RELOPT_TYPE_INT, offsetof(HashRelOptions, fillfactor)},
+	};
+
+	options = parseRelOptions(reloptions, validate, RELOPT_KIND_HASH,
+							  &numoptions);
+
+	/* if none set, we're done */
+	if (numoptions == 0)
+		return NULL;
+
+	rdopts = allocateReloptStruct(sizeof(HashRelOptions), options, numoptions);
+
+	fillRelOptions((void *) rdopts, sizeof(HashRelOptions), options, numoptions,
+				   validate, tab, lengthof(tab));
+
+	pfree(options);
+
+	return (bytea *) rdopts;
 }
 
 /*
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 9650145..f60321d 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2712,8 +2712,10 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
 	AssertArg(!(options & HEAP_INSERT_NO_LOGICAL));
 
 	needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
-	saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
-												   HEAP_DEFAULT_FILLFACTOR);
+	if (IsToastRelation(relation))
+		saveFreeSpace = ToastGetTargetPageFreeSpace();
+	else
+		saveFreeSpace = HeapGetTargetPageFreeSpace(relation);
 
 	/* Toast and set header data in all the tuples */
 	heaptuples = palloc(ntuples * sizeof(HeapTuple));
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index b8b5871..1574c0c 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -19,6 +19,7 @@
 #include "access/hio.h"
 #include "access/htup_details.h"
 #include "access/visibilitymap.h"
+#include "catalog/catalog.h"
 #include "storage/bufmgr.h"
 #include "storage/freespace.h"
 #include "storage/lmgr.h"
@@ -339,8 +340,10 @@ RelationGetBufferForTuple(Relation relation, Size len,
 						len, MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
-	saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
-												   HEAP_DEFAULT_FILLFACTOR);
+	if (IsToastRelation(relation))
+		saveFreeSpace = ToastGetTargetPageFreeSpace();
+	else
+		saveFreeSpace = HeapGetTargetPageFreeSpace(relation);
 
 	if (otherBuffer != InvalidBuffer)
 		otherBlock = BufferGetBlockNumber(otherBuffer);
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index c2f5343..cc0b112 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -130,8 +130,11 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
 	 * important than sometimes getting a wrong answer in what is after all
 	 * just a heuristic estimate.
 	 */
-	minfree = RelationGetTargetPageFreeSpace(relation,
-											 HEAP_DEFAULT_FILLFACTOR);
+
+	if (IsToastRelation(relation))
+		minfree = ToastGetTargetPageFreeSpace();
+	else
+		minfree = HeapGetTargetPageFreeSpace(relation);
 	minfree = Max(minfree, BLCKSZ / 10);
 
 	if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 44caeca..3c5b033 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -683,8 +683,10 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 						len, MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
-	saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel,
-												   HEAP_DEFAULT_FILLFACTOR);
+	if (IsToastRelation(state->rs_new_rel))
+		saveFreeSpace = ToastGetTargetPageFreeSpace();
+	else
+		saveFreeSpace = HeapGetTargetPageFreeSpace(state->rs_new_rel);
 
 	/* Now we can check to see if there's enough free space already. */
 	if (state->rs_buffer_valid)
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index d26c081..1f83601 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -725,7 +725,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 		hoff += BITMAPLEN(numAttrs);
 	hoff = MAXALIGN(hoff);
 	/* now convert to a limit on the tuple data size */
-	maxDataLen = RelationGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
+	maxDataLen = HeapGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
 
 	/*
 	 * Look for attributes with attstorage 'x' to compress.  Also find large
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index a5915a5..c8b0af3 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1607,8 +1607,7 @@ _bt_findsplitloc(Relation rel,
 	state.is_rightmost = P_RIGHTMOST(opaque);
 	state.have_split = false;
 	if (state.is_leaf)
-		state.fillfactor = RelationGetFillFactor(rel,
-												 BTREE_DEFAULT_FILLFACTOR);
+		state.fillfactor = BTGetFillFactor(rel);
 	else
 		state.fillfactor = BTREE_NONLEAF_FILLFACTOR;
 	state.newitemonleft = false;	/* these just to keep compiler quiet */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index e8725fb..958b801 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -814,7 +814,7 @@ _bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
 	}
 	else
 	{
-		StdRdOptions *relopts;
+		BTRelOptions *relopts;
 		float8		cleanup_scale_factor;
 		float8		prev_num_heap_tuples;
 
@@ -825,7 +825,7 @@ _bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
 		 * tuples exceeds vacuum_cleanup_index_scale_factor fraction of
 		 * original tuples count.
 		 */
-		relopts = (StdRdOptions *) info->index->rd_options;
+		relopts = (BTRelOptions *) info->index->rd_options;
 		cleanup_scale_factor = (relopts &&
 								relopts->vacuum_cleanup_index_scale_factor >= 0)
 			? relopts->vacuum_cleanup_index_scale_factor
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index 16f5755..f8c4fb4 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -681,8 +681,9 @@ _bt_pagestate(BTWriteState *wstate, uint32 level)
 	if (level > 0)
 		state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
 	else
-		state->btps_full = RelationGetTargetPageFreeSpace(wstate->index,
-														  BTREE_DEFAULT_FILLFACTOR);
+		state->btps_full = (BLCKSZ * (100 - BTGetFillFactor(wstate->index))
+							 / 100);
+
 	/* no parent level, yet */
 	state->btps_next = NULL;
 
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 205457e..11a1de3 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -2053,7 +2053,31 @@ BTreeShmemInit(void)
 bytea *
 btoptions(Datum reloptions, bool validate)
 {
-	return default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
+	relopt_value *options;
+	BTRelOptions *rdopts;
+	int			numoptions;
+	static const relopt_parse_elt tab[] = {
+		{"fillfactor", RELOPT_TYPE_INT, offsetof(BTRelOptions, fillfactor)},
+		{"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
+		offsetof(BTRelOptions, vacuum_cleanup_index_scale_factor)}
+
+	};
+
+	options = parseRelOptions(reloptions, validate, RELOPT_KIND_BTREE,
+							  &numoptions);
+
+	/* if none set, we're done */
+	if (numoptions == 0)
+		return NULL;
+
+	rdopts = allocateReloptStruct(sizeof(BTRelOptions), options, numoptions);
+
+	fillRelOptions((void *) rdopts, sizeof(BTRelOptions), options, numoptions,
+				   validate, tab, lengthof(tab));
+
+	pfree(options);
+
+	return (bytea *) rdopts;
 }
 
 /*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 9919e6f..44bdc5b 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -411,8 +411,7 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
 	 * related to the ones already on it.  But fillfactor mustn't cause an
 	 * error for requests that would otherwise be legal.
 	 */
-	needSpace += RelationGetTargetPageFreeSpace(index,
-												SPGIST_DEFAULT_FILLFACTOR);
+	needSpace += (BLCKSZ * (100 - SpGistGetFillFactor(index)) / 100);
 	needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
 
 	/* Get the cache entry for this flags setting */
@@ -589,7 +588,29 @@ SpGistInitMetapage(Page page)
 bytea *
 spgoptions(Datum reloptions, bool validate)
 {
-	return default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
+	relopt_value       *options;
+	SpGistRelOptions   *rdopts;
+	int					numoptions;
+	static const relopt_parse_elt tab[] = {
+		{"fillfactor", RELOPT_TYPE_INT, offsetof(SpGistRelOptions, fillfactor)},
+	};
+
+	options = parseRelOptions(reloptions, validate, RELOPT_KIND_SPGIST,
+							  &numoptions);
+
+	/* if none set, we're done */
+	if (numoptions == 0)
+		return NULL;
+
+	rdopts = allocateReloptStruct(sizeof(SpGistRelOptions), options,
+									numoptions);
+
+	fillRelOptions((void *) rdopts, sizeof(SpGistRelOptions), options,
+					numoptions, validate, tab, lengthof(tab));
+
+	pfree(options);
+
+	return (bytea *) rdopts;
 }
 
 /*
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d01b258..d54f9e4 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -128,7 +128,7 @@ create_ctas_internal(List *attrList, IntoClause *into)
 										validnsps,
 										true, false);
 
-	(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
+	(void) relation_reloptions(RELKIND_TOASTVALUE, toast_options, true);
 
 	NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c8c50e8..32c00ff 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -685,7 +685,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	if (relkind == RELKIND_VIEW)
 		(void) view_reloptions(reloptions, true);
 	else
-		(void) heap_reloptions(relkind, reloptions, true);
+		(void) relation_reloptions(relkind, reloptions, true);
 
 	if (stmt->ofTypename)
 	{
@@ -4406,7 +4406,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
 						 errmsg("cannot rewrite system relation \"%s\"",
 								RelationGetRelationName(OldHeap))));
 
-			if (RelationIsUsedAsCatalogTable(OldHeap))
+			if (HeapIsUsedAsCatalogTable(OldHeap))
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("cannot rewrite table \"%s\" used as a catalog table",
@@ -10674,7 +10674,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 		case RELKIND_TOASTVALUE:
 		case RELKIND_MATVIEW:
 		case RELKIND_PARTITIONED_TABLE:
-			(void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
+			(void) relation_reloptions(rel->rd_rel->relkind, newOptions, true);
 			break;
 		case RELKIND_VIEW:
 			(void) view_reloptions(newOptions, true);
@@ -10783,7 +10783,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 										 defList, "toast", validnsps, false,
 										 operation == AT_ResetRelOptions);
 
-		(void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
+		(void) relation_reloptions(RELKIND_TOASTVALUE, newOptions, true);
 
 		memset(repl_val, 0, sizeof(repl_val));
 		memset(repl_null, false, sizeof(repl_null));
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 5790b62..c8c0f79 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -146,7 +146,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 						  &rel->pages, &rel->tuples, &rel->allvisfrac);
 
 	/* Retrieve the parallel_workers reloption, or -1 if not set. */
-	rel->rel_parallel_workers = RelationGetParallelWorkers(relation, -1);
+	rel->rel_parallel_workers = HeapGetParallelWorkers(relation, -1);
 
 	/*
 	 * Make list of indexes.  Ignore indexes on system catalogs if told to.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 2099724..65ac3d6 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3145,7 +3145,7 @@ transformOnConflictArbiter(ParseState *pstate,
 									exprLocation((Node *) onConflictClause))));
 
 	/* Same applies to table used by logical decoding as catalog table */
-	if (RelationIsUsedAsCatalogTable(pstate->p_target_relation))
+	if (HeapIsUsedAsCatalogTable(pstate->p_target_relation))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("ON CONFLICT is not supported on table \"%s\" used as a catalog table",
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 2d5086d..b605edd 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2720,6 +2720,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
 {
 	bytea	   *relopts;
 	AutoVacOpts *av;
+	AutoVacOpts *src;
 
 	Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION ||
 		   ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
@@ -2729,8 +2730,13 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
 	if (relopts == NULL)
 		return NULL;
 
+	if (((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE)
+		src = &(((ToastRelOptions *) relopts)->autovacuum);
+	else
+		src = &(((HeapRelOptions *) relopts)->autovacuum);
+
 	av = palloc(sizeof(AutoVacOpts));
-	memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVacOpts));
+	memcpy(av, src, sizeof(AutoVacOpts));
 	pfree(relopts);
 
 	return av;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 43815d2..28dd02e 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1591,7 +1591,7 @@ ApplyRetrieveRule(Query *parsetree,
 
 	rte->rtekind = RTE_SUBQUERY;
 	rte->subquery = rule_action;
-	rte->security_barrier = RelationIsSecurityView(relation);
+	rte->security_barrier = ViewIsSecurityView(relation);
 	/* Clear fields that should not be set in a subquery RTE */
 	rte->relid = InvalidOid;
 	rte->relkind = 0;
@@ -3178,7 +3178,7 @@ rewriteTargetView(Query *parsetree, Relation view)
 
 		ChangeVarNodes(viewqual, base_rt_index, new_rt_index, 0);
 
-		if (RelationIsSecurityView(view))
+		if (ViewIsSecurityView(view))
 		{
 			/*
 			 * The view's quals go in front of existing barrier quals: those
@@ -3214,8 +3214,8 @@ rewriteTargetView(Query *parsetree, Relation view)
 	 */
 	if (parsetree->commandType != CMD_DELETE)
 	{
-		bool		has_wco = RelationHasCheckOption(view);
-		bool		cascaded = RelationHasCascadedCheckOption(view);
+		bool		has_wco = ViewHasCheckOption(view);
+		bool		cascaded = ViewHasCascadedCheckOption(view);
 
 		/*
 		 * If the parent view has a cascaded check option, treat this view as
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 970c94e..1b28a6a 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1024,7 +1024,7 @@ ProcessUtilitySlow(ParseState *pstate,
 																validnsps,
 																true,
 																false);
-							(void) heap_reloptions(RELKIND_TOASTVALUE,
+							(void) relation_reloptions(RELKIND_TOASTVALUE,
 												   toast_options,
 												   true);
 
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 02ef67c..e6b9208 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -274,6 +274,17 @@ typedef struct HashMetaPageData
 
 typedef HashMetaPageData *HashMetaPage;
 
+typedef struct HashRelOptions
+{
+	int32		varlena_header_;  /* varlena header (do not touch directly!) */
+	int			fillfactor;		  /* page fill factor in percent (0..100) */
+}	HashRelOptions;
+
+#define HashGetFillFactor(relation) \
+	((relation)->rd_options ? \
+		((HashRelOptions *) (relation)->rd_options)->fillfactor : \
+		HASH_DEFAULT_FILLFACTOR)
+
 /*
  * Maximum size of a hash index item (it's okay to have only one per page)
  */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index ea495f1..d7066d0 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -488,6 +488,19 @@ typedef BTScanOpaqueData *BTScanOpaque;
 #define SK_BT_DESC			(INDOPTION_DESC << SK_BT_INDOPTION_SHIFT)
 #define SK_BT_NULLS_FIRST	(INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_SHIFT)
 
+typedef struct BTRelOptions
+{
+	int32	varlena_header_;	/* varlena header (do not touch directly!) */
+	int		fillfactor;			/* page fill factor in percent (0..100) */
+	/* fraction of newly inserted tuples prior to trigger index cleanup */
+	float8		vacuum_cleanup_index_scale_factor;
+}	BTRelOptions;
+
+#define BTGetFillFactor(relation) \
+	((relation)->rd_options ? \
+		((BTRelOptions *) (relation)->rd_options)->fillfactor : \
+		BTREE_DEFAULT_FILLFACTOR)
+
 /*
  * external entry points for btree, in nbtree.c
  */
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 50690b9..6d309b3 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -271,9 +271,11 @@ extern void fillRelOptions(void *rdopts, Size basesize,
 			   bool validate,
 			   const relopt_parse_elt *elems, int nelems);
 
-extern bytea *default_reloptions(Datum reloptions, bool validate,
-				   relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
+extern bytea *toast_reloptions(Datum reloptions, bool validate);
+extern bytea *partitioned_reloptions(Datum reloptions, bool validate);
+extern bytea *heap_reloptions(Datum reloptions, bool validate);
+extern bytea *relation_reloptions(char relkind, Datum reloptions,
+								  bool validate);
 extern bytea *view_reloptions(Datum reloptions, bool validate);
 extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
 				 bool validate);
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 9c19e9e..4c57a0a 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -20,10 +20,6 @@
 #include "lib/stringinfo.h"
 
 
-/* reloption parameters */
-#define SPGIST_MIN_FILLFACTOR			10
-#define SPGIST_DEFAULT_FILLFACTOR		80
-
 /* SPGiST opclass support function numbers */
 #define SPGIST_CONFIG_PROC				1
 #define SPGIST_CHOOSE_PROC				2
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index d23862e..a567603 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -22,6 +22,17 @@
 #include "utils/relcache.h"
 
 
+typedef struct SpGistRelOptions
+{
+	int32		varlena_header_;  /* varlena header (do not touch directly!) */
+	int			fillfactor;		  /* page fill factor in percent (0..100) */
+}	SpGistRelOptions;
+
+#define SpGistGetFillFactor(relation) \
+	((relation)->rd_options ? \
+		((SpGistRelOptions *) (relation)->rd_options)->fillfactor : \
+		SPGIST_DEFAULT_FILLFACTOR)
+
 /* Page numbers of fixed-location pages */
 #define SPGIST_METAPAGE_BLKNO	 (0)	/* metapage */
 #define SPGIST_ROOT_BLKNO		 (1)	/* root for normal entries */
@@ -417,6 +428,11 @@ typedef SpGistDeadTupleData *SpGistDeadTuple;
 #define GBUF_REQ_NULLS(flags)	((flags) & GBUF_NULLS)
 
 /* spgutils.c */
+
+/* reloption parameters */
+#define SPGIST_MIN_FILLFACTOR			10
+#define SPGIST_DEFAULT_FILLFACTOR		80
+
 extern SpGistCache *spgGetCache(Relation index);
 extern void initSpGistState(SpGistState *state, Relation index);
 extern Buffer SpGistNewBuffer(Relation index);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 2217081..3af3027 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -253,71 +253,93 @@ typedef struct AutoVacOpts
 	float8		analyze_scale_factor;
 } AutoVacOpts;
 
-typedef struct StdRdOptions
+typedef struct HeapRelOptions
 {
 	int32		vl_len_;		/* varlena header (do not touch directly!) */
 	int			fillfactor;		/* page fill factor in percent (0..100) */
-	/* fraction of newly inserted tuples prior to trigger index cleanup */
-	float8		vacuum_cleanup_index_scale_factor;
 	int			toast_tuple_target; /* target for tuple toasting */
 	AutoVacOpts autovacuum;		/* autovacuum-related options */
 	bool		user_catalog_table; /* use as an additional catalog relation */
 	int			parallel_workers;	/* max number of parallel workers */
-} StdRdOptions;
+} HeapRelOptions;
+
+typedef struct ToastRelOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	AutoVacOpts autovacuum;		/* autovacuum-related options */
+} ToastRelOptions;
+
+typedef struct PartitionedRelOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	/* No options defined yet */
+} PartitionedRelOptions;
 
 #define HEAP_MIN_FILLFACTOR			10
 #define HEAP_DEFAULT_FILLFACTOR		100
 
+#define TOAST_DEFAULT_FILLFACTOR	100 /* Only default is actually used */
+
 /*
- * RelationGetToastTupleTarget
- *		Returns the relation's toast_tuple_target.  Note multiple eval of argument!
+ * HeapGetToastTupleTarget
+ *		Returns the heap's toast_tuple_target.  Note multiple eval of argument!
  */
-#define RelationGetToastTupleTarget(relation, defaulttarg) \
+#define HeapGetToastTupleTarget(relation, defaulttarg) \
 	((relation)->rd_options ? \
-	 ((StdRdOptions *) (relation)->rd_options)->toast_tuple_target : (defaulttarg))
+	 ((HeapRelOptions *) (relation)->rd_options)->toast_tuple_target : \
+		(defaulttarg))
 
 /*
- * RelationGetFillFactor
- *		Returns the relation's fillfactor.  Note multiple eval of argument!
+ * HeapGetFillFactor
+ *		Returns the heap's fillfactor.  Note multiple eval of argument!
  */
-#define RelationGetFillFactor(relation, defaultff) \
+#define HeapGetFillFactor(relation) \
 	((relation)->rd_options ? \
-	 ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))
+	 ((HeapRelOptions *) (relation)->rd_options)->fillfactor : \
+									(HEAP_DEFAULT_FILLFACTOR))
 
 /*
- * RelationGetTargetPageUsage
- *		Returns the relation's desired space usage per page in bytes.
+ * HeapGetTargetPageUsage
+ *		Returns the heap's desired space usage per page in bytes.
  */
-#define RelationGetTargetPageUsage(relation, defaultff) \
-	(BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
+#define HeapGetTargetPageUsage(relation) \
+	(BLCKSZ * HeapGetFillFactor(relation) / 100)
 
 /*
- * RelationGetTargetPageFreeSpace
- *		Returns the relation's desired freespace per page in bytes.
+ * HeapGetTargetPageFreeSpace
+ *		Returns the heap's desired freespace per page in bytes.
  */
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \
-	(BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+#define HeapGetTargetPageFreeSpace(relation) \
+	(BLCKSZ * (100 - HeapGetFillFactor(relation)) / 100)
 
 /*
- * RelationIsUsedAsCatalogTable
- *		Returns whether the relation should be treated as a catalog table
+ * HeapIsUsedAsCatalogTable
+ *		Returns whether the heap should be treated as a catalog table
  *		from the pov of logical decoding.  Note multiple eval of argument!
  */
-#define RelationIsUsedAsCatalogTable(relation)	\
+#define HeapIsUsedAsCatalogTable(relation)	\
 	((relation)->rd_options && \
 	 ((relation)->rd_rel->relkind == RELKIND_RELATION || \
 	  (relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
-	 ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)
+	 ((HeapRelOptions *) (relation)->rd_options)->user_catalog_table : false)
 
 /*
- * RelationGetParallelWorkers
- *		Returns the relation's parallel_workers reloption setting.
+ * HeapGetParallelWorkers
+ *		Returns the heap's parallel_workers reloption setting.
  *		Note multiple eval of argument!
  */
-#define RelationGetParallelWorkers(relation, defaultpw) \
+#define HeapGetParallelWorkers(relation, defaultpw) \
 	((relation)->rd_options ? \
-	 ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+		((HeapRelOptions *) (relation)->rd_options)->parallel_workers : \
+			(defaultpw))
 
+/*
+ * ToastGetTargetPageFreeSpace
+ *		Returns the TOAST relation's desired freespace per page in bytes.
+ *		Always calculated using default fillfactor value.
+ */
+#define ToastGetTargetPageFreeSpace() \
+	(BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100)
 
 /*
  * ViewOptions
@@ -331,29 +353,29 @@ typedef struct ViewOptions
 } ViewOptions;
 
 /*
- * RelationIsSecurityView
- *		Returns whether the relation is security view, or not.  Note multiple
+ * ViewIsSecurityView
+ *		Returns whether the view is security view, or not.  Note multiple
  *		eval of argument!
  */
-#define RelationIsSecurityView(relation)	\
+#define ViewIsSecurityView(relation)		\
 	((relation)->rd_options ?				\
 	 ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
 
 /*
- * RelationHasCheckOption
- *		Returns true if the relation is a view defined with either the local
+ * ViewHasCheckOption
+ *		Returns true if the view is defined with either the local
  *		or the cascaded check option.  Note multiple eval of argument!
  */
-#define RelationHasCheckOption(relation)									\
+#define ViewHasCheckOption(relation)										\
 	((relation)->rd_options &&												\
 	 ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0)
 
 /*
- * RelationHasLocalCheckOption
- *		Returns true if the relation is a view defined with the local check
+ * ViewHasLocalCheckOption
+ *		Returns true if the view is defined with the local check
  *		option.  Note multiple eval of argument!
  */
-#define RelationHasLocalCheckOption(relation)								\
+#define ViewHasLocalCheckOption(relation)									\
 	((relation)->rd_options &&												\
 	 ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ?	\
 	 strcmp((char *) (relation)->rd_options +								\
@@ -361,11 +383,11 @@ typedef struct ViewOptions
 			"local") == 0 : false)
 
 /*
- * RelationHasCascadedCheckOption
- *		Returns true if the relation is a view defined with the cascaded check
+ * ViewHasCascadedCheckOption
+ *		Returns true if the view is defined with the cascaded check
  *		option.  Note multiple eval of argument!
  */
-#define RelationHasCascadedCheckOption(relation)							\
+#define ViewHasCascadedCheckOption(relation)								\
 	((relation)->rd_options &&												\
 	 ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ?	\
 	 strcmp((char *) (relation)->rd_options +								\
@@ -564,7 +586,7 @@ typedef struct ViewOptions
 #define RelationIsAccessibleInLogicalDecoding(relation) \
 	(XLogLogicalInfoActive() && \
 	 RelationNeedsWAL(relation) && \
-	 (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relation)))
+	 (IsCatalogRelation(relation) || HeapIsUsedAsCatalogTable(relation)))
 
 /*
  * RelationIsLogicallyLogged

Reply via email to