334 lines
8.4 KiB
C
334 lines
8.4 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* dummy_index_am.c
|
|
* Index AM template main file.
|
|
*
|
|
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/test/modules/dummy_index_am/dummy_index_am.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/amapi.h"
|
|
#include "access/reloptions.h"
|
|
#include "catalog/index.h"
|
|
#include "commands/vacuum.h"
|
|
#include "nodes/pathnodes.h"
|
|
#include "utils/guc.h"
|
|
#include "utils/rel.h"
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
/* parse table for fillRelOptions */
|
|
relopt_parse_elt di_relopt_tab[6];
|
|
|
|
/* Kind of relation options for dummy index */
|
|
relopt_kind di_relopt_kind;
|
|
|
|
typedef enum DummyAmEnum
|
|
{
|
|
DUMMY_AM_ENUM_ONE,
|
|
DUMMY_AM_ENUM_TWO,
|
|
} DummyAmEnum;
|
|
|
|
/* Dummy index options */
|
|
typedef struct DummyIndexOptions
|
|
{
|
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
|
int option_int;
|
|
double option_real;
|
|
bool option_bool;
|
|
DummyAmEnum option_enum;
|
|
int option_string_val_offset;
|
|
int option_string_null_offset;
|
|
} DummyIndexOptions;
|
|
|
|
relopt_enum_elt_def dummyAmEnumValues[] =
|
|
{
|
|
{"one", DUMMY_AM_ENUM_ONE},
|
|
{"two", DUMMY_AM_ENUM_TWO},
|
|
{(const char *) NULL} /* list terminator */
|
|
};
|
|
|
|
/* Handler for index AM */
|
|
PG_FUNCTION_INFO_V1(dihandler);
|
|
|
|
/*
|
|
* Validation function for string relation options.
|
|
*/
|
|
static void
|
|
validate_string_option(const char *value)
|
|
{
|
|
ereport(NOTICE,
|
|
(errmsg("new option value for string parameter %s",
|
|
value ? value : "NULL")));
|
|
}
|
|
|
|
/*
|
|
* This function creates a full set of relation option types,
|
|
* with various patterns.
|
|
*/
|
|
static void
|
|
create_reloptions_table(void)
|
|
{
|
|
di_relopt_kind = add_reloption_kind();
|
|
|
|
add_int_reloption(di_relopt_kind, "option_int",
|
|
"Integer option for dummy_index_am",
|
|
10, -10, 100, AccessExclusiveLock);
|
|
di_relopt_tab[0].optname = "option_int";
|
|
di_relopt_tab[0].opttype = RELOPT_TYPE_INT;
|
|
di_relopt_tab[0].offset = offsetof(DummyIndexOptions, option_int);
|
|
|
|
add_real_reloption(di_relopt_kind, "option_real",
|
|
"Real option for dummy_index_am",
|
|
3.1415, -10, 100, AccessExclusiveLock);
|
|
di_relopt_tab[1].optname = "option_real";
|
|
di_relopt_tab[1].opttype = RELOPT_TYPE_REAL;
|
|
di_relopt_tab[1].offset = offsetof(DummyIndexOptions, option_real);
|
|
|
|
add_bool_reloption(di_relopt_kind, "option_bool",
|
|
"Boolean option for dummy_index_am",
|
|
true, AccessExclusiveLock);
|
|
di_relopt_tab[2].optname = "option_bool";
|
|
di_relopt_tab[2].opttype = RELOPT_TYPE_BOOL;
|
|
di_relopt_tab[2].offset = offsetof(DummyIndexOptions, option_bool);
|
|
|
|
add_enum_reloption(di_relopt_kind, "option_enum",
|
|
"Enum option for dummy_index_am",
|
|
dummyAmEnumValues,
|
|
DUMMY_AM_ENUM_ONE,
|
|
"Valid values are \"one\" and \"two\".",
|
|
AccessExclusiveLock);
|
|
di_relopt_tab[3].optname = "option_enum";
|
|
di_relopt_tab[3].opttype = RELOPT_TYPE_ENUM;
|
|
di_relopt_tab[3].offset = offsetof(DummyIndexOptions, option_enum);
|
|
|
|
add_string_reloption(di_relopt_kind, "option_string_val",
|
|
"String option for dummy_index_am with non-NULL default",
|
|
"DefaultValue", &validate_string_option,
|
|
AccessExclusiveLock);
|
|
di_relopt_tab[4].optname = "option_string_val";
|
|
di_relopt_tab[4].opttype = RELOPT_TYPE_STRING;
|
|
di_relopt_tab[4].offset = offsetof(DummyIndexOptions,
|
|
option_string_val_offset);
|
|
|
|
/*
|
|
* String option for dummy_index_am with NULL default, and without
|
|
* description.
|
|
*/
|
|
add_string_reloption(di_relopt_kind, "option_string_null",
|
|
NULL, /* description */
|
|
NULL, &validate_string_option,
|
|
AccessExclusiveLock);
|
|
di_relopt_tab[5].optname = "option_string_null";
|
|
di_relopt_tab[5].opttype = RELOPT_TYPE_STRING;
|
|
di_relopt_tab[5].offset = offsetof(DummyIndexOptions,
|
|
option_string_null_offset);
|
|
}
|
|
|
|
|
|
/*
|
|
* Build a new index.
|
|
*/
|
|
static IndexBuildResult *
|
|
dibuild(Relation heap, Relation index, IndexInfo *indexInfo)
|
|
{
|
|
IndexBuildResult *result;
|
|
|
|
result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
|
|
|
|
/* let's pretend that no tuples were scanned */
|
|
result->heap_tuples = 0;
|
|
/* and no index tuples were created (that is true) */
|
|
result->index_tuples = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Build an empty index for the initialization fork.
|
|
*/
|
|
static void
|
|
dibuildempty(Relation index)
|
|
{
|
|
/* No need to build an init fork for a dummy index */
|
|
}
|
|
|
|
/*
|
|
* Insert new tuple to index AM.
|
|
*/
|
|
static bool
|
|
diinsert(Relation index, Datum *values, bool *isnull,
|
|
ItemPointer ht_ctid, Relation heapRel,
|
|
IndexUniqueCheck checkUnique,
|
|
bool indexUnchanged,
|
|
IndexInfo *indexInfo)
|
|
{
|
|
/* nothing to do */
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Bulk deletion of all index entries pointing to a set of table tuples.
|
|
*/
|
|
static IndexBulkDeleteResult *
|
|
dibulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
|
|
IndexBulkDeleteCallback callback, void *callback_state)
|
|
{
|
|
/*
|
|
* There is nothing to delete. Return NULL as there is nothing to pass to
|
|
* amvacuumcleanup.
|
|
*/
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Post-VACUUM cleanup for index AM.
|
|
*/
|
|
static IndexBulkDeleteResult *
|
|
divacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
|
|
{
|
|
/* Index has not been modified, so returning NULL is fine */
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Estimate cost of index AM.
|
|
*/
|
|
static void
|
|
dicostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|
Cost *indexStartupCost, Cost *indexTotalCost,
|
|
Selectivity *indexSelectivity, double *indexCorrelation,
|
|
double *indexPages)
|
|
{
|
|
/* Tell planner to never use this index! */
|
|
*indexStartupCost = 1.0e10;
|
|
*indexTotalCost = 1.0e10;
|
|
|
|
/* Do not care about the rest */
|
|
*indexSelectivity = 1;
|
|
*indexCorrelation = 0;
|
|
*indexPages = 1;
|
|
}
|
|
|
|
/*
|
|
* Parse relation options for index AM, returning a DummyIndexOptions
|
|
* structure filled with option values.
|
|
*/
|
|
static bytea *
|
|
dioptions(Datum reloptions, bool validate)
|
|
{
|
|
return (bytea *) build_reloptions(reloptions, validate,
|
|
di_relopt_kind,
|
|
sizeof(DummyIndexOptions),
|
|
di_relopt_tab, lengthof(di_relopt_tab));
|
|
}
|
|
|
|
/*
|
|
* Validator for index AM.
|
|
*/
|
|
static bool
|
|
divalidate(Oid opclassoid)
|
|
{
|
|
/* Index is dummy so we are happy with any opclass */
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Begin scan of index AM.
|
|
*/
|
|
static IndexScanDesc
|
|
dibeginscan(Relation r, int nkeys, int norderbys)
|
|
{
|
|
IndexScanDesc scan;
|
|
|
|
/* Let's pretend we are doing something */
|
|
scan = RelationGetIndexScan(r, nkeys, norderbys);
|
|
return scan;
|
|
}
|
|
|
|
/*
|
|
* Rescan of index AM.
|
|
*/
|
|
static void
|
|
direscan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
|
|
ScanKey orderbys, int norderbys)
|
|
{
|
|
/* nothing to do */
|
|
}
|
|
|
|
/*
|
|
* End scan of index AM.
|
|
*/
|
|
static void
|
|
diendscan(IndexScanDesc scan)
|
|
{
|
|
/* nothing to do */
|
|
}
|
|
|
|
/*
|
|
* Index AM handler function: returns IndexAmRoutine with access method
|
|
* parameters and callbacks.
|
|
*/
|
|
Datum
|
|
dihandler(PG_FUNCTION_ARGS)
|
|
{
|
|
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
|
|
|
|
amroutine->amstrategies = 0;
|
|
amroutine->amsupport = 1;
|
|
amroutine->amcanorder = false;
|
|
amroutine->amcanorderbyop = false;
|
|
amroutine->amcanbackward = false;
|
|
amroutine->amcanunique = false;
|
|
amroutine->amcanmulticol = false;
|
|
amroutine->amoptionalkey = false;
|
|
amroutine->amsearcharray = false;
|
|
amroutine->amsearchnulls = false;
|
|
amroutine->amstorage = false;
|
|
amroutine->amclusterable = false;
|
|
amroutine->ampredlocks = false;
|
|
amroutine->amcanparallel = false;
|
|
amroutine->amcanbuildparallel = false;
|
|
amroutine->amcaninclude = false;
|
|
amroutine->amusemaintenanceworkmem = false;
|
|
amroutine->amsummarizing = false;
|
|
amroutine->amparallelvacuumoptions = VACUUM_OPTION_NO_PARALLEL;
|
|
amroutine->amkeytype = InvalidOid;
|
|
|
|
amroutine->ambuild = dibuild;
|
|
amroutine->ambuildempty = dibuildempty;
|
|
amroutine->aminsert = diinsert;
|
|
amroutine->ambulkdelete = dibulkdelete;
|
|
amroutine->amvacuumcleanup = divacuumcleanup;
|
|
amroutine->amcanreturn = NULL;
|
|
amroutine->amcostestimate = dicostestimate;
|
|
amroutine->amoptions = dioptions;
|
|
amroutine->amproperty = NULL;
|
|
amroutine->ambuildphasename = NULL;
|
|
amroutine->amvalidate = divalidate;
|
|
amroutine->ambeginscan = dibeginscan;
|
|
amroutine->amrescan = direscan;
|
|
amroutine->amgettuple = NULL;
|
|
amroutine->amgetbitmap = NULL;
|
|
amroutine->amendscan = diendscan;
|
|
amroutine->ammarkpos = NULL;
|
|
amroutine->amrestrpos = NULL;
|
|
amroutine->amestimateparallelscan = NULL;
|
|
amroutine->aminitparallelscan = NULL;
|
|
amroutine->amparallelrescan = NULL;
|
|
|
|
PG_RETURN_POINTER(amroutine);
|
|
}
|
|
|
|
void
|
|
_PG_init(void)
|
|
{
|
|
create_reloptions_table();
|
|
}
|