Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IS NOT NULL test for a record does not return TRUE when variable is set

Using a plpgsql procedure to extract a record if it exists, and then if it does, do something with it.

The variable is a rowtype:

my_var my_table%rowtype;

I populate it with a SQL statement:

select * from my_table where owner_id = 6 into my_var;

I know it definitely has the row:

raise notice 'my_var is %', my_var;

Returns:

NOTICE:  my_var is (383,6,10)

But now I want to test that it got the record and BOTH of these if conditions fail:

if my_var is null then
  raise notice 'IT IS NULL';
end if;
if my_var is not null then
  raise notice 'IT IS NOT NULL';
end if;

Neither of these raises appear in my messages log - it just never enters the blocks. What's the correct way to test if you received a row from a SELECT * INTO?

like image 877
Don Pflaster Avatar asked Jan 11 '23 05:01

Don Pflaster


1 Answers

I see two possible reasons, why ...

Neither of these raises appear in my messages log

Not logged

Firstly, a NOTICE is not normally written to the database log with default settings. The manual:

log_min_messages (enum)

Controls which message levels are written to the server log. Valid values are DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, WARNING, ERROR, LOG, FATAL, and PANIC. (...)
The default is WARNING. Note that LOG has a different rank here than in client_min_messages.

Bold emphasis mine. Also note the different default (NOTICE) for client_min_messages (preceding item in the manual).

Invalid test

Secondly, consider how a row expression is evaluated. A test row_variable IS NULL returns TRUE if (and only if) every single element is NULL. Consider:

SELECT (1, NULL) IS NULL     AS a  -- FALSE
     , (1, NULL) IS NOT NULL AS b  -- also FALSE!

Both expressions return FALSE. In other words, a row (or record) variable (1, NULL) is neither NULL, nor is it NOT NULL. Therefore, both of your tests fail.

sqlfiddle with more details

Related:

  • NOT NULL constraint over a set of columns

You can even assign a record variable with NULL (rec := NULL), which results in every element being NULL - if the type is a well-known row type. Otherwise, we are dealing with an anonymous record and the structure is undefined and you cannot access elements to begin with. But that's not the case with a rowtype like in your example (which is always well-known).

Solution: FOUND

What's the correct way to test if you received a row from a SELECT * INTO?

You have to consider that the row could be NULL, even if it was assigned. The query could very well have returned a bunch of NULL values (if the table definition in your query allows NULL values).

There is a simple and secure approach. Use GET DIAGNOSTICS or, where applicable, the special variable FOUND:

SELECT * FROM my_table WHERE owner_id = 6 INTO my_var;

IF NOT FOUND THEN
   RAISE NOTICE 'Query did not return a row!';
END IF;

Details in the manual.

like image 173
Erwin Brandstetter Avatar answered Jan 17 '23 15:01

Erwin Brandstetter