This is part or my bigger patch https://www.postgresql.org/message-id/flat/2146419.veIEZdk4E4@x200m#2146419.veIEZdk4E4@x200m we've decided to commit by smaller parts.
Now in postgres an StdRdOptions structure is used as binary represenations of reloptions for heap, toast, and some indexes. It has a number of fields, and only heap relation uses them all. Toast relations uses only autovacuum options, and indexes uses only fillfactor option. So for example if you set custom fillfactor value for some index, then it will lead to allocating 84 bytes of memory (sizeof StdRdOptions on i386) and only 8 bytes will be actually used (varlena header and fillfactor value). 74 bytes is not much, but allocating them for each index for no particular reason is bad idea, as I think. Moreover when I wrote my big reloption refactoring patch, I came to "one reloption kind - one binary representation" philosophy. It allows to write code with more logic in it. This patch replaces StdRdOptions with HeapRelOptions, ToastRelOptions, BTRelOptions, HashRelOptions, SpGistRelOptions and PartitionedRelOptions one for each relation kind that were using StdRdOptions before. The second thing I've done, I've renamed Relation* macroses from src/include/utils/rel.h, that were working with reloptions. I've renamed them into Heap*, Toast* and View* (depend on what relation options they were actually using) I did it because there names were misleading. For example RelationHasCheckOption can be called only for View relation, and will give wrong result for other relation types. It just takes binary representation of reloptions, cast is to (ViewOptions *) and then returns some value from it. Naming it as ViewHasCheckOption would better reflect what it actually do, and strictly specify that it is applicable only to View relations. Possible flaws: I replaced saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel, HEAP_DEFAULT_FILLFACTOR); with if (IsToastRelation(state->rs_new_rel)) saveFreeSpace = ToastGetTargetPageFreeSpace(); else saveFreeSpace = HeapGetTargetPageFreeSpace(state->rs_new_rel); wherever I met it (and other cases like that), but I am not sure if in some cases that part of code is used for heap only or not. So may be this "ifs" is not needed and should be removed, and only Heap-case should be left. But I am not that much familiar with postgres internals to see it for sure... I need advice of more experienced developers here. -- Do code for fun.
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 46276ce..bf7441e 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 * @@ -986,7 +985,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); @@ -1319,61 +1318,133 @@ 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)} + 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); + + return (bytea *) rdopts; +} + +/* + * 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) +{ + relopt_value *options; + PartitionedRelOptions *rdopts; + int numoptions; + static const relopt_parse_elt tab[] = { + /* No options defined yet */ + }; + + options = parseRelOptions(reloptions, validate, RELOPT_KIND_PARTITIONED, + &numoptions); + + /* if none set, we're done */ + if (numoptions == 0) + return NULL; + + rdopts = allocateReloptStruct(sizeof(PartitionedRelOptions), options, numoptions); + + fillRelOptions((void *) rdopts, sizeof(PartitionedRelOptions), options, numoptions, validate, tab, lengthof(tab)); pfree(options); @@ -1414,39 +1485,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 e3c8721..aeb1ead 100644 --- a/src/backend/access/hash/hashpage.c +++ b/src/backend/access/hash/hashpage.c @@ -368,7 +368,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 4e485cc..63b1e80 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 8a846e7..66707fb 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, bool need_cids = RelationIsAccessibleInLogicalDecoding(relation); 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 0d7bc68..c2e1e0b 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" @@ -323,8 +324,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 f67d7d1..224c9de 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 7d466c2..085111b 100644 --- a/src/backend/access/heap/rewriteheap.c +++ b/src/backend/access/heap/rewriteheap.c @@ -670,8 +670,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 546f80f..f0aa862 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -727,7 +727,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, hoff += sizeof(Oid); 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 51059c0..ebb37b4 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -1427,8 +1427,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/nbtsort.c b/src/backend/access/nbtree/nbtsort.c index 521ae6e..9f8cc08 100644 --- a/src/backend/access/nbtree/nbtsort.c +++ b/src/backend/access/nbtree/nbtsort.c @@ -689,8 +689,8 @@ _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 752667c..b2d6176 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -2041,7 +2041,28 @@ 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)}, + }; + + 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 c4278b0..984d4af 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -401,8 +401,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 */ @@ -579,7 +578,28 @@ 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 3d82edb..1dd8190 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 74e020b..b26f844 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -624,7 +624,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) { @@ -4277,7 +4277,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", @@ -10536,7 +10536,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); @@ -10645,7 +10645,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 60f2171..a911924 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -145,7 +145,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 3a02307..7fe7f4a 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -3173,7 +3173,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 702f8d8..2309f98 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -2703,6 +2703,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 || @@ -2712,8 +2713,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 66253fc..bb038ee 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1571,7 +1571,7 @@ ApplyRetrieveRule(Query *parsetree, rte->rtekind = RTE_SUBQUERY; rte->relid = InvalidOid; - rte->security_barrier = RelationIsSecurityView(relation); + rte->security_barrier = ViewIsSecurityView(relation); rte->subquery = rule_action; rte->inh = false; /* must not be set for a subquery */ @@ -3055,7 +3055,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 @@ -3091,8 +3091,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 8c23ee5..f436227 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1036,7 +1036,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 65d23f3..1149dd9 100644 --- a/src/include/access/hash.h +++ b/src/include/access/hash.h @@ -271,6 +271,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 2b0b1da..7433592 100644 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -434,6 +434,17 @@ 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) */ +} 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 b32c1e9..2ea3bd5 100644 --- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@ -270,9 +270,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 c6d7e22..8bdcc9b 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 6d5f1c6..9faf09d 100644 --- a/src/include/access/spgist_private.h +++ b/src/include/access/spgist_private.h @@ -21,6 +21,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 */ @@ -382,6 +393,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 aa8add5..0e81478 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -273,7 +273,7 @@ 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) */ @@ -281,61 +281,83 @@ typedef struct StdRdOptions 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 @@ -349,29 +371,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 + \ @@ -379,11 +401,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 + \ @@ -568,7 +590,7 @@ typedef struct ViewOptions #define RelationIsAccessibleInLogicalDecoding(relation) \ (XLogLogicalInfoActive() && \ RelationNeedsWAL(relation) && \ - (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relation))) + (IsCatalogRelation(relation) || HeapIsUsedAsCatalogTable(relation))) /* * RelationIsLogicallyLogged
signature.asc
Description: This is a digitally signed message part.