On Tue, Jul 19, 2022 at 10:57 PM Andres Freund <and...@anarazel.de> wrote:
>
> Hi,
>
> On 2022-07-19 14:30:34 +0700, John Naylor wrote:
> > I'm thinking where the first few attributes are fixed length, not null,
and
> > (because of AIX) not double-aligned, we can do a single memcpy on
multiple
> > columns at once. That will still be a common pattern after namedata is
> > varlen. Otherwise, use helper functions/macros similar to the above but
> > instead of passing a tuple descriptor, use info we have at compile time.
>
> I think that might be over-optimizing things. I don't think we do these
> conversions at a rate that's high enough to warrant it - the common stuff
> should be in relcache etc.  It's possible that we might want to optimize
the
> catcache case specifically - but that'd be more optimizing memory usage
than
> "conversion" imo.

Okay, here is a hackish experiment that applies on top of v2 but also
invalidates some of that earlier work. Since there is already a pg_cast.c,
I demoed a new function there which looks like this:

void
Deform_pg_cast_tuple(Form_pg_cast pg_cast_struct, HeapTuple pg_cast_tuple,
TupleDesc pg_cast_desc)
{
    Datum values[Natts_pg_cast];
    bool isnull[Natts_pg_cast];

    heap_deform_tuple(pg_cast_tuple, pg_cast_desc, values, isnull);

    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]);
}

For the general case we can use pg_*_deform.c or something like that, with
extern declarations in the main headers. To get this to work, I had to add
a couple pointless table open/close calls to get the tuple descriptor,
since currently the whole tuple is stored in the syscache, but that's not
good even as a temporary measure. Storing the full struct in the syscache
is a good future step, as noted upthread, but to get there without a bunch
more churn, maybe the above function can copy the tuple descriptor into a
local stack variable from an expanded version of schemapg.h. Once the
deformed structs are stored in caches, I imagine most of the times we want
to deform are when we have the table open, and we can pass the descriptor
as above without additional code.

--
John Naylor
EDB: http://www.enterprisedb.com
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 66e98daad1..cdc26b1118 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -3028,7 +3028,7 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
 					break;
 				}
 
-				GETSTRUCT_MEMCPY(pg_cast, &castForm, tup);
+				GETSTRUCT_MEMCPY(pg_cast, &castForm, tup, RelationGetDescr(castDesc));
 
 				appendStringInfo(&buffer, _("cast from %s to %s"),
 								 format_type_be(castForm.castsource),
@@ -4870,7 +4870,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 					break;
 				}
 
-				GETSTRUCT_MEMCPY(pg_cast, &castForm, tup);
+				GETSTRUCT_MEMCPY(pg_cast, &castForm, tup, RelationGetDescr(castRel));
 
 				appendStringInfo(&buffer, "(%s AS %s)",
 								 format_type_be_qualified(castForm.castsource),
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
index 1812bb7fcc..83379f3505 100644
--- a/src/backend/catalog/pg_cast.c
+++ b/src/backend/catalog/pg_cast.c
@@ -27,6 +27,23 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
+// WIP: #include from generated .c file?
+void
+Deform_pg_cast_tuple(Form_pg_cast pg_cast_struct, HeapTuple pg_cast_tuple, TupleDesc pg_cast_desc)
+{
+	Datum			values[Natts_pg_cast];
+	bool			isnull[Natts_pg_cast];
+
+	heap_deform_tuple(pg_cast_tuple, pg_cast_desc, values, isnull);
+
+	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]);
+}
+
 /*
  * ----------------------------------------------------------------
  *		CastCreate
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 0bd90fd5e8..f4bac9d171 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -30,6 +30,9 @@
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 #include "utils/typcache.h"
+// WIP
+#include "access/table.h"
+#include "utils/rel.h"
 
 
 static Node *coerce_type_typmod(Node *node,
@@ -2997,6 +3000,7 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
 	HeapTuple	tuple;
 	FormData_pg_cast castForm;
 	bool		result;
+	Relation castDesc;
 
 	/* Fast path if same type */
 	if (srctype == targettype)
