Dropped support for Postgres 12

This commit is contained in:
Andrew Kane
2024-09-19 18:13:54 -07:00
parent 7117513532
commit b738ffecc1
14 changed files with 14 additions and 150 deletions

View File

@@ -13,14 +13,12 @@ jobs:
- postgres: 17 - postgres: 17
os: ubuntu-24.04 os: ubuntu-24.04
- postgres: 16 - postgres: 16
os: ubuntu-24.04 os: ubuntu-22.04
- postgres: 15 - postgres: 15
os: ubuntu-22.04 os: ubuntu-22.04
- postgres: 14 - postgres: 14
os: ubuntu-22.04
- postgres: 13
os: ubuntu-20.04 os: ubuntu-20.04
- postgres: 12 - postgres: 13
os: ubuntu-20.04 os: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@@ -1,6 +1,7 @@
## 0.8.0 (unreleased) ## 0.8.0 (unreleased)
- Reduced memory usage for HNSW index scans - Reduced memory usage for HNSW index scans
- Dropped support for Postgres 12
## 0.7.4 (2024-08-05) ## 0.7.4 (2024-08-05)

View File

@@ -4,8 +4,8 @@
#include "postgres.h" #include "postgres.h"
/* Check version in first header */ /* Check version in first header */
#if PG_VERSION_NUM < 120000 #if PG_VERSION_NUM < 130000
#error "Requires PostgreSQL 12+" #error "Requires PostgreSQL 13+"
#endif #endif
extern uint64 (*BitHammingDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance); extern uint64 (*BitHammingDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance);

View File

@@ -19,11 +19,6 @@
#include "utils/numeric.h" #include "utils/numeric.h"
#include "vector.h" #include "vector.h"
#if PG_VERSION_NUM < 130000
#define TYPALIGN_DOUBLE 'd'
#define TYPALIGN_INT 'i'
#endif
#define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1) #define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1)
#define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1)) #define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1))

View File

@@ -60,17 +60,9 @@ HnswInit(void)
hnsw_relopt_kind = add_reloption_kind(); hnsw_relopt_kind = add_reloption_kind();
add_int_reloption(hnsw_relopt_kind, "m", "Max number of connections", add_int_reloption(hnsw_relopt_kind, "m", "Max number of connections",
HNSW_DEFAULT_M, HNSW_MIN_M, HNSW_MAX_M HNSW_DEFAULT_M, HNSW_MIN_M, HNSW_MAX_M, AccessExclusiveLock);
#if PG_VERSION_NUM >= 130000
,AccessExclusiveLock
#endif
);
add_int_reloption(hnsw_relopt_kind, "ef_construction", "Size of the dynamic candidate list for construction", add_int_reloption(hnsw_relopt_kind, "ef_construction", "Size of the dynamic candidate list for construction",
HNSW_DEFAULT_EF_CONSTRUCTION, HNSW_MIN_EF_CONSTRUCTION, HNSW_MAX_EF_CONSTRUCTION HNSW_DEFAULT_EF_CONSTRUCTION, HNSW_MIN_EF_CONSTRUCTION, HNSW_MAX_EF_CONSTRUCTION, AccessExclusiveLock);
#if PG_VERSION_NUM >= 130000
,AccessExclusiveLock
#endif
);
DefineCustomIntVariable("hnsw.ef_search", "Sets the size of the dynamic candidate list for search", DefineCustomIntVariable("hnsw.ef_search", "Sets the size of the dynamic candidate list for search",
"Valid range is 1..1000.", &hnsw_ef_search, "Valid range is 1..1000.", &hnsw_ef_search,
@@ -155,23 +147,10 @@ hnswoptions(Datum reloptions, bool validate)
{"ef_construction", RELOPT_TYPE_INT, offsetof(HnswOptions, efConstruction)}, {"ef_construction", RELOPT_TYPE_INT, offsetof(HnswOptions, efConstruction)},
}; };
#if PG_VERSION_NUM >= 130000
return (bytea *) build_reloptions(reloptions, validate, return (bytea *) build_reloptions(reloptions, validate,
hnsw_relopt_kind, hnsw_relopt_kind,
sizeof(HnswOptions), sizeof(HnswOptions),
tab, lengthof(tab)); tab, lengthof(tab));
#else
relopt_value *options;
int numoptions;
HnswOptions *rdopts;
options = parseRelOptions(reloptions, validate, hnsw_relopt_kind, &numoptions);
rdopts = allocateReloptStruct(sizeof(HnswOptions), options, numoptions);
fillRelOptions((void *) rdopts, sizeof(HnswOptions), options, numoptions,
validate, tab, lengthof(tab));
return (bytea *) rdopts;
#endif
} }
/* /*
@@ -196,9 +175,7 @@ hnswhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0; amroutine->amstrategies = 0;
amroutine->amsupport = 3; amroutine->amsupport = 3;
#if PG_VERSION_NUM >= 130000
amroutine->amoptsprocnum = 0; amroutine->amoptsprocnum = 0;
#endif
amroutine->amcanorder = false; amroutine->amcanorder = false;
amroutine->amcanorderbyop = true; amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false; /* can change direction mid-scan */ amroutine->amcanbackward = false; /* can change direction mid-scan */
@@ -215,15 +192,11 @@ hnswhandler(PG_FUNCTION_ARGS)
amroutine->amcanbuildparallel = true; amroutine->amcanbuildparallel = true;
#endif #endif
amroutine->amcaninclude = false; amroutine->amcaninclude = false;
#if PG_VERSION_NUM >= 130000
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */ amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
#endif
#if PG_VERSION_NUM >= 160000 #if PG_VERSION_NUM >= 160000
amroutine->amsummarizing = false; amroutine->amsummarizing = false;
#endif #endif
#if PG_VERSION_NUM >= 130000
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL; amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
#endif
amroutine->amkeytype = InvalidOid; amroutine->amkeytype = InvalidOid;
/* Interface functions */ /* Interface functions */

