I have a function that can check if each element in an array is present in a given column of a table, and it works fine. I would now like to extend the function so that it can handle json/jsonb arrays as well as SQL arrays.
In this MWE (PSQL-11) the commented lines are psudo code for what I would like to do. Keeping them commented out lets you run the code but it then only works for SQL arrays.
CREATE TABLE IF NOT EXISTS animals
(
animal text COLLATE pg_catalog."default" NOT NULL,
animal_doc jsonb,
CONSTRAINT animals_pkey PRIMARY KEY (animal)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE animals
OWNER to postgres;
CREATE OR REPLACE FUNCTION check_is_animal(anyelement) RETURNS boolean AS $$
DECLARE
i text;
arr text[];
BEGIN
-- IF ((SELECT pg_typeof($1)) = 'jsonb') THEN
-- arr = jsonb_array_elements($1);
-- ELIF ((SELECT pg_typeof($1)) = 'json') THEN
-- arr = json_array_elements($1);
-- ELSE
arr = $1;
-- END IF;
FOREACH i IN ARRAY arr LOOP
IF i NOT IN (SELECT animal FROM animals) THEN
RAISE NOTICE '% is not a known animal', i;
RETURN FALSE;
END IF;
END LOOP;
RETURN TRUE;
END;
$$ LANGUAGE plpgsql;
INSERT INTO animals (animal) VALUES ('Cat') ON CONFLICT ON CONSTRAINT animals_pkey DO NOTHING;
INSERT INTO animals (animal) VALUES ('Dog') ON CONFLICT ON CONSTRAINT animals_pkey DO NOTHING;
DO $$
BEGIN
IF check_is_animal(array['Cat','Dog', 'Horse']) THEN
RAISE NOTICE 'All good';
ELSE
RAISE NOTICE 'Failed';
END IF;
/*
IF check_is_animal(jsonb('["Cat","Dog", "Horse"]')) THEN
RAISE NOTICE 'All good';
ELSE
RAISE NOTICE 'Failed';
END IF;
*/
END $$;
The SELECT pg_typeof($1)
was found here.
Any suggestions for how the get this to work?
Best Answer
The result of
pg_typeof()
is anoid
, if you want to compare that with a string value, you need to cast it, e.g.if pg_typeof($1)::text = 'jsonb' then...
But once you checked if the input is a JSON you also need to verify if it's a JSON array, because
{"foo": "bar"}
will also be reported as json(b).The way you iterate over the array is also quite inefficient as you are running the same select statement over and over again. You can check this with a single statement.
There is no ELIF in PL/pgSQL. As documented in the manual it needs to be
ELSIF
So the function would look something like this:
If the JSON array contains unknown key names and you can't use the hardcoded
name
, you can use this instead:The final select statement checks if all elements in the array exist in the table. By using a NOT EXISTS condition, the statement can stop as soon as one non-existing animal is found.
Online example