Skip to content

Commit c95c25f

Browse files
committed
Custom reloptions for table AM
Let table AM define custom reloptions for its tables. This allows to specify AM-specific parameters by WITH clause when creating a table. The code may use some parts from prior work by Hao Wu. Discussion: https://postgr.es/m/CAPpHfdurb9ycV8udYqM%3Do0sPS66PJ4RCBM1g-bBpvzUfogY0EA%40mail.gmail.com Discussion: https://postgr.es/m/AMUA1wBBBxfc3tKRLLdU64rb.1.1683276279979.Hmail.wuhao%40hashdata.cn Reviewed-by: Reviewed-by: Pavel Borisov, Matthias van de Meent
1 parent 27bc177 commit c95c25f

File tree

8 files changed

+126
-27
lines changed

8 files changed

+126
-27
lines changed

src/backend/access/common/reloptions.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "access/nbtree.h"
2525
#include "access/reloptions.h"
2626
#include "access/spgist_private.h"
27+
#include "access/tableam.h"
2728
#include "catalog/pg_type.h"
2829
#include "commands/defrem.h"
2930
#include "commands/tablespace.h"
@@ -1377,7 +1378,7 @@ untransformRelOptions(Datum options)
13771378
*/
13781379
bytea *
13791380
extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
1380-
amoptions_function amoptions)
1381+
const TableAmRoutine *tableam, amoptions_function amoptions)
13811382
{
13821383
bytea *options;
13831384
bool isnull;
@@ -1399,7 +1400,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
13991400
case RELKIND_RELATION:
14001401
case RELKIND_TOASTVALUE:
14011402
case RELKIND_MATVIEW:
1402-
options = heap_reloptions(classForm->relkind, datum, false);
1403+
options = tableam_reloptions(tableam, classForm->relkind,
1404+
datum, false);
14031405
break;
14041406
case RELKIND_PARTITIONED_TABLE:
14051407
options = partitioned_table_reloptions(datum, false);

src/backend/access/heap/heapam_handler.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "access/heapam.h"
2424
#include "access/heaptoast.h"
2525
#include "access/multixact.h"
26+
#include "access/reloptions.h"
2627
#include "access/rewriteheap.h"
2728
#include "access/syncscan.h"
2829
#include "access/tableam.h"
@@ -2155,6 +2156,16 @@ heapam_relation_toast_am(Relation rel)
21552156
return rel->rd_rel->relam;
21562157
}
21572158

2159+
static bytea *
2160+
heapam_reloptions(char relkind, Datum reloptions, bool validate)
2161+
{
2162+
Assert(relkind == RELKIND_RELATION ||
2163+
relkind == RELKIND_TOASTVALUE ||
2164+
relkind == RELKIND_MATVIEW);
2165+
2166+
return heap_reloptions(relkind, reloptions, validate);
2167+
}
2168+
21582169

21592170
/* ------------------------------------------------------------------------
21602171
* Planner related callbacks for the heap AM
@@ -2660,6 +2671,7 @@ static const TableAmRoutine heapam_methods = {
26602671
.relation_needs_toast_table = heapam_relation_needs_toast_table,
26612672
.relation_toast_am = heapam_relation_toast_am,
26622673
.relation_fetch_toast_slice = heap_fetch_toast_slice,
2674+
.reloptions = heapam_reloptions,
26632675

26642676
.relation_estimate_size = heapam_estimate_rel_size,
26652677

src/backend/access/table/tableamapi.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313

1414
#include "access/tableam.h"
1515
#include "access/xact.h"
16+
#include "catalog/pg_am.h"
1617
#include "commands/defrem.h"
1718
#include "miscadmin.h"
1819
#include "utils/guc_hooks.h"
20+
#include "utils/syscache.h"
1921

2022

2123
/*
@@ -98,6 +100,29 @@ GetTableAmRoutine(Oid amhandler)
98100
return routine;
99101
}
100102

103+
/*
104+
* GetTableAmRoutineByAmOid
105+
* Given the table access method oid get its TableAmRoutine struct, which
106+
* will be palloc'd in the caller's memory context.
107+
*/
108+
const TableAmRoutine *
109+
GetTableAmRoutineByAmOid(Oid amoid)
110+
{
111+
HeapTuple ht_am;
112+
Form_pg_am amrec;
113+
const TableAmRoutine *tableam = NULL;
114+
115+
ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
116+
if (!HeapTupleIsValid(ht_am))
117+
elog(ERROR, "cache lookup failed for access method %u",
118+
amoid);
119+
amrec = (Form_pg_am) GETSTRUCT(ht_am);
120+
121+
tableam = GetTableAmRoutine(amrec->amhandler);
122+
ReleaseSysCache(ht_am);
123+
return tableam;
124+
}
125+
101126
/* check_hook: validate new default_table_access_method */
102127
bool
103128
check_default_table_access_method(char **newval, void **extra, GucSource source)

