From 20fba44412e8ef1bb4cd5b051b9d7e82618a6d93 Mon Sep 17 00:00:00 2001 From: John Naylor Date: Wed, 10 Aug 2022 17:19:24 +0700 Subject: [PATCH v4 2/2] Teach catcache to store structs for pg_cast --- src/backend/parser/parse_coerce.c | 6 +-- src/backend/utils/cache/catcache.c | 73 ++++++++++++++++++++---------- src/include/access/htup_details.h | 2 + 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 39b7e5707b..07a1b047e3 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -3056,7 +3056,7 @@ IsBinaryCoercible(Oid srctype, Oid targettype) ObjectIdGetDatum(targettype)); if (!HeapTupleIsValid(tuple)) return false; /* no cast */ - castForm = (Form_pg_cast) GETSTRUCT(tuple); + castForm = GETSTRUCT_NEW(pg_cast, tuple); result = (castForm->castmethod == COERCION_METHOD_BINARY && castForm->castcontext == COERCION_CODE_IMPLICIT); @@ -3120,7 +3120,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, if (HeapTupleIsValid(tuple)) { - Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); + Form_pg_cast castForm = GETSTRUCT_NEW(pg_cast, tuple); CoercionContext castcontext; /* convert char value for castcontext to CoercionContext enum */ @@ -3287,7 +3287,7 @@ find_typmod_coercion_function(Oid typeId, if (HeapTupleIsValid(tuple)) { - Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); + Form_pg_cast castForm = GETSTRUCT_NEW(pg_cast, tuple); *funcid = castForm->castfunc; ReleaseSysCache(tuple); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index b1287bb6a0..8ddc109052 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -21,6 +21,7 @@ #include "access/table.h" #include "access/valid.h" #include "access/xact.h" +#include "catalog/pg_cast.h" // fixme #include "catalog/pg_collation.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" @@ -2158,6 +2159,42 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments, return ct; } +// WIP: generated functions would look like this and be called through a pointer +// FIXME: ct->tuple is no longer a real tuple +// XXX: for now assume the caller has switched to the right memory context +static CatCTup * +CatCArrayGetStruct_pg_cast(HeapTuple pg_cast_tuple, CatCTup *ct, Datum *values, bool *isnull) +{ + Form_pg_cast pg_cast_struct; + + /* Allocate memory for CatCTup and the cached struct in one go */ + ct = (CatCTup *) palloc(sizeof(CatCTup) + + MAXIMUM_ALIGNOF + sizeof(FormData_pg_cast)); + + /* copy the identification info */ + // WIP: for caches we only need t_self, can we just have that as a + // separate field in CatCTup? + ct->tuple.t_len = pg_cast_tuple->t_len; + ct->tuple.t_self = pg_cast_tuple->t_self; + ct->tuple.t_tableOid = pg_cast_tuple->t_tableOid; + + // WIP: treat t_data as a pointer to the struct + ct->tuple.t_data = (HeapTupleHeader) + MAXALIGN(((char *) ct) + sizeof(CatCTup)); + pg_cast_struct = (Form_pg_cast) ct->tuple.t_data; + + /* copy tuple contents */ + // WIP: we can just assign because there are no varlen attributes + pg_cast_struct->oid = DatumGetObjectId(values[Anum_pg_cast_oid - 1]); + pg_cast_struct->castsource = DatumGetObjectId(values[Anum_pg_cast_castsource - 1]); + pg_cast_struct->casttarget = DatumGetObjectId(values[Anum_pg_cast_casttarget - 1]); + pg_cast_struct->castfunc = DatumGetObjectId(values[Anum_pg_cast_castfunc - 1]); + pg_cast_struct->castcontext = DatumGetChar(values[Anum_pg_cast_castcontext - 1]); + pg_cast_struct->castmethod = DatumGetChar(values[Anum_pg_cast_castmethod - 1]); + + return ct; +} + /* * CatalogCacheCreateEntry * Create a new CatCTup entry, copying the given HeapTuple and other @@ -2176,6 +2213,8 @@ CatalogCacheCreateEntry_STRUCT(CatCache *cache, HeapTuple ntp, Datum *arguments, if (ntp) { int i; + Datum *values; + bool *isnull; Assert(!negative); @@ -2191,37 +2230,25 @@ CatalogCacheCreateEntry_STRUCT(CatCache *cache, HeapTuple ntp, Datum *arguments, else dtp = ntp; - /* Allocate memory for CatCTup and the cached tuple in one go */ - oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + /* deform the tuple */ + values = palloc(cache->cc_tupdesc->natts * sizeof(Datum)); + isnull = palloc(cache->cc_tupdesc->natts * sizeof(bool)); + heap_deform_tuple(dtp, cache->cc_tupdesc, values, isnull); - ct = (CatCTup *) palloc(sizeof(CatCTup) + - MAXIMUM_ALIGNOF + dtp->t_len); - ct->tuple.t_len = dtp->t_len; - ct->tuple.t_self = dtp->t_self; - ct->tuple.t_tableOid = dtp->t_tableOid; - ct->tuple.t_data = (HeapTupleHeader) - MAXALIGN(((char *) ct) + sizeof(CatCTup)); - /* copy tuple contents */ - memcpy((char *) ct->tuple.t_data, - (const char *) dtp->t_data, - dtp->t_len); + /* copy the tuple-as-struct into the cache */ + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + ct = CatCArrayGetStruct_pg_cast(dtp, ct, values, isnull); MemoryContextSwitchTo(oldcxt); if (dtp != ntp) heap_freetuple(dtp); - /* extract keys - they'll point into the tuple if not by-value */ + /* extract keys */ + // FIXME: this is broken for varlen attributes -- need a way to copy them for (i = 0; i < cache->cc_nkeys; i++) { - Datum atp; - bool isnull; - - atp = heap_getattr(&ct->tuple, - cache->cc_keyno[i], - cache->cc_tupdesc, - &isnull); - Assert(!isnull); - ct->keys[i] = atp; + Assert(!isnull[cache->cc_keyno[i]]); + ct->keys[i] = values[cache->cc_keyno[i]]; } } else diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h index 51a60eda08..7928c2d133 100644 --- a/src/include/access/htup_details.h +++ b/src/include/access/htup_details.h @@ -652,6 +652,8 @@ struct MinimalTupleData * GETSTRUCT - given a HeapTuple pointer, return address of the user data */ #define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff) +// FIXME +#define GETSTRUCT_NEW(CAT, TUP) ((Form_##CAT) ((char *) (TUP)->t_data)) /* * Accessor macros to be used with HeapTuple pointers. -- 2.36.1