Question on "record type has not been registered"

Started by Jim Nasbyabout 9 years ago1 messages
#1Jim Nasby
Jim.Nasby@BlueTreble.com
1 attachment(s)

I'm working on a function(attached) that returns a bitmask of NULL
fields in a record. It works fine if I feed it a row directly, but fails
in this case:

select record_nulls(r), expected, CASE WHEN record_nulls(r) <> expected
THEN 'BAD' END AS bad, r
from (values(row(NULL,NULL,NULL,2,2,NULL,2,2), '11100100'::varbit),
(row(2),'0')
) v(r,expected)
;
ERROR: record type has not been registered

I'm not sure why this is failing; a simple SELECT * from that FROM
clause works fine. I also tried removing the second row in case the
mismatch of record types was the issue; that didn't help either.
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com
855-TREBLE2 (855-873-2532) mobile: 512-569-9461

Attachments:

record_nulls.difftext/plain; charset=UTF-8; name=record_nulls.diff; x-mac-creator=0; x-mac-type=0Download
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index 622bb88..cd185b8 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -25,6 +25,7 @@
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/typcache.h"
+#include "utils/varbit.h"
 
 
 /*
@@ -1818,3 +1819,79 @@ btrecordimagecmp(PG_FUNCTION_ARGS)
 {
 	PG_RETURN_INT32(record_image_cmp(fcinfo));
 }
+
+/*
+ * record_nulls: return null map as bit
+ */
+Datum
+record_nulls(PG_FUNCTION_ARGS)
+{
+	HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
+	Oid			tupType;
+	int32		tupTypmod;
+	TupleDesc	tupdesc;
+	HeapTupleData tuple;
+	int			ncolumns,
+				natts,
+				len,			/* varlena length of result */
+				attno;
+	bool		hasnulls;
+	bits8	   *bp;				/* ptr to null bitmap in tuple */
+	bits8		x = 0;
+	VarBit	   *result;			/* The resulting bit string */
+	bits8	   *r;				/* pointer into the result */
+
+	/* Extract type info from the tuple itself */
+	tupType = HeapTupleHeaderGetTypeId(rec);
+	tupTypmod = HeapTupleHeaderGetTypMod(rec);
+	tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+	natts = tupdesc->natts;
+
+	/* Build a temporary HeapTuple control structure */
+	/* XXX there's probably a cheaper way to do this... */
+	tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
+	ItemPointerSetInvalid(&(tuple.t_self));
+	tuple.t_tableOid = InvalidOid;
+	tuple.t_data = rec;
+	hasnulls = HeapTupleHasNulls(&tuple);
+
+	bp = rec->t_bits;		/* ptr to null bitmap in tuple */
+
+	/* Find how many columns are dropped */
+	ncolumns = natts;
+	if (hasnulls)
+		for (attno = 0; attno < natts; attno++)
+			if (tupdesc->attrs[attno]->attisdropped)
+				ncolumns--;
+
+	len = VARBITTOTALLEN(ncolumns);
+	/* set to 0 so that *r is always initialised and string is zero-padded */
+	result = (VarBit *) palloc0(len);
+	SET_VARSIZE(result, len);
+	VARBITLEN(result) = ncolumns;
+
+	/* If there are no NULLs then we're done */
+	if (hasnulls)
+	{
+		r = VARBITS(result);
+		x = HIGHBIT;
+		for (attno = 0; attno < natts; attno++)
+		{
+			/* Ignore dropped columns in datatype */
+			if (tupdesc->attrs[attno]->attisdropped)
+				continue;
+
+			if (att_isnull(attno, bp))
+				*r |= x;
+			x >>= 1;
+			if (x == 0)
+			{
+				x = HIGHBIT;
+				r++;
+			}
+		}
+	}
+
+	ReleaseTupleDesc(tupdesc);
+	PG_RETURN_VARBIT_P(result);
+}
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e2d08ba..092d5f4 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4949,6 +4949,10 @@ DATA(insert OID = 3186 (  record_image_ge	   PGNSP PGUID 12 1 0 0 0 f f f f t f
 DATA(insert OID = 3187 (  btrecordimagecmp	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 23 "2249 2249" _null_ _null_ _null_ _null_ _null_ btrecordimagecmp _null_ _null_ _null_ ));
 DESCR("less-equal-greater based on byte images");
 
+/* misc record functions */
+DATA(insert OID = 3343 (  record_nulls			PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 1560 "2249" _null_ _null_ _null_ _null_ _null_ record_nulls _null_ _null_ _null_ ));
+DESCR("bitmap of fields in a record that are NULL");
+
 /* Extensions */
 DATA(insert OID = 3082 (  pg_available_extensions		PGNSP PGUID 12 10 100 0 0 f f f f t t s s 0 0 2249 "" "{19,25,25}" "{o,o,o}" "{name,default_version,comment}" _null_ _null_ pg_available_extensions _null_ _null_ _null_ ));
 DESCR("list available extensions");
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 2ae212a..2b75658 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -718,6 +718,7 @@ extern Datum record_image_gt(PG_FUNCTION_ARGS);
 extern Datum record_image_le(PG_FUNCTION_ARGS);
 extern Datum record_image_ge(PG_FUNCTION_ARGS);
 extern Datum btrecordimagecmp(PG_FUNCTION_ARGS);
+extern Datum record_nulls(PG_FUNCTION_ARGS);
 
 /* ruleutils.c */
 extern bool quote_all_identifiers;
diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql
index a62dee2..d187205 100644
--- a/src/test/regress/sql/rowtypes.sql
+++ b/src/test/regress/sql/rowtypes.sql
@@ -310,3 +310,14 @@ with r(a,b) as
   (values (1,row(1,2)), (1,row(null,null)), (1,null),
           (null,row(1,2)), (null,row(null,null)), (null,null) )
 select r, r is null as isnull, r is not null as isnotnull from r;
+
+--
+-- Test record_nulls()
+--
+/* Currenly doesn't work because record_nulls only works with registered types
+select record_nulls(r), expected, CASE WHEN record_nulls(r) <> expected THEN 'BAD' END AS bad, r
+from (values(row(NULL,NULL,NULL,2,2,NULL,2,2), '11100100'::varbit),
+			(row(2),'0')
+		) v(r,expected)
+;
+*/