src/backend/commands/tablecmds.c

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
715715
ObjectAddress address;
716716
LOCKMODE parentLockmode;
717717
Oid accessMethodId = InvalidOid;
718+
const TableAmRoutine *tableam = NULL;
718719

719720
/*
720721
* Truncate relname to appropriate length (probably a waste of time, as
@@ -850,6 +851,28 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
850851
if (!OidIsValid(ownerId))
851852
ownerId = GetUserId();
852853

854+
/*
855+
* For relations with table AM and partitioned tables, select access
856+
* method to use: an explicitly indicated one, or (in the case of a
857+
* partitioned table) the parent's, if it has one.
858+
*/
859+
if (stmt->accessMethod != NULL)
860+
{
861+
Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
862+
accessMethodId = get_table_am_oid(stmt->accessMethod, false);
863+
}
864+
else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
865+
{
866+
if (stmt->partbound)
867+
{
868+
Assert(list_length(inheritOids) == 1);
869+
accessMethodId = get_rel_relam(linitial_oid(inheritOids));
870+
}
871+
872+
if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
873+
accessMethodId = get_table_am_oid(default_table_access_method, false);
874+
}
875+
853876
/*
854877
* Parse and validate reloptions, if any.
855878
*/
@@ -858,6 +881,12 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
858881

859882
switch (relkind)
860883
{
884+
case RELKIND_RELATION:
885+
case RELKIND_TOASTVALUE:
886+
case RELKIND_MATVIEW:
887+
tableam = GetTableAmRoutineByAmOid(accessMethodId);
888+
(void) tableam_reloptions(tableam, relkind, reloptions, true);
889+
break;
861890
case RELKIND_VIEW:
862891
(void) view_reloptions(reloptions, true);
863892
break;
@@ -866,6 +895,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
866895
break;
867896
default:
868897
(void) heap_reloptions(relkind, reloptions, true);
898+
break;
869899
}
870900

871901
if (stmt->ofTypename)
@@ -957,28 +987,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
957987
}
958988
}
959989

960-
/*
961-
* For relations with table AM and partitioned tables, select access
962-
* method to use: an explicitly indicated one, or (in the case of a
963-
* partitioned table) the parent's, if it has one.
964-
*/
965-
if (stmt->accessMethod != NULL)
966-
{
967-
Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
968-
accessMethodId = get_table_am_oid(stmt->accessMethod, false);
969-
}
970-
else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
971-
{
972-
if (stmt->partbound)
973-
{
974-
Assert(list_length(inheritOids) == 1);
975-
accessMethodId = get_rel_relam(linitial_oid(inheritOids));
976-
}
977-
978-
if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
979-
accessMethodId = get_table_am_oid(default_table_access_method, false);
980-
}
981-
982990
/*
983991
* Create the relation. Inherited defaults and constraints are passed in
984992
* for immediate handling --- since they don't need parsing, they can be
@@ -15524,7 +15532,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
1552415532
case RELKIND_RELATION:
1552515533
case RELKIND_TOASTVALUE:
1552615534
case RELKIND_MATVIEW:
15527-
(void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15535+
(void) table_reloptions(rel, rel->rd_rel->relkind,
15536+
newOptions, true);
1552815537
break;
1552915538
case RELKIND_PARTITIONED_TABLE:
1553015539
(void) partitioned_table_reloptions(newOptions, true);

src/backend/postmaster/autovacuum.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2661,7 +2661,9 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
26612661
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
26622662
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE);
26632663

2664-
relopts = extractRelOptions(tup, pg_class_desc, NULL);
2664+
relopts = extractRelOptions(tup, pg_class_desc,
2665+
GetTableAmRoutineByAmOid(((Form_pg_class) GETSTRUCT(tup))->relam),
2666+
NULL);
26652667
if (relopts == NULL)
26662668
return NULL;
26672669

src/backend/utils/cache/relcache.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "access/htup_details.h"
3434
#include "access/multixact.h"
3535
#include "access/parallel.h"
36+
#include "access/relation.h"
3637
#include "access/reloptions.h"
3738
#include "access/sysattr.h"
3839
#include "access/table.h"
@@ -464,6 +465,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
464465
{
465466
bytea *options;
466467
amoptions_function amoptsfn;
468+
const TableAmRoutine *tableam = NULL;
467469

468470
relation->rd_options = NULL;
469471

@@ -478,6 +480,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
478480
case RELKIND_VIEW:
479481
case RELKIND_MATVIEW:
480482
case RELKIND_PARTITIONED_TABLE:
483+
tableam = relation->rd_tableam;
481484
amoptsfn = NULL;
482485
break;
483486
case RELKIND_INDEX:
@@ -493,7 +496,8 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
493496
* we might not have any other for pg_class yet (consider executing this
494497
* code for pg_class itself)
495498
*/
496-
options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
499+
options = extractRelOptions(tuple, GetPgClassDescriptor(),
500+
tableam, amoptsfn);
497501

