how to get current row value in plpgsql function - sql

I need to create plpgsql methods which use current row value without passing in parameter in update command.
I tried
create temp table test ( test text, result text ) on commit drop;
insert into test values ('1','');
CREATE OR REPLACE FUNCTION public.gettest() RETURNS text AS $$
DECLARE
comp text := NULL;
BEGIN
EXECUTE 'SELECT ''Current row is '' ||test.test' INTO comp;
RETURN comp;
END; $$ LANGUAGE plpgsql STRICT STABLE;
update test set result = 'Result: ' || gettest();
but got exception
ERROR: missing FROM-clause entry for table "test"
LINE 1: SELECT 'Current row is ' ||test.test
^
QUERY: SELECT 'Current row is ' ||test.test
CONTEXT: PL/pgSQL function gettest() line 6 at EXECUTE statement
********** Error **********
ERROR: missing FROM-clause entry for table "test"
SQL state: 42P01
Context: PL/pgSQL function gettest() line 6 at EXECUTE statement
How to fix ?
How to fix without passing vaue to plpgsql method parameter ?

There is no such thing as an "implicit current row". You have to pass the the function whatever it needs as a parameter. You can however pass a complete row if you want to:
create temp table test (val text, result text ) on commit drop;
insert into test values ('1','');
insert into test values ('2','');
CREATE OR REPLACE FUNCTION gettest(p_test test) RETURNS text AS $$
DECLARE
comp text := NULL;
BEGIN
comp := 'Current row is '||p_test.val;
RETURN comp;
END; $$ LANGUAGE plpgsql STRICT STABLE;
update test set result = 'Result: ' || gettest(test);
I had to rename the column test to something else, otherwise the call gettest(test) would refer to the column not the whole table (=row) and thus it didn't work.

Related

SQL procedure loop on a key from JSON object

I am trying to insert a JSON object into multiple tables
CREATE TABLE images
(
id serial primary key ,
image_url varchar(255),
filename varchar(255)
);
Procedure:
CREATE OR REPLACE procedure example1(
images_json json
)
AS
$$
DECLARE i json;
BEGIN
RAISE NOTICE 'ITEM ID is %',images_json->>'item_id'; --will be used later on
FOR i IN SELECT * FROM json_array_elements(images_json->>'images')
LOOP
INSERT INTO images (image_url, filename) VALUES (i->>'url', i->>'filename');
end loop;
end;
$$
language plpgsql;
When I test it
call example1('{"item_id": 123,"images":[{ "url": "https://google.com","filename": "google.png"},
{ "url": "https://yahoo.com","filename":"yahoo.png"},
{"url": "https://www.bing.com","filename":"bing.png"}]}')
I get the following error
ERROR: function json_array_elements(text) does not exist
No function matches the given name and argument types. You might need to add explicit type casts.
Where: PL/pgSQL function example1(json) line 5 at FOR over SELECT rows
I want to insert each of the images array into images table.
Your immediate error is, that ->> returns a text value, but json_array_elements() expects a json value. You need to use the -> operator here:
FOR i IN SELECT * FROM json_array_elements(images_json -> 'images')
^ here
But you don't need a LOOP for this (or PL/pgSQL to begin with):
CREATE OR REPLACE procedure example1(images_json json)
AS
$$
BEGIN
RAISE NOTICE 'ITEM ID is %',images_json->>'item_id'; --will be used later on
INSERT INTO images (image_url, filename)
SELECT i->>'url', i->>'filename'
FROM json_array_elements(images_json -> 'images') as i;
end;
$$
language plpgsql;

I need help regarding dynamic SQL and dynamically setting the table name in a function

So, i need to create a function that returns the count of the lines in a table, the table name must be specified by the user. This is the code i have:
CREATE OR REPLACE FUNCTION datiInTab( table regclass )
RETURNS NUMERIC(5) AS
$$
DECLARE
num NUMERIC(5);
cmd VARCHAR(1000):='SELECT COUNT(*) FROM ';
BEGIN
EXECUTE cmd || table;
RETURN num;
END;
$$
LANGUAGE plpgsql;
SELECT * FROM datiInTab(corsi);
And this is the error that returns after i execute:
ERROR: ERRORE: la colonna "corsi" non esiste
LINE 1: SELECT * FROM datiInTab(corsi); ^ SQL state: 42703 Character: 25
Meaning that the column "corsi" doesn't exist.
I debugged as much as i could and the only conclusion is that i don't really know what i need to do in order to make use of this dynamic call.
edit: regclass was a last minute addition, after trying with a simple varchar, that returned the same exact error
You can't name a variable table. it is a reserved word
and you have to pass a string else postgres thinks you want to pass a column
CREATE OR REPLACE FUNCTION datiInTab( table1 regclass )
RETURNS NUMERIC(5) AS
$$
DECLARE
num NUMERIC(5);
cmd VARCHAR(1000):='SELECT COUNT(*) FROM ';
BEGIN
EXECUTE cmd || table1;
RETURN num;
END;
$$
LANGUAGE plpgsql;
✓
SELECT * FROM datiInTab('corsi');
ERROR: relation "corsi" does not exist
LINE 1: SELECT * FROM datiInTab('corsi');
^
db<>fiddle here

