]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Custom reloptions for table AM
authorAlexander Korotkov <akorotkov@postgresql.org>
Sat, 30 Mar 2024 20:36:25 +0000 (22:36 +0200)
committerAlexander Korotkov <akorotkov@postgresql.org>
Sat, 30 Mar 2024 20:36:25 +0000 (22:36 +0200)
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
src/backend/access/common/reloptions.c
src/backend/access/heap/heapam_handler.c
src/backend/access/table/tableamapi.c
src/backend/commands/tablecmds.c
src/backend/postmaster/autovacuum.c
src/backend/utils/cache/relcache.c
src/include/access/reloptions.h
src/include/access/tableam.h

index d6eb5d8559939fef263e9cab96c9a98d13cbc45f..963995388bb65a3046e501c70ed9af6b1eccfe3f 100644 (file)
@@ -24,6 +24,7 @@
 #include "access/nbtree.h"
 #include "access/reloptions.h"
 #include "access/spgist_private.h"
+#include "access/tableam.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/tablespace.h"
@@ -1377,7 +1378,7 @@ untransformRelOptions(Datum options)
  */
 bytea *
 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
-                                 amoptions_function amoptions)
+                                 const TableAmRoutine *tableam, amoptions_function amoptions)
 {
        bytea      *options;
        bool            isnull;
@@ -1399,7 +1400,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
                case RELKIND_RELATION:
                case RELKIND_TOASTVALUE:
                case RELKIND_MATVIEW:
-                       options = heap_reloptions(classForm->relkind, datum, false);
+                       options = tableam_reloptions(tableam, classForm->relkind,
+                                                                                datum, false);
                        break;
                case RELKIND_PARTITIONED_TABLE:
                        options = partitioned_table_reloptions(datum, false);
index a7ef0cf72d32cdb9393d94605e4bfad70b30fdec..26b3be9779d0bfd50067481cd738eb174e679357 100644 (file)
@@ -23,6 +23,7 @@
 #include "access/heapam.h"
 #include "access/heaptoast.h"
 #include "access/multixact.h"
+#include "access/reloptions.h"
 #include "access/rewriteheap.h"
 #include "access/syncscan.h"
 #include "access/tableam.h"
@@ -2155,6 +2156,16 @@ heapam_relation_toast_am(Relation rel)
        return rel->rd_rel->relam;
 }
 
+static bytea *
+heapam_reloptions(char relkind, Datum reloptions, bool validate)
+{
+       Assert(relkind == RELKIND_RELATION ||
+                  relkind == RELKIND_TOASTVALUE ||
+                  relkind == RELKIND_MATVIEW);
+
+       return heap_reloptions(relkind, reloptions, validate);
+}
+
 
 /* ------------------------------------------------------------------------
  * Planner related callbacks for the heap AM
@@ -2660,6 +2671,7 @@ static const TableAmRoutine heapam_methods = {
        .relation_needs_toast_table = heapam_relation_needs_toast_table,
        .relation_toast_am = heapam_relation_toast_am,
        .relation_fetch_toast_slice = heap_fetch_toast_slice,
+       .reloptions = heapam_reloptions,
 
        .relation_estimate_size = heapam_estimate_rel_size,
 
index 55b8caeadf2c8b2d3b350d9661954e1f31852299..d9e23ef31754750e0c548d91e48f0cb0a1546a5b 100644 (file)
 
 #include "access/tableam.h"
 #include "access/xact.h"
+#include "catalog/pg_am.h"
 #include "commands/defrem.h"
 #include "miscadmin.h"
 #include "utils/guc_hooks.h"
+#include "utils/syscache.h"
 
 
 /*
@@ -98,6 +100,29 @@ GetTableAmRoutine(Oid amhandler)
        return routine;
 }
 
+/*
+ * GetTableAmRoutineByAmOid
+ *             Given the table access method oid get its TableAmRoutine struct, which
+ *             will be palloc'd in the caller's memory context.
+ */
+const TableAmRoutine *
+GetTableAmRoutineByAmOid(Oid amoid)
+{
+       HeapTuple       ht_am;
+       Form_pg_am      amrec;
+       const TableAmRoutine *tableam = NULL;
+
+       ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+       if (!HeapTupleIsValid(ht_am))
+               elog(ERROR, "cache lookup failed for access method %u",
+                        amoid);
+       amrec = (Form_pg_am) GETSTRUCT(ht_am);
+
+       tableam = GetTableAmRoutine(amrec->amhandler);
+       ReleaseSysCache(ht_am);
+       return tableam;
+}
+
 /* check_hook: validate new default_table_access_method */
 bool
 check_default_table_access_method(char **newval, void **extra, GucSource source)
