Functions to Validate User’s Input

PostgreSQL 16 introduces a couple of new embedded functions: [pg_input_is_valid](https://www.postgresql.org/docs/16/functions-info.html#FUNCTIONS-INFO-VALIDITY-TABLE){:target="_blank"} and [pg_input_error_info](https://www.postgresql.org/docs/16/functions-info.html#FUNCTIONS-INFO-VALIDITY-TABLE){:target="_blank"}. Both the functions accepts a couple of strings, the first one being the value to be validated, and the second one being the type to which you want to cast the value. This can be useful because you can check ahead of time if a given data type (expressed as a string) can be converted into a specific data type without raising an exception. The first use case that comes into my mind is the conversion of some stringified date into an effective date, for example when importing data from an external source like a text file. Let’s see this in action:

testdb=> select * from pg_input_is_valid( '1978-07-19', 'timestamp' );
 pg_input_is_valid
-------------------
 t
(1 row)

testdb=> select * from pg_input_error_info( '1978-07-19', 'timestamp' );
 message | detail | hint | sql_error_code
---------+--------+------+----------------
         |        |      |
(1 row)




With a valid date, the pg_input_is_valid function returns true and the pg_input_error_info does not return any row. But what happens if the date is in a wrong format?

testdb=> \x
Expanded display is on.
testdb=> select * from pg_input_is_valid( '1978-19-07', 'timestamp' );
-[ RECORD 1 ]-----+--
pg_input_is_valid | f

testdb=> select * from pg_input_error_info( '1978-19-07', 'timestamp' );
-[ RECORD 1 ]--+--------------------------------------------------
message        | date/time field value out of range: "1978-19-07"
detail         |
hint           | Perhaps you need a different "datestyle" setting.
sql_error_code | 22008



As you can see from the above example, passing a wrong date/time format raises the error, and thanks to these functions we are now able to discover ahead of its usage what the problem could be. Another example, just to clarify more:

testdb=> select pg_input_error_info( '4 months', 'interval' );
-[ RECORD 1 ]-------+------
pg_input_error_info | (,,,)

testdb=> select pg_input_error_info( '4 mesi', 'interval' );
-[ RECORD 1 ]-------+---------------------------------------------------------------
pg_input_error_info | ("invalid input syntax for type interval: ""4 mesi""",,,22007)



It is therefore quite easy to use such checks into your own function:

testdb=> CREATE OR REPLACE FUNCTION input_check( t text[] )RETURNS int
AS $CODE$
DECLARE
  current text; ok int := 0;  e text;
BEGIN
  FOREACH current IN ARRAY t LOOP
    IF pg_input_is_valid( current, 'date' ) THEN
       ok := ok + 1;
    ELSE
       SELECT message
       INTO e
       FROM pg_input_error_info( current, 'date' );
       RAISE DEBUG 'Skipping [%] because is not valid: %', current, e;
   END IF;
  END LOOP;

  RETURN ok;
END
$CODE$
LANGUAGE plpgsql;
CREATE FUNCTION




that, once invoked with the following input, provides the result as shown below:

testdb=> select input_check( array[ '2023-09-25', 'luca', '0001-01-01', 'Sat 23 Sep 2023', 'Feb 30 2023' ] );
DEBUG:  Skipping [luca] because is not valid: invalid input syntax for type date: "luca"
DEBUG:  Skipping [Feb 30 2023] because is not valid: date/time field value out of range: "Feb 30 2023"
 input_check
-------------
           3
(1 row)



The article Functions to Validate User's Input has been posted by Luca Ferrari on September 25, 2023