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
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)
+;
+*/
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to