Re: (check) constraints on composite type - Mailing list pgsql-general

From David Johnston
Subject Re: (check) constraints on composite type
Date
Msg-id 01fe01cccfdc$56b50380$041f0a80$@yahoo.com
Whole thread Raw
In response to Re: (check) constraints on composite type  (Ralph Graulich <maillist@shauny.de>)
List pgsql-general
-----Original Message-----
From: pgsql-general-owner@postgresql.org
[mailto:pgsql-general-owner@postgresql.org] On Behalf Of Ralph Graulich
Sent: Tuesday, January 10, 2012 3:47 PM
To: David Johnston
Cc: pgsql-general@postgresql.org
Subject: Re: [GENERAL] (check) constraints on composite type

Let's assume I have three fields in my custom type:

field1 varchar(4)   // or text
field2 smallint
field3 char(1)      // or text

Now I want to add a complex check constraint involving the combination of
those three fields, written in pseudo code:

field1 IN ('A') && field2 BETWEEN 1 AND 120 && field3 IS NULL
field1 IN ('B') && field2 BETWEEN 1 AND 40 && field3 IS NULL
field1 IN ('TFnr') && field3 BETWEEN 1600 AND 2200 && field3 IN ('a', 'b',
'c')
field1 IS NULL & field2 = 1 & field3 IN ('a'..'z') OR field3 IS NULL
...            & field2 = 2 & field3 IN ('a'..'h') OR field3 IS NULL
...            & field2 = 3 & field3 IN ('a'..'o', 'Z') OR field3 IS NULL

and so on (the problem is actually far more complex, just to give a hint
of.). My point is putting all the validation of data into the database
itself, so just having to care for changes at one place and making sure data
and data domains of valid data are tightly integrated, which I favor.

Regards,
     Ralph

----------------------------------------------------------------------------
---

I would suggest trying to make your constraints as "template" as possible
and then create a table that provides inputs for each of the constraint
"parameters".  You include a field which flags the constraint as either
"require" or "disallow" and then you write a function that iterates over the
table and compares each of the constraints against the value and ensure that
at least of the "require" constraints pass and that all the "disallow"
constraints fail.

RegularExpressions will likely come in handy and you will probably need to
create "field_n_can_be_null" booleans to allow them explicitly.

The implementation I've used is to "CROSS JOIN" the value and the constraint
table and then use ARRAY_AGG to create multiple arrays for the various test
results (the specific set of arrays varies depending on your needs).  I've
done this with a single text field check using reqularexpressions and the
results work fairly well (ignoring performance concerns which have not yet
been a factor due to the lite load I experience).  For an exclusion
constraint I add "Exclude" if the constraint matches otherwise I add
"Ignore"; for inclusion constraints I add "Include" for matches or "Ignore";
I then exclude if "Exclude" = ANY(match_results) OR "Ignore" =
ALL(match_results); otherwise there has to be at least one "Include" and
thus the check passes.

For example:

SELECT val, CASE WHEN 'Exclude' = ANY(results) THEN 'Fail - Excluded'
          WHEN 'Ignore' = ALL(results) THEN 'Fail - No Includes
Match'
          WHEN 'Include' = ANY(results) THEN 'Include'
          ELSE 'Fail - Not Possible As Coded But Just In Case' END
AS test_outcome
FROM (
SELECT val, ARRAY_AGG( CASE WHEN val ~ pattern THEN CASE WHEN include THEN
'Include' ELSE 'Exclude' END ELSE 'Ignore' END ) AS results
FROM (VALUES ('value to check')) vals (val)
CROSS JOIN (VALUES ('test pattern',true) [, ...] ) tests (pattern, include)
GROUP BY val
) val_test

David J.




pgsql-general by date:

Previous
From: Ralph Graulich
Date:
Subject: Re: (check) constraints on composite type
Next
From: Filip Rembiałkowski
Date:
Subject: Re: string = any()