From 9b92ffb4895eabc174f4d46a222b044da1828fcd Mon Sep 17 00:00:00 2001 From: hanwei Date: Wed, 30 Aug 2023 12:23:11 +0800 Subject: [PATCH] Feature: add user-defined index access method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The purpose of the commit is in support of user-defined index access method. Currently, there are many restrictions for index access methods and new index access methods are incompatible with internal framework. At the same time, it's impossible for user to change default index access method. So I build some hooks that are for changing default behavior and being compatible for those strange restrictions. For express the meaning of the modification, I show a example as below. For example , I want to add 7 kinds of new index access method that based on diffrent storage engine (maybe unionstore storage) and they are correspond to internal index access methods as below: - usbtree ---btree - usgin --- gin - usgist --- gist - usspgist --- spgist - ushash --- hash - usbitmap --- bitmap The main diffrence between them is index data can separate from other data and support Compute and Storage Separation. Suppose we want to use these new functions, we can create a new extension and we can run sql (for example by `create extension unionstore;`) command and you can find new access methods : ``` regression=# select * from pg_am; oid | amname | amhandler | amtype --------+---------------+---------------------------+-------- 2 | heap | heap_tableam_handler | t 403 | btree | bthandler | i 405 | hash | hashhandler | i 783 | gist | gisthandler | i 2742 | gin | ginhandler | i 4000 | spgist | spghandler | i 3580 | brin | brinhandler | i 7024 | ao_row | ao_row_tableam_handler | t 7166 | ao_column | ao_column_tableam_handler | t 7013 | bitmap | bmhandler | i 16394 | union_store | heap_tableam_handler | t 16402 | ushash | ushashhandler | i 16403 | usbtree | usbthandler | i 16404 | usgist | usgisthandler | i 16405 | usgin | usginhandler | i 16406 | usspgist | usspghandler | i 16407 | usbrin | usbrinhandler | i 16408 | usbitmap | usbmhandler | i 22820 | heap2 | heap_tableam_handler | t 111831 | ao_row_testam | ao_row_tableam_handler | t 111847 | ao_col_testam | ao_column_tableam_handler | t 111850 | heap_testam | heap_tableam_handler | t ``` So you can use these new index access methods as internal index access method. At the same time, I add new guc variable and you can correct default index type by `set default_index_type = usbtree` or show relevant info by `show default_index_type`; In conclusion, it's more flexible and it decoupe from other part. --- src/backend/access/brin/brin.c | 4 +- src/backend/access/gin/ginfast.c | 2 +- src/backend/access/index/amapi.c | 11 +++++ src/backend/access/index/indexam.c | 51 ++++++++++++++++++++ src/backend/access/nbtree/nbtree.c | 1 - src/backend/access/spgist/spgutils.c | 1 - src/backend/catalog/index.c | 6 ++- src/backend/commands/cluster.c | 2 +- src/backend/commands/indexcmds.c | 6 ++- src/backend/commands/matview.c | 5 +- src/backend/commands/opclasscmds.c | 6 +-- src/backend/commands/tablecmds.c | 2 +- src/backend/executor/nodeIndexscan.c | 2 +- src/backend/mock.mk | 3 -- src/backend/optimizer/path/indxpath.c | 4 +- src/backend/optimizer/util/pathnode.c | 3 +- src/backend/optimizer/util/plancat.c | 4 +- src/backend/parser/gram.y | 2 +- src/backend/parser/parse_utilcmd.c | 4 +- src/backend/utils/adt/selfuncs.c | 2 +- src/backend/utils/cache/lsyscache.c | 4 +- src/backend/utils/misc/guc.c | 1 + src/backend/utils/misc/guc_gp.c | 12 +++++ src/backend/utils/sort/sortsupport.c | 4 +- src/backend/utils/sort/tuplesort.c | 2 +- src/include/access/amapi.h | 2 + src/include/access/brin.h | 4 +- src/include/access/gin_private.h | 4 +- src/include/access/hash.h | 2 +- src/include/access/nbtree.h | 4 +- src/include/access/spgist_private.h | 2 +- src/include/catalog/index.h | 5 ++ src/include/catalog/pg_index.h | 2 + src/include/utils/sync_guc_name.h | 1 + src/test/regress/expected/brin.out | 9 ++-- src/test/regress/expected/brin_optimizer.out | 9 ++-- src/test/regress/sql/brin.sql | 6 ++- 37 files changed, 146 insertions(+), 48 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 5d7493e1949..df5ceacc90f 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1204,7 +1204,7 @@ brin_summarize_range_internal(PG_FUNCTION_ARGS) /* Must be a BRIN index */ if (indexRel->rd_rel->relkind != RELKIND_INDEX || - indexRel->rd_rel->relam != BRIN_AM_OID) + !IsIndexAccessMethod(indexRel->rd_rel->relam, BRIN_AM_OID)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a BRIN index", @@ -1290,7 +1290,7 @@ brin_desummarize_range(PG_FUNCTION_ARGS) /* Must be a BRIN index */ if (indexRel->rd_rel->relkind != RELKIND_INDEX || - indexRel->rd_rel->relam != BRIN_AM_OID) + !IsIndexAccessMethod(indexRel->rd_rel->relam, BRIN_AM_OID)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a BRIN index", diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c index 02d2d6cf4e7..88d2d9a7182 100644 --- a/src/backend/access/gin/ginfast.c +++ b/src/backend/access/gin/ginfast.c @@ -1042,7 +1042,7 @@ gin_clean_pending_list(PG_FUNCTION_ARGS) /* Must be a GIN index */ if (indexRel->rd_rel->relkind != RELKIND_INDEX || - indexRel->rd_rel->relam != GIN_AM_OID) + !IsIndexAccessMethod(indexRel->rd_rel->relam, GIN_AM_OID)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a GIN index", diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c index d30bc435146..028b8e9999e 100644 --- a/src/backend/access/index/amapi.c +++ b/src/backend/access/index/amapi.c @@ -20,7 +20,18 @@ #include "utils/builtins.h" #include "utils/syscache.h" +is_index_access_method_hook_type is_index_access_method_hook = NULL; +bool +IsIndexAccessMethod(Oid relam, Oid indexAccessMethod) +{ + if ((is_index_access_method_hook && (*is_index_access_method_hook)(relam, indexAccessMethod)) || + (!is_index_access_method_hook && relam == indexAccessMethod)) + { + return true; + } + return false; +} /* * GetIndexAmRoutine - call the specified access method handler routine to get * its IndexAmRoutine struct, which will be palloc'd in the caller's context. diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 095d4f84e63..ebacc2bfcb5 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -988,3 +988,54 @@ index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions, return build_local_reloptions(&relopts, attoptions, validate); } + +/* check_hook: validate new default_index_access_method */ +bool +check_default_index_access_method(char **newval, void **extra, GucSource source) +{ + if (**newval == '\0') + { + GUC_check_errdetail("%s cannot be empty.", + "check_default_index_access_method"); + return false; + } + + if (strlen(*newval) >= NAMEDATALEN) + { + GUC_check_errdetail("%s is too long (maximum %d characters).", + "check_default_index_access_method", NAMEDATALEN - 1); + return false; + } + + /* + * If we aren't inside a transaction, or not connected to a database, we + * cannot do the catalog access necessary to verify the method. Must + * accept the value on faith. + */ + if (IsTransactionState() && MyDatabaseId != InvalidOid) + { + if (!OidIsValid(get_index_am_oid(*newval, true))) + { + /* + * When source == PGC_S_TEST, don't throw a hard error for a + * nonexistent index access method, only a NOTICE. See comments in + * guc.h. + */ + if (source == PGC_S_TEST) + { + ereport(NOTICE, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("index access method \"%s\" does not exist", + *newval))); + } + else + { + GUC_check_errdetail("index access method \"%s\" does not exist.", + *newval); + return false; + } + } + } + + return true; +} diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index 5e3a2b7aa47..7c3563024db 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -41,7 +41,6 @@ #include "catalog/indexing.h" #include "catalog/pg_namespace.h" - /* * BTPARALLEL_NOT_INITIALIZED indicates that the scan has not started. * diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c index 4484805192c..e3e663a006e 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -35,7 +35,6 @@ #include "utils/lsyscache.h" #include "utils/syscache.h" - /* * SP-GiST handler function: return IndexAmRoutine with access method parameters * and callbacks. diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 917a541c6c0..4438ca40e33 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -99,6 +99,8 @@ #include "cdb/cdboidsync.h" #include "utils/faultinjector.h" +/* GUC variables */ +char *default_index_access_method = DEFAULT_INDEX_TYPE; /* Potentially set by pg_upgrade_support functions */ Oid binary_upgrade_next_index_pg_class_oid = InvalidOid; @@ -2808,7 +2810,7 @@ BuildSpeculativeIndexInfo(Relation index, IndexInfo *ii) */ Assert(ii->ii_Unique); - if (index->rd_rel->relam != BTREE_AM_OID) + if (!IsIndexAccessMethod(index->rd_rel->relam, BTREE_AM_OID)) elog(ERROR, "unexpected non-btree speculative unique index"); ii->ii_UniqueOps = (Oid *) palloc(sizeof(Oid) * indnkeyatts); @@ -3129,7 +3131,7 @@ index_build(Relation heapRelation, * to introduce parallelism to singlenode mode. */ if (parallel && !IS_SINGLENODE() && IsNormalProcessingMode() && - indexRelation->rd_rel->relam == BTREE_AM_OID) + IsIndexAccessMethod(indexRelation->rd_rel->relam, BTREE_AM_OID)) indexInfo->ii_ParallelWorkers = plan_create_index_workers(RelationGetRelid(heapRelation), RelationGetRelid(indexRelation)); diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 12ad9fa2af0..ed0762fd42b 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -962,7 +962,7 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, * tells us it's cheaper. Otherwise, always indexscan if an index is * provided, else plain seqscan. */ - if (OldIndex != NULL && OldIndex->rd_rel->relam == BTREE_AM_OID) + if (OldIndex != NULL && IsIndexAccessMethod(OldIndex->rd_rel->relam, BTREE_AM_OID)) use_sort = plan_cluster_use_sort(OIDOldHeap, OIDOldIndex); else use_sort = false; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 51e30960223..cfbbebdf2be 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1021,6 +1021,10 @@ DefineIndex(Oid relationId, * look up the access method, verify it can handle the requested features */ accessMethodName = stmt->accessMethod; + if (accessMethodName == NULL) + { + accessMethodName = default_index_access_method; + } tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName)); if (!HeapTupleIsValid(tuple)) { @@ -1268,7 +1272,7 @@ DefineIndex(Oid relationId, * btree opclasses; if there are ever any other index types that * support unique indexes, this logic will need extension. */ - if (accessMethodId == BTREE_AM_OID) + if (IsIndexAccessMethod(accessMethodId, BTREE_AM_OID)) eq_strategy = BTEqualStrategyNumber; else ereport(ERROR, diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index b1f6306f681..124f764c15f 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -15,6 +15,7 @@ */ #include "postgres.h" +#include "access/amapi.h" #include "access/genam.h" #include "access/heapam.h" #include "access/htup_details.h" @@ -908,7 +909,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner, if (!HeapTupleIsValid(cla_ht)) elog(ERROR, "cache lookup failed for opclass %u", opclass); cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht); - Assert(cla_tup->opcmethod == BTREE_AM_OID); + Assert(IsIndexAccessMethod(cla_tup->opcmethod, BTREE_AM_OID)); opfamily = cla_tup->opcfamily; opcintype = cla_tup->opcintype; ReleaseSysCache(cla_ht); @@ -1068,7 +1069,7 @@ is_usable_unique_index(Relation indexRel) */ if (indexStruct->indisunique && indexStruct->indimmediate && - indexRel->rd_rel->relam == BTREE_AM_OID && + IsIndexAccessMethod(indexRel->rd_rel->relam, BTREE_AM_OID) && indexStruct->indisvalid && RelationGetIndexPredicate(indexRel) == NIL && indexStruct->indnatts > 0) diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index ab9008ca9cd..4bd2716baea 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -595,7 +595,7 @@ DefineOpClass(CreateOpClassStmt *stmt) if (OidIsValid(storageoid)) { /* Just drop the spec if same as column datatype */ - if (storageoid == typeoid) + if (storageoid == typeoid && !amstorage) storageoid = InvalidOid; else if (!amstorage) ereport(ERROR, @@ -1289,7 +1289,7 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid, * returning int4, while proc 2 must be a 2-arg proc returning int8. * Otherwise we don't know. */ - else if (amoid == BTREE_AM_OID) + else if (IsIndexAccessMethod(amoid, BTREE_AM_OID)) { if (member->number == BTORDER_PROC) { @@ -1372,7 +1372,7 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid, errmsg("btree equal image functions must not be cross-type"))); } } - else if (amoid == HASH_AM_OID) + else if (IsIndexAccessMethod(amoid, HASH_AM_OID)) { if (member->number == HASHSTANDARD_PROC) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 63e1ee76d87..77adf685ddf 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -10751,7 +10751,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, * strategy number is equality. (Is it reasonable to insist that * every such index AM use btree's number for equality?) */ - if (amid != BTREE_AM_OID) + if (!IsIndexAccessMethod(amid, BTREE_AM_OID)) elog(ERROR, "only b-tree indexes are supported for foreign keys"); eqstrategy = BTEqualStrategyNumber; diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index a083e13d6b2..4c5d3b587e0 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -1378,7 +1378,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, * We have to look up the operator's associated btree support * function */ - if (index->rd_rel->relam != BTREE_AM_OID || + if (!IsIndexAccessMethod(index->rd_rel->relam, BTREE_AM_OID) || varattno < 1 || varattno > indnkeyatts) elog(ERROR, "bogus RowCompare index qualification"); opfamily = index->rd_opfamily[varattno - 1]; diff --git a/src/backend/mock.mk b/src/backend/mock.mk index 11b02242aab..c72c2dca2b0 100644 --- a/src/backend/mock.mk +++ b/src/backend/mock.mk @@ -37,9 +37,6 @@ EXCL_OBJS=\ # of the test programs. Feel free to link them back (i.e. remove them from # this exclusion list) as needed. EXCL_OBJS+=\ - src/backend/access/hash/hash.o \ - src/backend/access/hash/hashsearch.o \ - \ src/backend/utils/adt/cash.o \ src/backend/utils/adt/char.o \ src/backend/utils/adt/complex_type.o \ diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 421ff7bb9ba..d1c7c631a13 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -2830,7 +2830,7 @@ match_rowcompare_to_indexcol(PlannerInfo *root, Oid expr_coll; /* Forget it if we're not dealing with a btree index */ - if (index->relam != BTREE_AM_OID) + if (!IsIndexAccessMethod(index->relam, BTREE_AM_OID)) return NULL; index_relid = index->rel->relid; @@ -3529,7 +3529,7 @@ ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel, * generate_implied_equalities_for_column; see * match_eclass_clauses_to_index. */ - if (index->relam == BTREE_AM_OID && + if (IsIndexAccessMethod(index->relam, BTREE_AM_OID) && !list_member_oid(ec->ec_opfamilies, curFamily)) return false; diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 918c554579e..2371df4eacc 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -19,6 +19,7 @@ #include +#include "access/amapi.h" #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/extensible.h" @@ -1124,7 +1125,7 @@ create_index_path(PlannerInfo *root, required_outer); pathnode->path.parallel_aware = false; /* GPDB_12_MERGE_FEATURE_NOT_SUPPORTED: the parallel StreamBitmap scan is not implemented */ - pathnode->path.parallel_safe = rel->consider_parallel && (index->relam != BITMAP_AM_OID); + pathnode->path.parallel_safe = rel->consider_parallel && !IsIndexAccessMethod(index->relam, BITMAP_AM_OID); pathnode->path.parallel_workers = 0; pathnode->path.pathkeys = pathkeys; diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 2281294422c..832635cb981 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -311,7 +311,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, /* * Fetch the ordering information for the index, if any. */ - if (info->relam == BTREE_AM_OID) + if (IsIndexAccessMethod(info->relam, BTREE_AM_OID)) { /* * If it's a btree index, we can use its opfamily OIDs @@ -436,7 +436,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, info->tuples > rel->tuples) info->tuples = rel->tuples; - if (info->relam == BTREE_AM_OID) + if (IsIndexAccessMethod(info->relam, BTREE_AM_OID)) { /* For btrees, get tree height while we have the index open */ info->tree_height = _bt_getrootheight(indexRelation); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c9a49579eef..300f7da3505 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10165,7 +10165,7 @@ opt_index_name: access_method_clause: USING name { $$ = $2; } - | /*EMPTY*/ { $$ = DEFAULT_INDEX_TYPE; } + | /*EMPTY*/ { $$ = NULL; } ; index_params: index_elem { $$ = list_make1($1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index dd07b4075be..e0c8423d45c 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -3289,7 +3289,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) index->idxname = NULL; /* DefineIndex will choose name */ index->relation = cxt->relation; - index->accessMethod = constraint->access_method ? constraint->access_method : DEFAULT_INDEX_TYPE; + index->accessMethod = constraint->access_method ? constraint->access_method : default_index_access_method; index->options = constraint->options; index->tableSpace = constraint->indexspace; index->whereClause = constraint->where_clause; @@ -3413,7 +3413,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) * else dump and reload will produce a different index (breaking * pg_upgrade in particular). */ - if (index_rel->rd_rel->relam != get_index_am_oid(DEFAULT_INDEX_TYPE, false)) + if (!IsIndexAccessMethod(index_rel->rd_rel->relam, BTREE_AM_OID)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("index \"%s\" is not a btree", index_name), diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 221099b03f4..086d627d4d2 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -6229,7 +6229,7 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, ScanDirection indexscandir; /* Ignore non-btree indexes */ - if (index->relam != BTREE_AM_OID) + if (!IsIndexAccessMethod(index->relam, BTREE_AM_OID)) continue; /* diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index fe6b8c56a56..e57e41b10ec 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -866,8 +866,8 @@ equality_ops_are_compatible(Oid opno1, Oid opno2) Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple); /* must be btree or hash */ - if (op_form->amopmethod == BTREE_AM_OID || - op_form->amopmethod == HASH_AM_OID) + if (IsIndexAccessMethod(op_form->amopmethod, BTREE_AM_OID) || + IsIndexAccessMethod(op_form->amopmethod, HASH_AM_OID)) { if (op_in_opfamily(opno2, op_form->amopfamily)) { diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 5cc58b4f5d6..d4df12d2ebd 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -48,6 +48,7 @@ #include "catalog/pg_authid.h" #include "catalog/pg_profile.h" #include "catalog/storage.h" +#include "catalog/index.h" #include "commands/async.h" #include "commands/prepare.h" #include "commands/trigger.h" diff --git a/src/backend/utils/misc/guc_gp.c b/src/backend/utils/misc/guc_gp.c index e19ebd0e951..a25db7f796a 100644 --- a/src/backend/utils/misc/guc_gp.c +++ b/src/backend/utils/misc/guc_gp.c @@ -58,6 +58,7 @@ #include "utils/resource_manager.h" #include "utils/varlena.h" #include "utils/vmem_tracker.h" +#include "catalog/index.h" /* * These constants are copied from guc.c. They should not bitrot when we @@ -4572,6 +4573,17 @@ struct config_string ConfigureNamesString_gp[] = GP_VERSION, NULL, NULL, NULL }, + + { + {"default_index_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the default index access method."), + NULL, + GUC_IS_NAME + }, + &default_index_access_method, + DEFAULT_INDEX_TYPE, + check_default_index_access_method, NULL, NULL + }, #ifndef USE_INTERNAL_FTS { {"gp_etcd_account_id", PGC_BACKEND, CUSTOM_OPTIONS, diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c index 6a889ec189f..97d8c580d64 100644 --- a/src/backend/utils/sort/sortsupport.c +++ b/src/backend/utils/sort/sortsupport.c @@ -167,7 +167,7 @@ PrepareSortSupportFromIndexRel(Relation indexRel, int16 strategy, Assert(ssup->comparator == NULL); - if (indexRel->rd_rel->relam != BTREE_AM_OID) + if (!IsIndexAccessMethod(indexRel->rd_rel->relam, BTREE_AM_OID)) elog(ERROR, "unexpected non-btree AM: %u", indexRel->rd_rel->relam); if (strategy != BTGreaterStrategyNumber && strategy != BTLessStrategyNumber) @@ -194,7 +194,7 @@ PrepareSortSupportFromGistIndexRel(Relation indexRel, SortSupport ssup) Assert(ssup->comparator == NULL); - if (indexRel->rd_rel->relam != GIST_AM_OID) + if (!IsIndexAccessMethod(indexRel->rd_rel->relam, GIST_AM_OID)) elog(ERROR, "unexpected non-gist AM: %u", indexRel->rd_rel->relam); ssup->ssup_reverse = false; diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 8d2bdc3df94..0b9c16f9417 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -985,7 +985,7 @@ tuplesort_begin_cluster(TupleDesc tupDesc, MemoryContext oldcontext; int i; - Assert(indexRel->rd_rel->relam == BTREE_AM_OID); + Assert(IsIndexAccessMethod(indexRel->rd_rel->relam, BTREE_AM_OID)); oldcontext = MemoryContextSwitchTo(state->maincontext); diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h index 02be79c3663..a30f4bb6a63 100644 --- a/src/include/access/amapi.h +++ b/src/include/access/amapi.h @@ -14,6 +14,7 @@ #define AMAPI_H #include "access/genam.h" +#include "catalog/pg_index.h" /* * We don't wish to include planner header files here, since most of an index @@ -285,6 +286,7 @@ typedef struct IndexAmRoutine /* Functions in access/index/amapi.c */ +extern bool IsIndexAccessMethod(Oid relam, Oid indexAccessMethod); extern IndexAmRoutine *GetIndexAmRoutine(Oid amhandler); extern IndexAmRoutine *GetIndexAmRoutineByAmId(Oid amoid, bool noerror); diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 4e2be13cd61..5c80a6ae611 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -38,13 +38,13 @@ typedef struct BrinStatsData #define BRIN_DEFAULT_PAGES_PER_RANGE 128 #define BrinGetPagesPerRange(relation) \ (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ - relation->rd_rel->relam == BRIN_AM_OID), \ + IsIndexAccessMethod(relation->rd_rel->relam, BRIN_AM_OID)), \ (relation)->rd_options ? \ ((BrinOptions *) (relation)->rd_options)->pagesPerRange : \ BRIN_DEFAULT_PAGES_PER_RANGE) #define BrinGetAutoSummarize(relation) \ (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ - relation->rd_rel->relam == BRIN_AM_OID), \ + IsIndexAccessMethod(relation->rd_rel->relam, BRIN_AM_OID)), \ (relation)->rd_options ? \ ((BrinOptions *) (relation)->rd_options)->autosummarize : \ false) diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h index b24112ff96a..74c9b70dc32 100644 --- a/src/include/access/gin_private.h +++ b/src/include/access/gin_private.h @@ -32,12 +32,12 @@ typedef struct GinOptions #define GIN_DEFAULT_USE_FASTUPDATE true #define GinGetUseFastUpdate(relation) \ (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ - relation->rd_rel->relam == GIN_AM_OID), \ + IsIndexAccessMethod(relation->rd_rel->relam, GIN_AM_OID)), \ (relation)->rd_options ? \ ((GinOptions *) (relation)->rd_options)->useFastUpdate : GIN_DEFAULT_USE_FASTUPDATE) #define GinGetPendingListCleanupSize(relation) \ (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ - relation->rd_rel->relam == GIN_AM_OID), \ + IsIndexAccessMethod(relation->rd_rel->relam, GIN_AM_OID)), \ (relation)->rd_options && \ ((GinOptions *) (relation)->rd_options)->pendingListCleanupSize != -1 ? \ ((GinOptions *) (relation)->rd_options)->pendingListCleanupSize : \ diff --git a/src/include/access/hash.h b/src/include/access/hash.h index 6de41944a9c..f29cd3db35e 100644 --- a/src/include/access/hash.h +++ b/src/include/access/hash.h @@ -272,7 +272,7 @@ typedef struct HashOptions #define HashGetFillFactor(relation) \ (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ - relation->rd_rel->relam == HASH_AM_OID), \ + IsIndexAccessMethod(relation->rd_rel->relam, HASH_AM_OID)), \ (relation)->rd_options ? \ ((HashOptions *) (relation)->rd_options)->fillfactor : \ HASH_DEFAULT_FILLFACTOR) diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index 28d360d058f..816a0fe761d 100644 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -1092,7 +1092,7 @@ typedef struct BTOptions #define BTGetFillFactor(relation) \ (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ - relation->rd_rel->relam == BTREE_AM_OID), \ + IsIndexAccessMethod(relation->rd_rel->relam, BTREE_AM_OID)), \ (relation)->rd_options ? \ ((BTOptions *) (relation)->rd_options)->fillfactor : \ BTREE_DEFAULT_FILLFACTOR) @@ -1100,7 +1100,7 @@ typedef struct BTOptions (BLCKSZ * (100 - BTGetFillFactor(relation)) / 100) #define BTGetDeduplicateItems(relation) \ (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ - relation->rd_rel->relam == BTREE_AM_OID), \ + IsIndexAccessMethod(relation->rd_rel->relam, BTREE_AM_OID)), \ ((relation)->rd_options ? \ ((BTOptions *) (relation)->rd_options)->deduplicate_items : true)) diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h index 40d3b71b89e..cc7f0922556 100644 --- a/src/include/access/spgist_private.h +++ b/src/include/access/spgist_private.h @@ -31,7 +31,7 @@ typedef struct SpGistOptions #define SpGistGetFillFactor(relation) \ (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ - relation->rd_rel->relam == SPGIST_AM_OID), \ + IsIndexAccessMethod(relation->rd_rel->relam, SPGIST_AM_OID)), \ (relation)->rd_options ? \ ((SpGistOptions *) (relation)->rd_options)->fillfactor : \ SPGIST_DEFAULT_FILLFACTOR) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 8e0ee2349b2..28985171776 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -20,10 +20,13 @@ #include "catalog/objectaddress.h" #include "executor/tuptable.h" /* TupTableSlot */ #include "nodes/execnodes.h" +#include "utils/guc.h" struct EState; /* #include "nodes/execnodes.h" */ #define DEFAULT_INDEX_TYPE "btree" +/* GUCs */ +extern char *default_index_access_method; /* Action code for index_set_state_flags */ typedef enum @@ -225,6 +228,8 @@ extern void SerializeReindexState(Size maxsize, char *start_address); extern void RestoreReindexState(void *reindexstate); extern void IndexSetParentIndex(Relation idx, Oid parentOid); +extern bool check_default_index_access_method(char **newval, void **extra, + GucSource source); /* diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h index 38b5bfc42c2..15794cb50ef 100644 --- a/src/include/catalog/pg_index.h +++ b/src/include/catalog/pg_index.h @@ -73,6 +73,8 @@ FOREIGN_KEY(indrelid REFERENCES pg_class(oid)); * ---------------- */ typedef FormData_pg_index *Form_pg_index; +typedef bool (*is_index_access_method_hook_type)(Oid id, Oid expected_oid); +extern PGDLLIMPORT is_index_access_method_hook_type is_index_access_method_hook; DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops)); #define IndexIndrelidIndexId 2678 diff --git a/src/include/utils/sync_guc_name.h b/src/include/utils/sync_guc_name.h index 1b4c8a040c4..aefc91e8716 100644 --- a/src/include/utils/sync_guc_name.h +++ b/src/include/utils/sync_guc_name.h @@ -17,6 +17,7 @@ "DateStyle", "debug_discard_caches", "default_table_access_method", + "default_index_access_method", "default_tablespace", "default_toast_compression", "dml_ignore_target_partition_check", diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out index 73a68af6b51..bdc3dad0569 100644 --- a/src/test/regress/expected/brin.out +++ b/src/test/regress/expected/brin.out @@ -560,7 +560,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b = 1; (4 rows) -- make sure data are properly de-toasted in BRIN index -CREATE TABLE brintest_3 (a text, b text, c text, d text); +CREATE TABLE brintest_3 (a text, b text, c text, d text, e varchar); -- long random strings (~2000 chars each, so ~6kB for min/max on two -- columns) to trigger toasting WITH rand_value AS (SELECT string_agg(md5(i::text),'') AS val FROM generate_series(1,60) s(i)) @@ -573,6 +573,9 @@ DELETE FROM brintest_3; -- is a one way to achieve that, because it does exactly such wait. CREATE INDEX brin_test_temp_idx ON brintest_3(a); DROP INDEX brin_test_temp_idx; +-- make sure varchar to text implicitly +CREATE INDEX brin_test_varchar_to_text_idx on brintest_3(e); +DROP INDEX brin_test_varchar_to_text_idx; -- vacuum the table, to discard TOAST data VACUUM brintest_3; -- retry insert with a different random-looking (but deterministic) value @@ -596,8 +599,8 @@ SELECT * FROM brintest_3 WHERE b < '0'; (6 rows) SELECT * FROM brintest_3 WHERE b < '0'; - a | b | c | d ----+---+---+--- + a | b | c | d | e +---+---+---+---+--- (0 rows) DROP TABLE brintest_3; diff --git a/src/test/regress/expected/brin_optimizer.out b/src/test/regress/expected/brin_optimizer.out index 3dd5ff30598..a95155d8344 100644 --- a/src/test/regress/expected/brin_optimizer.out +++ b/src/test/regress/expected/brin_optimizer.out @@ -584,7 +584,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b = 1; (6 rows) -- make sure data are properly de-toasted in BRIN index -CREATE TABLE brintest_3 (a text, b text, c text, d text); +CREATE TABLE brintest_3 (a text, b text, c text, d text, e varchar); NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Cloudberry Database data distribution key for this table. HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- long random strings (~2000 chars each, so ~6kB for min/max on two @@ -601,6 +601,9 @@ HINT: For non-partitioned tables, run analyze (). For -- is a one way to achieve that, because it does exactly such wait. CREATE INDEX brin_test_temp_idx ON brintest_3(a); DROP INDEX brin_test_temp_idx; +-- make sure varchar to text implicitly +CREATE INDEX brin_test_varchar_to_text_idx on brintest_3(e); +DROP INDEX brin_test_varchar_to_text_idx; -- vacuum the table, to discard TOAST data VACUUM brintest_3; -- retry insert with a different random-looking (but deterministic) value @@ -624,8 +627,8 @@ SELECT * FROM brintest_3 WHERE b < '0'; (6 rows) SELECT * FROM brintest_3 WHERE b < '0'; - a | b | c | d ----+---+---+--- + a | b | c | d | e +---+---+---+---+--- (0 rows) DROP TABLE brintest_3; diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql index c8879599a5f..e9704aaa0d7 100644 --- a/src/test/regress/sql/brin.sql +++ b/src/test/regress/sql/brin.sql @@ -511,7 +511,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE a = 1; EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b = 1; -- make sure data are properly de-toasted in BRIN index -CREATE TABLE brintest_3 (a text, b text, c text, d text); +CREATE TABLE brintest_3 (a text, b text, c text, d text, e varchar); -- long random strings (~2000 chars each, so ~6kB for min/max on two -- columns) to trigger toasting @@ -528,6 +528,10 @@ DELETE FROM brintest_3; CREATE INDEX brin_test_temp_idx ON brintest_3(a); DROP INDEX brin_test_temp_idx; +-- make sure varchar to text implicitly +CREATE INDEX brin_test_varchar_to_text_idx on brintest_3(e); +DROP INDEX brin_test_varchar_to_text_idx; + -- vacuum the table, to discard TOAST data VACUUM brintest_3;