View File

@@ -76,11 +76,6 @@
#define SeedRandom(seed) srandom(seed) #define SeedRandom(seed) srandom(seed)
#endif #endif
#if PG_VERSION_NUM < 130000
#define list_delete_last(list) list_truncate(list, list_length(list) - 1)
#define list_sort(list, cmp) ((list) = list_qsort(list, cmp))
#endif
#define HnswIsElementTuple(tup) ((tup)->type == HNSW_ELEMENT_TUPLE_TYPE) #define HnswIsElementTuple(tup) ((tup)->type == HNSW_ELEMENT_TUPLE_TYPE)
#define HnswIsNeighborTuple(tup) ((tup)->type == HNSW_NEIGHBOR_TUPLE_TYPE) #define HnswIsNeighborTuple(tup) ((tup)->type == HNSW_NEIGHBOR_TUPLE_TYPE)

View File

@@ -60,12 +60,6 @@
#include "pgstat.h" #include "pgstat.h"
#endif #endif
#if PG_VERSION_NUM >= 130000
#define CALLBACK_ITEM_POINTER ItemPointer tid
#else
#define CALLBACK_ITEM_POINTER HeapTuple hup
#endif
#if PG_VERSION_NUM >= 140000 #if PG_VERSION_NUM >= 140000
#include "utils/backend_status.h" #include "utils/backend_status.h"
#include "utils/wait_event.h" #include "utils/wait_event.h"
@@ -75,10 +69,6 @@
#define PARALLEL_KEY_HNSW_AREA UINT64CONST(0xA000000000000002) #define PARALLEL_KEY_HNSW_AREA UINT64CONST(0xA000000000000002)
#define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xA000000000000003) #define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xA000000000000003)
#if PG_VERSION_NUM < 130000
#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T + SIZEOF_VOID_P * 2)
#endif
/* /*
* Create the metapage * Create the metapage
*/ */
@@ -585,17 +575,13 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
* Callback for table_index_build_scan * Callback for table_index_build_scan
*/ */
static void static void
BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values, BuildCallback(Relation index, ItemPointer tid, Datum *values,
bool *isnull, bool tupleIsAlive, void *state) bool *isnull, bool tupleIsAlive, void *state)
{ {
HnswBuildState *buildstate = (HnswBuildState *) state; HnswBuildState *buildstate = (HnswBuildState *) state;
HnswGraph *graph = buildstate->graph; HnswGraph *graph = buildstate->graph;
MemoryContext oldCtx; MemoryContext oldCtx;
#if PG_VERSION_NUM < 130000
ItemPointer tid = &hup->t_self;
#endif
/* Skip nulls */ /* Skip nulls */
if (isnull[0]) if (isnull[0])
return; return;
@@ -658,11 +644,7 @@ HnswMemoryContextAlloc(Size size, void *state)
HnswBuildState *buildstate = (HnswBuildState *) state; HnswBuildState *buildstate = (HnswBuildState *) state;
void *chunk = MemoryContextAlloc(buildstate->graphCtx, size); void *chunk = MemoryContextAlloc(buildstate->graphCtx, size);
#if PG_VERSION_NUM >= 130000
buildstate->graphData.memoryUsed = MemoryContextMemAllocated(buildstate->graphCtx, false); buildstate->graphData.memoryUsed = MemoryContextMemAllocated(buildstate->graphCtx, false);
#else
buildstate->graphData.memoryUsed += MAXALIGN(size);
#endif
return chunk; return chunk;
} }