index 6741e721ae352a6c6a6aae49fff2e8eb484daddd..3fcb9cd078340032aec834de304f7d174061bd88 100644 (file)
@@ -715,6 +715,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
        ObjectAddress address;
        LOCKMODE        parentLockmode;
        Oid                     accessMethodId = InvalidOid;
+       const TableAmRoutine *tableam = NULL;
 
        /*
         * Truncate relname to appropriate length (probably a waste of time, as
@@ -850,6 +851,28 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
        if (!OidIsValid(ownerId))
                ownerId = GetUserId();
 
+       /*
+        * For relations with table AM and partitioned tables, select access
+        * method to use: an explicitly indicated one, or (in the case of a
+        * partitioned table) the parent's, if it has one.
+        */
+       if (stmt->accessMethod != NULL)
+       {
+               Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
+               accessMethodId = get_table_am_oid(stmt->accessMethod, false);
+       }
+       else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
+       {
+               if (stmt->partbound)
+               {
+                       Assert(list_length(inheritOids) == 1);
+                       accessMethodId = get_rel_relam(linitial_oid(inheritOids));
+               }
+
+               if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
+                       accessMethodId = get_table_am_oid(default_table_access_method, false);
+       }
+
        /*
         * Parse and validate reloptions, if any.
         */
@@ -858,6 +881,12 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 
        switch (relkind)
        {
+               case RELKIND_RELATION:
+               case RELKIND_TOASTVALUE:
+               case RELKIND_MATVIEW:
+                       tableam = GetTableAmRoutineByAmOid(accessMethodId);
+                       (void) tableam_reloptions(tableam, relkind, reloptions, true);
+                       break;
                case RELKIND_VIEW:
                        (void) view_reloptions(reloptions, true);
                        break;
@@ -866,6 +895,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
                        break;
                default:
                        (void) heap_reloptions(relkind, reloptions, true);
+                       break;
        }
 
        if (stmt->ofTypename)
@@ -957,28 +987,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
                }
        }
 
-       /*
-        * For relations with table AM and partitioned tables, select access
-        * method to use: an explicitly indicated one, or (in the case of a
-        * partitioned table) the parent's, if it has one.
-        */
-       if (stmt->accessMethod != NULL)
-       {
-               Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
-               accessMethodId = get_table_am_oid(stmt->accessMethod, false);
-       }
-       else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
-       {
-               if (stmt->partbound)
-               {
-                       Assert(list_length(inheritOids) == 1);
-                       accessMethodId = get_rel_relam(linitial_oid(inheritOids));
-               }
-
-               if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
-                       accessMethodId = get_table_am_oid(default_table_access_method, false);
-       }
-
        /*
         * Create the relation.  Inherited defaults and constraints are passed in
         * for immediate handling --- since they don't need parsing, they can be
@@ -15524,7 +15532,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
                case RELKIND_RELATION:
                case RELKIND_TOASTVALUE:
                case RELKIND_MATVIEW:
-                       (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
+                       (void) table_reloptions(rel, rel->rd_rel->relkind,
+                                                                       newOptions, true);
                        break;
                case RELKIND_PARTITIONED_TABLE:
                        (void) partitioned_table_reloptions(newOptions, true);
index 71e8a6f258476a03cfffdcb16640899c916adc7c..d1d76016ab441ac412f52f807aa593d46130920b 100644 (file)
@@ -2661,7 +2661,9 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
                   ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
                   ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE);
 
-       relopts = extractRelOptions(tup, pg_class_desc, NULL);
+       relopts = extractRelOptions(tup, pg_class_desc,
+                                                               GetTableAmRoutineByAmOid(((Form_pg_class) GETSTRUCT(tup))->relam),
+                                                               NULL);
        if (relopts == NULL)
                return NULL;
 
index 1f419c2a6dd055a6d61f4b3a73a47ab0fae04264..039c0d3eef4e2d20ed91b2a9ecf4a11681df4fe4 100644 (file)
@@ -33,6 +33,7 @@
 #include "access/htup_details.h"
 #include "access/multixact.h"
 #include "access/parallel.h"
+#include "access/relation.h"
 #include "access/reloptions.h"
 #include "access/sysattr.h"
 #include "access/table.h"
@@ -464,6 +465,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 {
        bytea      *options;
        amoptions_function amoptsfn;
+       const TableAmRoutine *tableam = NULL;
 
        relation->rd_options = NULL;
 
@@ -478,6 +480,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
                case RELKIND_VIEW:
                case RELKIND_MATVIEW:
                case RELKIND_PARTITIONED_TABLE:
+                       tableam = relation->rd_tableam;
                        amoptsfn = NULL;
                        break;
                case RELKIND_INDEX:
@@ -493,7 +496,8 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
         * we might not have any other for pg_class yet (consider executing this
         * code for pg_class itself)
         */
