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