View File

@@ -160,7 +160,7 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
so->first = false; so->first = false;
#if defined(HNSW_MEMORY) && PG_VERSION_NUM >= 130000 #if defined(HNSW_MEMORY)
elog(INFO, "memory: %zu MB", MemoryContextMemAllocated(so->tmpCtx, false) / (1024 * 1024)); elog(INFO, "memory: %zu MB", MemoryContextMemAllocated(so->tmpCtx, false) / (1024 * 1024));
#endif #endif
} }

View File

@@ -5,6 +5,7 @@
#include "access/generic_xlog.h" #include "access/generic_xlog.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "catalog/pg_type_d.h" #include "catalog/pg_type_d.h"
#include "common/hashfn.h"
#include "fmgr.h" #include "fmgr.h"
#include "hnsw.h" #include "hnsw.h"
#include "lib/pairingheap.h" #include "lib/pairingheap.h"
@@ -14,12 +15,6 @@
#include "utils/memdebug.h" #include "utils/memdebug.h"
#include "utils/rel.h" #include "utils/rel.h"
#if PG_VERSION_NUM >= 130000
#include "common/hashfn.h"
#else
#include "utils/hashutils.h"
#endif
#if PG_VERSION_NUM < 170000 #if PG_VERSION_NUM < 170000
static inline uint64 static inline uint64
murmurhash64(uint64 data) murmurhash64(uint64 data)
@@ -701,23 +696,15 @@ AddToVisited(char *base, visited_hash * v, HnswElementPtr elementPtr, Relation i
} }
else if (base != NULL) else if (base != NULL)
{ {
#if PG_VERSION_NUM >= 130000
HnswElement element = HnswPtrAccess(base, elementPtr); HnswElement element = HnswPtrAccess(base, elementPtr);
offsethash_insert_hash(v->offsets, HnswPtrOffset(elementPtr), element->hash, found); offsethash_insert_hash(v->offsets, HnswPtrOffset(elementPtr), element->hash, found);
#else
offsethash_insert(v->offsets, HnswPtrOffset(elementPtr), found);
#endif
} }
else else
{ {
#if PG_VERSION_NUM >= 130000
HnswElement element = HnswPtrAccess(base, elementPtr); HnswElement element = HnswPtrAccess(base, elementPtr);
pointerhash_insert_hash(v->pointers, (uintptr_t) HnswPtrPointer(elementPtr), element->hash, found); pointerhash_insert_hash(v->pointers, (uintptr_t) HnswPtrPointer(elementPtr), element->hash, found);
#else
pointerhash_insert(v->pointers, (uintptr_t) HnswPtrPointer(elementPtr), found);
#endif
} }
} }
@@ -948,17 +935,10 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
* Compare candidate distances with pointer tie-breaker * Compare candidate distances with pointer tie-breaker
*/ */
static int static int
#if PG_VERSION_NUM >= 130000
CompareCandidateDistances(const ListCell *a, const ListCell *b) CompareCandidateDistances(const ListCell *a, const ListCell *b)
{ {
HnswCandidate *hca = lfirst(a); HnswCandidate *hca = lfirst(a);
HnswCandidate *hcb = lfirst(b); HnswCandidate *hcb = lfirst(b);
#else
CompareCandidateDistances(const void *a, const void *b)
{
HnswCandidate *hca = lfirst(*(ListCell **) a);
HnswCandidate *hcb = lfirst(*(ListCell **) b);
#endif
if (hca->distance < hcb->distance) if (hca->distance < hcb->distance)
return 1; return 1;
@@ -979,17 +959,10 @@ CompareCandidateDistances(const void *a, const void *b)
* Compare candidate distances with offset tie-breaker * Compare candidate distances with offset tie-breaker
*/ */
static int static int
#if PG_VERSION_NUM >= 130000
CompareCandidateDistancesOffset(const ListCell *a, const ListCell *b) CompareCandidateDistancesOffset(const ListCell *a, const ListCell *b)
{ {
HnswCandidate *hca = lfirst(a); HnswCandidate *hca = lfirst(a);
HnswCandidate *hcb = lfirst(b); HnswCandidate *hcb = lfirst(b);
#else
CompareCandidateDistancesOffset(const void *a, const void *b)
{
HnswCandidate *hca = lfirst(*(ListCell **) a);
HnswCandidate *hcb = lfirst(*(ListCell **) b);
#endif
if (hca->distance < hcb->distance) if (hca->distance < hcb->distance)
return 1; return 1;
@@ -1271,7 +1244,6 @@ RemoveElements(char *base, List *w, HnswElement skipElement)
return w2; return w2;
} }
#if PG_VERSION_NUM >= 130000
/* /*
* Precompute hash * Precompute hash
*/ */
@@ -1287,7 +1259,6 @@ PrecomputeHash(char *base, HnswElement element)
else else
element->hash = hash_offset(HnswPtrOffset(ptr)); element->hash = hash_offset(HnswPtrOffset(ptr));
} }
#endif
/* /*
* Algorithm 1 from paper * Algorithm 1 from paper
@@ -1302,11 +1273,9 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
Datum q = HnswGetValue(base, element); Datum q = HnswGetValue(base, element);
HnswElement skipElement = existing ? element : NULL; HnswElement skipElement = existing ? element : NULL;
#if PG_VERSION_NUM >= 130000
/* Precompute hash */ /* Precompute hash */
if (index == NULL) if (index == NULL)
PrecomputeHash(base, element); PrecomputeHash(base, element);
#endif
/* No neighbors if no entry point */ /* No neighbors if no entry point */
if (entryPoint == NULL) if (entryPoint == NULL)

View File

@@ -26,12 +26,6 @@
#include "pgstat.h" #include "pgstat.h"
#endif #endif
#if PG_VERSION_NUM >= 130000
#define CALLBACK_ITEM_POINTER ItemPointer tid
#else
#define CALLBACK_ITEM_POINTER HeapTuple hup
#endif
#if PG_VERSION_NUM >= 140000 #if PG_VERSION_NUM >= 140000
#include "utils/backend_status.h" #include "utils/backend_status.h"
#include "utils/wait_event.h" #include "utils/wait_event.h"
@@ -96,7 +90,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
* Callback for sampling * Callback for sampling
*/ */
static void static void
SampleCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values, SampleCallback(Relation index, ItemPointer tid, Datum *values,
bool *isnull, bool tupleIsAlive, void *state) bool *isnull, bool tupleIsAlive, void *state)
{ {
IvfflatBuildState *buildstate = (IvfflatBuildState *) state; IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
@@ -207,16 +201,12 @@ AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState
* Callback for table_index_build_scan * Callback for table_index_build_scan
*/ */
static void static void
BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values, BuildCallback(Relation index, ItemPointer tid, Datum *values,
bool *isnull, bool tupleIsAlive, void *state) bool *isnull, bool tupleIsAlive, void *state)
{ {
IvfflatBuildState *buildstate = (IvfflatBuildState *) state; IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
MemoryContext oldCtx; MemoryContext oldCtx;
#if PG_VERSION_NUM < 130000
ItemPointer tid = &hup->t_self;
#endif
/* Skip nulls */ /* Skip nulls */
if (isnull[0]) if (isnull[0])
return; return;

View File

@@ -27,11 +27,7 @@ IvfflatInit(void)
{ {
ivfflat_relopt_kind = add_reloption_kind(); ivfflat_relopt_kind = add_reloption_kind();
add_int_reloption(ivfflat_relopt_kind, "lists", "Number of inverted lists", add_int_reloption(ivfflat_relopt_kind, "lists", "Number of inverted lists",
IVFFLAT_DEFAULT_LISTS, IVFFLAT_MIN_LISTS, IVFFLAT_MAX_LISTS IVFFLAT_DEFAULT_LISTS, IVFFLAT_MIN_LISTS, IVFFLAT_MAX_LISTS, AccessExclusiveLock);
#if PG_VERSION_NUM >= 130000
,AccessExclusiveLock
#endif
);
DefineCustomIntVariable("ivfflat.probes", "Sets the number of probes", DefineCustomIntVariable("ivfflat.probes", "Sets the number of probes",
"Valid range is 1..lists.", &ivfflat_probes, "Valid range is 1..lists.", &ivfflat_probes,
@@ -149,23 +145,10 @@ ivfflatoptions(Datum reloptions, bool validate)
{"lists", RELOPT_TYPE_INT, offsetof(IvfflatOptions, lists)}, {"lists", RELOPT_TYPE_INT, offsetof(IvfflatOptions, lists)},
}; };
#if PG_VERSION_NUM >= 130000
return (bytea *) build_reloptions(reloptions, validate, return (bytea *) build_reloptions(reloptions, validate,
ivfflat_relopt_kind, ivfflat_relopt_kind,
sizeof(IvfflatOptions), sizeof(IvfflatOptions),
tab, lengthof(tab)); tab, lengthof(tab));
#else
relopt_value *options;
int numoptions;
IvfflatOptions *rdopts;
options = parseRelOptions(reloptions, validate, ivfflat_relopt_kind, &numoptions);
rdopts = allocateReloptStruct(sizeof(IvfflatOptions), options, numoptions);
fillRelOptions((void *) rdopts, sizeof(IvfflatOptions), options, numoptions,
validate, tab, lengthof(tab));
return (bytea *) rdopts;
#endif
} }
/* /*
@@ -190,9 +173,7 @@ ivfflathandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0; amroutine->amstrategies = 0;
amroutine->amsupport = 5; amroutine->amsupport = 5;
#if PG_VERSION_NUM >= 130000
amroutine->amoptsprocnum = 0; amroutine->amoptsprocnum = 0;
#endif
amroutine->amcanorder = false; amroutine->amcanorder = false;
amroutine->amcanorderbyop = true; amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false; /* can change direction mid-scan */ amroutine->amcanbackward = false; /* can change direction mid-scan */
@@ -209,15 +190,11 @@ ivfflathandler(PG_FUNCTION_ARGS)
amroutine->amcanbuildparallel = true; amroutine->amcanbuildparallel = true;
#endif #endif
amroutine->amcaninclude = false; amroutine->amcaninclude = false;
#if PG_VERSION_NUM >= 130000
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */ amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
#endif
#if PG_VERSION_NUM >= 160000 #if PG_VERSION_NUM >= 160000
amroutine->amsummarizing = false; amroutine->amsummarizing = false;
#endif #endif
#if PG_VERSION_NUM >= 130000
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL; amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
#endif
amroutine->amkeytype = InvalidOid; amroutine->amkeytype = InvalidOid;
/* Interface functions */ /* Interface functions */

View File

@@ -151,12 +151,8 @@ RandomCenters(Relation index, VectorArray centers, const IvfflatTypeInfo * typeI
static void static void
ShowMemoryUsage(MemoryContext context, Size estimatedSize) ShowMemoryUsage(MemoryContext context, Size estimatedSize)
{ {
#if PG_VERSION_NUM >= 130000
elog(INFO, "total memory: %zu MB", elog(INFO, "total memory: %zu MB",
MemoryContextMemAllocated(context, true) / (1024 * 1024)); MemoryContextMemAllocated(context, true) / (1024 * 1024));
#else
MemoryContextStats(context);
#endif
elog(INFO, "estimated memory: %zu MB", estimatedSize / (1024 * 1024)); elog(INFO, "estimated memory: %zu MB", estimatedSize / (1024 * 1024));
} }
#endif #endif

View File

@@ -292,14 +292,7 @@ ivfflatrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int
IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque; IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque;
if (!so->first) if (!so->first)
{
#if PG_VERSION_NUM >= 130000
tuplesort_reset(so->sortstate); tuplesort_reset(so->sortstate);
#else
tuplesort_end(so->sortstate);
so->sortstate = InitScanSortState(so->tupdesc);
#endif
}
so->first = true; so->first = true;
pairingheap_reset(so->listQueue); pairingheap_reset(so->listQueue);
@@ -346,7 +339,7 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
IvfflatBench("GetScanItems", GetScanItems(scan, value)); IvfflatBench("GetScanItems", GetScanItems(scan, value));
so->first = false; so->first = false;
#if defined(IVFFLAT_MEMORY) && PG_VERSION_NUM >= 130000 #if defined(IVFFLAT_MEMORY)
elog(INFO, "memory: %zu MB", MemoryContextMemAllocated(CurrentMemoryContext, true) / (1024 * 1024)); elog(INFO, "memory: %zu MB", MemoryContextMemAllocated(CurrentMemoryContext, true) / (1024 * 1024));
#endif #endif

View File

@@ -26,11 +26,6 @@
#include "varatt.h" #include "varatt.h"
#endif #endif
#if PG_VERSION_NUM < 130000
#define TYPALIGN_DOUBLE 'd'
#define TYPALIGN_INT 'i'
#endif
#define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1) #define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1)
#define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1)) #define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1))