TL;DR:
I am trying to pass the names and values of one or more columns into a function in Postgres, which is used in a check constraint. This seems to work properly, until the column names need quoting (i.e., contain upper case letters), when I get 'column "x" does not exist' messages. If I quote the identifier the behaviour of the function changes.
I can't seem to find a way to reference both the column name and it's value in a function, called from a check constraint, if the column identifier needs quoting.
The full story:
In Postgres, I am trying to emulate a Unique constraint using a user defined function and a Check constraint.
I want to do this because I need a "conditional" Unique constraint, where the uniqueness may not be enforced if other conditions in the Check are/aren't met.
(I appreciate that an obvious answer might be "You don't want to do this", or "This is a bad idea", but I would appreciate answers that instead resolve the issue I'm having more directly.)
Current attempt:
As there may be more than one column included as part of the unique, I created function that accepts a table, an array of columns and an array of values.
CREATE OR REPLACE FUNCTION is_unique(_table text, _columns text[], _values text[]) RETURNS boolean AS
$$
DECLARE
result integer;
statement text = 'SELECT (EXISTS (SELECT 1 FROM ' || quote_ident(_table) || ' WHERE ';
first boolean = true;
BEGIN
FOR i IN array_lower(_columns,1)..array_upper(_columns,1) LOOP
IF first THEN
statement = statement || quote_ident(_columns[i]) || '=' || _values[i];
first = false;
ELSE
statement = statement || ' AND '|| quote_ident(_columns[i]) || '=' || _values[i];
END IF;
END LOOP;
statement = statement || '))::int';
EXECUTE statement INTO result;
RETURN NOT result::boolean;
END
$$
LANGUAGE 'plpgsql';
What I am trying to do in this function is form a statement of the form:
SELECT 1 FROM _table WHERE _column[i]=_value[i] AND ...
This can then be used as part of a Check constraint, such as:
CHECK (is_unique('sometable'::text,'{somecolumn}'::text[],ARRAY[somecolumn]::text[]))
What is happening:
This structure appears to work when used with columns that do not need to be quoted, but otherwise the behaviour seems to break. Once I insert a single row, I am not able to insert another row even if the value is unique. I believe the problem is the value of the column is possibly being compared to itself, or the identifiers are being compared.
Does anyone have any suggestions as to how I should change my code to resolve this issue? Unfortunately, coping with quoted identifiers is important in this case.
ALTER TABLE "test table" ADD CHECK (is_unique('test table', array['int col'], array['int col']));there is a syntax error on the right half of the =. How can I resolve this?LANGUAGE plpgsqlis correct.