Evaluate dynamic condition in PL/pgSQL using EXECUTE

I have a function that needs to dynamically validate the input based on the value type. It does this by finding the constraint in another table, but for simplicity I provide the constraint as well in the function below.
This other table contains (value_type, value_constraint), where value_constraint is a text field that contains e.g. value::int > 0. I need to dynamically check this constraint within my insert function. I was trying to do that using EXECUTE as seen below, but it's not working.
How do I dynamically execute a condition statement and get the value as a boolean into v_successful_insert?
CREATE OR REPLACE FUNCTION insert_value(p_value_type text, p_value_constraint text, p_value text) RETURNS boolean
AS $$
DECLARE
v_successful_insert bool;
BEGIN
EXECUTE p_value_constraint INTO v_successful_insert;
IF v_successful_insert THEN
INSERT INTO my_table (value_type, value)
VALUES (p_value_type, p_value);
END IF;
RETURN v_successful_insert;
END;
$$
LANGUAGE plpgsql volatile;
The code is run on Postgresql 10.6.
You need to do a SELECT in the execute. For example:
DO $$
DECLARE
p_value TEXT := 'x';
p_value_constraint TEXT := '::int > 0';
result BOOLEAN;
BEGIN
BEGIN
EXECUTE 'SELECT $1' || p_value_constraint
INTO result
USING p_value;
EXCEPTION
WHEN INVALID_TEXT_REPRESENTATION THEN
result := FALSE;
END;
RAISE NOTICE '%', result;
END $$
Prints FALSE

Accessing current row values in a trigger

I created a function will insert a value into a json for each row inserted in a table.
But when I execute the insert, it says me that the column doesn't exist.
Here is the function :
CREATE OR REPLACE FUNCTION insert_id_function()
RETURNS trigger AS'
BEGIN
NEW.previewcontent = previewcontent || ''{"id":1}'';
RETURN NEW;
END;'
LANGUAGE plpgsql VOLATILE;
Here is the trigger :
CREATE TRIGGER insert_id_trigger
BEFORE INSERT
ON "Telnet"
FOR EACH ROW
EXECUTE PROCEDURE insert_id_function();
Here is the error :
`ERROR: column "previewcontent" does not exist LINE 1: SELECT previewcontent || '{"id":1}'`
Here is my table definition :
This is a column of the NEW record:
NEW.previewcontent = NEW.previewcontent || ''{"id":1}'';
If the type of previewcontent is jsonb use jsonb_build_object() to add the current value of id to the jsonb column:
CREATE OR REPLACE FUNCTION insert_id_function()
RETURNS trigger AS $$
BEGIN
NEW.previewcontent = NEW.previewcontent || jsonb_build_object('id', NEW.id);
RETURN NEW;
END;
$$ LANGUAGE plpgsql VOLATILE;

postgresql function error: column name does not exist

i've implemented a function that check if a value appears in a specific row of a specific table:
CREATE FUNCTION check_if_if_exist(id INTEGER, table_name character(50), table_column character(20) ) RETURNS BOOLEAN AS $$
DECLARE res BOOLEAN;
BEGIN
SELECT table_column INTO res
FROM table_name
WHERE table_column = id;
RETURN res;
END;
$$ LANGUAGE plpgsql
i've create and fill a simple test table for try this function:
CREATE TABLE tab(f INTEGER);
and i call function like
SELECT check_if_exist(10, tab, f);
but i occurs in this error:
ERROR: column "prova" does not exist
LINE 1: SELECT check_if_exist(10, tab, f);
^
********** Error **********
ERROR: column "tab" does not exist
SQL state: 42703
Character: 27
why?
In addition to Elmo response you must be careful with types. You have got:
ERROR: column "tab" does not exist
because SQL parser do not know how to deal with tab which is without quote. Your query must be like:
SELECT check_if_exist(10, 'tab', 'f');
As Elmo answered you use dynamic query, so even if you quote tab you will got error:
ERROR: relation "table_name" does not exist
so you can use EXECUTE, example:
CREATE OR REPLACE FUNCTION check_if_exist(id INTEGER, table_name varchar, table_column varchar) RETURNS BOOLEAN AS $$
DECLARE
sql varchar;
cnt int;
BEGIN
sql := 'SELECT count(*) FROM ' || quote_ident(table_name) || ' WHERE ' || quote_ident(table_column) || '=$1';
RAISE NOTICE 'sql %', sql;
EXECUTE sql USING id INTO cnt;
RETURN cnt > 0;
END;
$$ LANGUAGE plpgsql
You can also use VARCHAR instead of character(N) in function arguments and use CREATE OR REPLACE FUNCTION ... instead of just CREATE FUNCTION ... which is very handy at debugging.
Your code has no chance to work - when dealing with different tables in PLPGSQL you need to utilize dynamic queries, so EXECUTE is required - http://www.postgresql.org/docs/current/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN
But first of all - there is nothing bad in using PostgreSQL EXISTS - http://www.postgresql.org/docs/current/static/functions-subquery.html#AEN15284 instead of inventing your own - performance of your solution will be significantly worse than using included batteries...
Hopefully this is helpful. Good luck.