498502
/*
499503
* Copy parsed data into CacheMemoryContext. To guard against the

src/include/access/reloptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "access/amapi.h"
2323
#include "access/htup.h"
24+
#include "access/tableam.h"
2425
#include "access/tupdesc.h"
2526
#include "nodes/pg_list.h"
2627
#include "storage/lock.h"
@@ -224,6 +225,7 @@ extern Datum transformRelOptions(Datum oldOptions, List *defList,
224225
bool acceptOidsOff, bool isReset);
225226
extern List *untransformRelOptions(Datum options);
226227
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
228+
const TableAmRoutine *tableam,
227229
amoptions_function amoptions);
228230
extern void *build_reloptions(Datum reloptions, bool validate,
229231
relopt_kind kind,

src/include/access/tableam.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,28 @@ typedef struct TableAmRoutine
737737
int32 slicelength,
738738
struct varlena *result);
739739

740+
/*
741+
* This callback parses and validates the reloptions array for a table.
742+
*
743+
* This is called only when a non-null reloptions array exists for the
744+
* table. 'reloptions' is a text array containing entries of the form
745+
* "name=value". The function should construct a bytea value, which will
746+
* be copied into the rd_options field of the table's relcache entry. The
747+
* data contents of the bytea value are open for the access method to
748+
* define.
749+
*
750+
* When 'validate' is true, the function should report a suitable error
751+
* message if any of the options are unrecognized or have invalid values;
752+
* when 'validate' is false, invalid entries should be silently ignored.
753+
* ('validate' is false when loading options already stored in pg_catalog;
754+
* an invalid entry could only be found if the access method has changed
755+
* its rules for options, and in that case ignoring obsolete entries is
756+
* appropriate.)
757+
*
758+
* It is OK to return NULL if default behavior is wanted.
759+
*/
760+
bytea *(*reloptions) (char relkind, Datum reloptions, bool validate);
761+
740762

741763
/* ------------------------------------------------------------------------
742764
* Planner related functions.
@@ -1925,6 +1947,26 @@ table_relation_fetch_toast_slice(Relation toastrel, Oid valueid,
19251947
result);
19261948
}
19271949

1950+
/*
1951+
* Parse options for given table.
1952+
*/
1953+
static inline bytea *
1954+
table_reloptions(Relation rel, char relkind,
1955+
Datum reloptions, bool validate)
1956+
{
1957+
return rel->rd_tableam->reloptions(relkind, reloptions, validate);
1958+
}
1959+
1960+
/*
1961+
* Parse table options without knowledge of particular table.
1962+
*/
1963+
static inline bytea *
1964+
tableam_reloptions(const TableAmRoutine *tableam, char relkind,
1965+
Datum reloptions, bool validate)
1966+
{
1967+
return tableam->reloptions(relkind, reloptions, validate);
1968+
}
1969+
19281970

19291971
/* ----------------------------------------------------------------------------
19301972
* Planner related functionality
@@ -2102,6 +2144,7 @@ extern void table_block_relation_estimate_size(Relation rel,
21022144
*/
21032145

21042146
extern const TableAmRoutine *GetTableAmRoutine(Oid amhandler);
2147+
extern const TableAmRoutine *GetTableAmRoutineByAmOid(Oid amoid);
21052148

21062149
/* ----------------------------------------------------------------------------
21072150
* Functions in heapam_handler.c

0 commit comments

Comments
 (0)