-       options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
+       options = extractRelOptions(tuple, GetPgClassDescriptor(),
+                                                               tableam, amoptsfn);
 
        /*
         * Copy parsed data into CacheMemoryContext.  To guard against the
index 81829b8270a79b9788a855d9242b45054559c601..8ddc75df2870a3e0036cd8e1fb30e9621611a803 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "access/amapi.h"
 #include "access/htup.h"
+#include "access/tableam.h"
 #include "access/tupdesc.h"
 #include "nodes/pg_list.h"
 #include "storage/lock.h"
@@ -224,6 +225,7 @@ extern Datum transformRelOptions(Datum oldOptions, List *defList,
                                                                 bool acceptOidsOff, bool isReset);
 extern List *untransformRelOptions(Datum options);
 extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
+                                                               const TableAmRoutine *tableam,
                                                                amoptions_function amoptions);
 extern void *build_reloptions(Datum reloptions, bool validate,
                                                          relopt_kind kind,
index 8ed4e7295ad6382fa1faa4da550dc5d5369216eb..cf68ec48ebf7152f05f6bdf3ac39942201d6440b 100644 (file)
@@ -737,6 +737,28 @@ typedef struct TableAmRoutine
                                                                                           int32 slicelength,
                                                                                           struct varlena *result);
 
+       /*
+        * This callback parses and validates the reloptions array for a table.
+        *
+        * This is called only when a non-null reloptions array exists for the
+        * table.  'reloptions' is a text array containing entries of the form
+        * "name=value".  The function should construct a bytea value, which will
+        * be copied into the rd_options field of the table's relcache entry. The
+        * data contents of the bytea value are open for the access method to
+        * define.
+        *
+        * When 'validate' is true, the function should report a suitable error
+        * message if any of the options are unrecognized or have invalid values;
+        * when 'validate' is false, invalid entries should be silently ignored.
+        * ('validate' is false when loading options already stored in pg_catalog;
+        * an invalid entry could only be found if the access method has changed
+        * its rules for options, and in that case ignoring obsolete entries is
+        * appropriate.)
+        *
+        * It is OK to return NULL if default behavior is wanted.
+        */
+       bytea      *(*reloptions) (char relkind, Datum reloptions, bool validate);
+
 
        /* ------------------------------------------------------------------------
         * Planner related functions.
@@ -1925,6 +1947,26 @@ table_relation_fetch_toast_slice(Relation toastrel, Oid valueid,
                                                                                                         result);
 }
 
+/*
+ * Parse options for given table.
+ */
+static inline bytea *
+table_reloptions(Relation rel, char relkind,
+                                Datum reloptions, bool validate)
+{
+       return rel->rd_tableam->reloptions(relkind, reloptions, validate);
+}
+
+/*
+ * Parse table options without knowledge of particular table.
+ */
+static inline bytea *
+tableam_reloptions(const TableAmRoutine *tableam, char relkind,
+                                  Datum reloptions, bool validate)
+{
+       return tableam->reloptions(relkind, reloptions, validate);
+}
+
 
 /* ----------------------------------------------------------------------------
  * Planner related functionality
@@ -2102,6 +2144,7 @@ extern void table_block_relation_estimate_size(Relation rel,
  */
 
 extern const TableAmRoutine *GetTableAmRoutine(Oid amhandler);
+extern const TableAmRoutine *GetTableAmRoutineByAmOid(Oid amoid);
 
 /* ----------------------------------------------------------------------------
  * Functions in heapam_handler.c