Forum OpenACS Q&A: user_contact_summary fix (followup to user_demographic_summary)

The user_demographic_summary posting, which also featured a fix of mine, was the "inspiration" for this fix. (Thanks to all who contributed!)

First, I should definitely mention that my fix posted for user_demographic_summary needs to have all the "comment lines" (any lines beginning with "--") stripped, or it won't work, as I discovered a little while ago.

Next, there are a couple of things I discovered when I started to troubleshoot user_contact_summary:

  1. Since my ha_line2 (or, equivalently, wa_line2) field was null, when I tried concatenating just the first two items, the ha_line1 field suddenly "disappeared"! That's why it was necessary to add the "case" stuff to ha_line2 and wa_line2. (And it would actually be necessary to add them to any of the other fields, as well, unless you make the assumption, as I did, that if any of the other fields are populated, then all the rest of them are, and, if not, you don't really care, because all you'd get would be a partial address, which is kind of sloppy. If you do care, then the rest of the fix is left for you as an exercise. 😊
  2. The error message I was getting:
    ERROR: Unable to identify an operator '||' for types 'varchar' and 'bpchar'
    You will have to retype this query using an explicit cast
    did not occur when I tried adding each field progressively to the concatenation -- until, that is, the very last field. Well, guess what? The last field is a char(2), so the error message appears to have been complaining about an incompatibility between the concatenated varchar's and the final char!!!

Anyhow, without further ado, here is the fix:

drop function user_contact_summary (integer);
create function user_contact_summary (integer)
returns varchar
as '
declare
  contact_row           users_contact%ROWTYPE;
  v_return              varchar(4000);
begin
  select * into contact_row from users_contact where user_id = $1;
 
  IF NOT FOUND then return '''';
  END IF;
  IF contact_row.m_address = ''w'' THEN
    v_return:= contact_row.wa_line1 || '' '' ||
      case when contact_row.wa_line2 is null then ''''
      else contact_row.wa_line2 end || '' '' || 
      contact_row.wa_city || '', '' ||
      contact_row.wa_state || '' '' ||
      contact_row.wa_postal_code || '' '' ||
      cast(contact_row.wa_country_code as varchar(2));
  ELSE
    v_return:= contact_row.ha_line1 || '' '' ||
      case when contact_row.ha_line2 is null then ''''
      else contact_row.ha_line2 end || '' '' || 
      contact_row.ha_city || '', '' ||
      contact_row.ha_state || '' '' ||
      contact_row.ha_postal_code || '' '' ||
      cast(contact_row.ha_country_code as varchar(2));
  END IF;
 
  return v_return;
end;
' language 'plpgsql';

Presumably this should make it possible for me to test the original code for view-verbose.tcl successfully now, since that was one of the modules that was broken because of user_contact_summary.

OK, here's an answer to part of your observations...

'string' || NULL does indeed return NULL, and it should, according to
SQL92 rules.  NULLification is consistent for all types, and *any*
operator with a NULL operand should return NULL as a result.  NULL
means "has no value".

Now, as it turns out Oracle equates '' - the empty string - with NULL,
the value-with-no-value.  This is *wrong* and indeed, Oracle's own
documentation claims they may fix it someday and that code shouldn't
rely on this bug.

Unfortunately, ACS Classic (the Oracle version) does depend on this in
many places.  It treats NULL and '' as synonyms, i.e. varchar fields
aren't given an explicit "default ''" clause if they're going to be
used as operands to the concatenation operator, or alternatively don't
check via "case" as you suggest doing.

This works because in Oracle NULL || 'abc' returns 'abc', which makes
as much sense as "1 + NULL" returning "1", or "1 > NULL" returning
"true" (or "false", for that matter).

Grrr...these are hard to spot "by inspection" when porting queries.
We should try to implement a consistent approach to the problem for
the ACS 4.0 port.  It's unrealistic to expect aD to avoid this
'feature', because Oracle gives absolutely no help via warnings, etc,
when you take advantage of this mistreatment of '' as NULL.