@@ -3056,12 +3060,15 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
 							ObjectIdGetDatum(targettype));
 	if (!HeapTupleIsValid(tuple))
 		return false;			/* no cast */
-	GETSTRUCT_MEMCPY(pg_cast, &castForm, tuple);
+
+	castDesc = table_open(CastRelationId, AccessShareLock); // WIP: this would be better with generated "compact tuple descriptors"
+	GETSTRUCT_MEMCPY(pg_cast, &castForm, tuple, RelationGetDescr(castDesc));
 
 	result = (castForm.castmethod == COERCION_METHOD_BINARY &&
 			  castForm.castcontext == COERCION_CODE_IMPLICIT);
 
 	ReleaseSysCache(tuple);
+	table_close(castDesc, AccessShareLock); // FIXME
 
 	return result;
 }
@@ -3123,7 +3130,9 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
 		FormData_pg_cast castForm;
 		CoercionContext castcontext;
 
-		GETSTRUCT_MEMCPY(pg_cast, &castForm, tuple);
+		// TODO: the syscache should have the deformed struct instead of the tuple
+		Relation castDesc = table_open(CastRelationId, AccessShareLock);
+		GETSTRUCT_MEMCPY(pg_cast, &castForm, tuple, RelationGetDescr(castDesc));
 
 		/* convert char value for castcontext to CoercionContext enum */
 		switch (castForm.castcontext)
@@ -3167,6 +3176,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
 		}
 
 		ReleaseSysCache(tuple);
+		table_close(castDesc, AccessShareLock); // FIXME
 	}
 	else
 	{
@@ -3290,10 +3300,12 @@ find_typmod_coercion_function(Oid typeId,
 	if (HeapTupleIsValid(tuple))
 	{
 		FormData_pg_cast castForm;
+		Relation castDesc = table_open(CastRelationId, AccessShareLock); // WIP
 
-		GETSTRUCT_MEMCPY(pg_cast, &castForm, tuple);
+		GETSTRUCT_MEMCPY(pg_cast, &castForm, tuple, RelationGetDescr(castDesc));
 		*funcid = castForm.castfunc;
 		ReleaseSysCache(tuple);
+		table_close(castDesc, AccessShareLock); // FIXME
 	}
 
 	if (!OidIsValid(*funcid))
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index f300030504..ac9ecabc52 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -657,7 +657,7 @@ struct MinimalTupleData
  * GETSTRUCT - given a HeapTuple pointer, deform the user data into the passed struct
  */
 // WIP: The _MEMCPY suffix is a temporary scaffold to allow partial progress one catalog at a time.
-#define GETSTRUCT_MEMCPY(CAT, STRUCT, TUP) Deform_##CAT##_tuple(STRUCT, (char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+#define GETSTRUCT_MEMCPY(CAT, STRUCT, TUP, DESC) Deform_##CAT##_tuple((STRUCT), (TUP), (DESC))
 
 /*
  * Accessor macros to be used with HeapTuple pointers.
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 4887ea16bd..fde557477a 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -56,9 +56,6 @@ CATALOG(pg_cast,2605,CastRelationId)
  */
 typedef FormData_pg_cast *Form_pg_cast;
 
-/* now that we've defined the struct type, include the deform function */
-#include "catalog/pg_cast_deform.h"
-
 DECLARE_UNIQUE_INDEX_PKEY(pg_cast_oid_index, 2660, CastOidIndexId, on pg_cast using btree(oid oid_ops));
 DECLARE_UNIQUE_INDEX(pg_cast_source_target_index, 2661, CastSourceTargetIndexId, on pg_cast using btree(castsource oid_ops, casttarget oid_ops));
 
@@ -94,6 +91,7 @@ typedef enum CoercionMethod
 
 #endif							/* EXPOSE_TO_CLIENT_CODE */
 
+extern void Deform_pg_cast_tuple(Form_pg_cast pg_cast_struct, HeapTuple pg_cast_tuple, TupleDesc pg_cast_desc);
 
 extern ObjectAddress CastCreate(Oid sourcetypeid,
 								Oid targettypeid,

Reply via email to