I have the following function in my postgres database:
create function my_schema.create_my_book(book my_schema.book) returns my_schema.book as $$
declare
v_book my_schema.book;
begin
insert into my_schema.book(title, language) values (book.title, book.language) returning * into v_book;
return v_book;
end;
$$ language plpgsql volatile;
This way I have to type out all the column names (title, language) and values (book.title, book.language). My book table is quite big so this will blow up my code by a lot, and once I add a column I will have to remember to add it to this function too.
Is there a way to directly insert the whole book my_schema.book object?
Yes, here it is. You do not even need plpgsql to do this, plain sql will do (and works faster).
create or replace function my_schema.create_my_book(arg_book my_schema.book)
returns my_schema.book as
$$
insert into my_schema.book select arg_book.* returning *;
$$ language sql volatile;
I changed the argument's name to arg_book in order to avoid possible ambiguity. And since the type of arg_book is my_schema.book this simple code adapts itself to table mutation and continues to work.
To solve the id issue
create or replace function my_schema.create_my_book(arg_book my_schema.book)
returns my_schema.book as
$$
declare
v_book my_schema.book%rowtype;
begin
arg_book.id := nextval('the-id-sequence-name');
insert into my_schema.book select arg_book.* returning * into v_book;
return v_book;
end;
$$ language plpgsql volatile;
which is pretty close to your initial function, just dynamic.
Related
I try to write an insert function in Postgres, but always get the error that I have a syntax error on line 7.
CREATE FUNCTION "Portfolio"."Einfuegen"(IN "idIN" integer, IN "bildIN" bigint, IN "dokumenteIN"
text[])
RETURNS SETOF "Portfolio"."Namen"
LANGUAGE 'sql'
AS $BODY$
BEGIN
INSERT INTO Portfolio.Namen (_id,dokumente,bild) VALUES (idIN,dokumenteIN,bildIN);
END;
$BODY$;
ALTER FUNCTION "Portfolio"."Einfuegen"(integer, bigint, text[])
OWNER TO postgres;
A language sql function may not use a begin ... end; block. Additionally: your function is declared as returns setof but doesn't return anything.
And once you start using the stupid double quotes around identifiers (and parameters) you have always use them:
So it should look like this:
CREATE FUNCTION "Portfolio"."Einfuegen"(IN "idIN" integer, IN "bildIN" bigint, IN "dokumenteIN" text[])
RETURNS SETOF "Portfolio"."Namen"
LANGUAGE sql
AS
$BODY$
INSERT INTO "Portfolio"."Namen"(_id,dokumente,bild)
VALUES ("idIN", "dokumenteIN", "bildIN") --<< you have to use the quotes here as well
returning *; -- return the newly inserted row
END;
$BODY$;
I would strongly suggest you do not use quoted identifiers. They are much more trouble in the long run than their are worth it.
Explain me please, why I'm getting error here:
djabase=# CREATE or REPLACE FUNCTION upset(domain_name varchar)
RETURNS TABLE (id int, domain varchar(50)) AS $$
BEGIN
SELECT domain from separser_domains where domain=$1;
EXCEPTION
when sqlstate 'no_data' then
INSERT into separser_domains(domain) VALUES (domain_name);
END; $$
LANGUAGE 'sql' STABLE;
ERROR: syntax error at or near "SELECT"
LINE 4: SELECT domain from separser_domains where domain=$1;
You are using PLPGSQL syntax but declaring the function as SQL. This the reason, why the function fails with a SYNTAX ERROR. The language of the function is declared with the statement LANGUAGE and you declared it in the last line:
LANGUAGE 'sql' STABLE;
SQL functions do not support the BEGIN - END statement and exceptions trapping.
To fix it, simple change LANGUAGE 'sql' in LANGUAGE 'plpgsql'.
Some other considerations:
Catching a exception in this place is probably not necessary. Use the FOUND variable instead.
With the STABLE keyword, the function can not perform INSERT. Change STABLEto VOLATILE.
You declared RETURNS TABLE but the function does not return anything. Use RETURNS VOID instead.
Using SELECT and discarding the result is not allowed. Use PERFORM instead.
A valid version of your version could be this:
DROP FUNCTION IF EXISTS upset(varchar);
CREATE or REPLACE FUNCTION upset(domain_name varchar)
RETURNS VOID AS $$
BEGIN
PERFORM domain from separser_domains where domain=domain_name;
IF NOT FOUND THEN
INSERT into separser_domains(domain) VALUES (domain_name);
END IF;
RETURN;
END;
$$
LANGUAGE 'plpgsql' VOLATILE;
I have a sql UPDATE statement in a plpgsql function. I now want to call the pg_notify function for each updated row and am uncertain if my solution is the best possibility.
I am not aware of any position in the UPDATE statement itself where I could apply the function. I don't think it is possible in the SET part and if I would apply the function in the WHERE part, it would be applied to each row as it is checked and not only the updated rows, correct?
I therefore thought I could use the RETURNING part for my purposes and designed the function like this:
CREATE OR REPLACE FUNCTION function_name() RETURNS VOID AS $BODY$
BEGIN
UPDATE table1
SET a = TRUE
FROM table2
WHERE table1.b = table2.c
AND <more conditions>
RETURNING pg_notify('notification_name', table1.pk);
END;
$BODY$ LANGUAGE 'plpgsql' VOLATILE;
Unfortunately this gave me an error saying that I am not using or storing the return value of the query anywhere. I therefore tried putting PERFORM in front of the query but this seemed to be syntactically incorrect.
After trying different combinations with PERFORM my ultimate solution is this:
CREATE OR REPLACE FUNCTION function_name() RETURNS VOID AS $BODY$
DECLARE
dev_null INTEGER;
BEGIN
WITH updated AS (
UPDATE table1
SET a = TRUE
FROM table2
WHERE table1.b = table2.c
AND <more conditions>
RETURNING pg_notify('notification_name', table1.pk)
)
SELECT 1 INTO dev_null;
END;
$BODY$ LANGUAGE 'plpgsql' VOLATILE;
This works as it is supposed to, but I feel like there should be a better solution which does not temporarily store a useless result and does not use a useless variable.
Thank you for your help.
** EDIT 1 **
As can be seen in #pnorton 's answer, a trigger would do the trick in most cases. For me, however, it is not applicable as the receiver of the notifications also sometimes updates the table and I do not want to generate notifications in such a case
"I have a sql UPDATE statement in a plpgsql function. I now want to
call the pg_notify function for each updated row "
Ok I might be tempted to use a trigger Eg
CREATE TABLE foobar (id serial primary key, name varchar);
CREATE OR REPLACE FUNCTION notify_trigger() RETURNS trigger AS $$
DECLARE
BEGIN
PERFORM pg_notify('watch_tb_update', TG_TABLE_NAME || ',id,' || NEW.id );
RETURN new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER foobar_trigger AFTER INSERT ON foobar
FOR EACH ROW EXECUTE PROCEDURE notify_trigger();
LISTEN watch_tb_update;
INSERT into foobar(id, name) values(1,'test_name');
I've tested this and it works fine
I need to show the Tree_Nodes table data
CREATE OR REPLACE FUNCTION sample()
RETURNS TABLE() AS
$BODY$
BEGIN
select * from "Tree_Nodes";
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION sample()
OWNER TO postgres;
It fails with:
ERROR: query has no destination for result data
Avoid the error and i will get the table column format in all data
To return the result of a SELECT, a pure SQL function is much more suitable:
CREATE OR REPLACE FUNCTION sample()
RETURNS TABLE ( .... ) AS
$BODY$
select * from "Tree_Nodes";
$BODY$
LANGUAGE sql;
Or if you really need PL/pgSQL, you need to use return query (which is clearly documented in the manual)
CREATE OR REPLACE FUNCTION sample()
RETURNS TABLE (....)
AS
$BODY$
BEGIN
return query select * from "Tree_Nodes";
END;
$BODY$
LANGUAGE plpgsql;
But you cannot just specify returns table() you have to also define the structure of the result:
CREATE OR REPLACE FUNCTION sample()
RETURNS TABLE(id integer, some_column text, other_column decimal(10,2), ...)
AS
The exact error you quote is caused by using SELECT without an INTO clause in PL/PgSQL. You must either use SELECT INTO somevariable, use RETURN QUERY, if you want to discard the data, use the PERFORM statement instead of SELECT, as covered by the PL/PgSQL manual.
Once you fix that by using RETURN QUERY SELECT .... you'll find that the function still doesn't work, because RETURNS TABLE() doesn't make sense. You're returning an empty result set. It'll fail, complaining that the statement is returning a result set that doesn't match the function.
It makes no sense to do this anyway, since you can just write it as a trivial SQL function like:
CREATE OR REPLACE FUNCTION sample()
RETURNS SETOF "Tree_Nodes"
AS $$
SELECT * FROM "Tree_Nodes";
$$ LANGUAGE sql;
This function appears to serve no purpose. What are you trying to achieve with it?
(By the way, you should generally avoid SELECT * in production code. List the columns. That way, if you add a column later, things that use the table won't suddenly stop working.)
I'm using PostgreSQL with pgAdmin and I can't get a trigger function to work. However, as far as I am aware, you can return type trigger in PostgreSQL?
CREATE OR REPLACE FUNCTION validate_Cat()
RETURNS TRIGGER AS
$BODY$
BEGIN
-- CODE here
END;
$BODY$
LANGUAGE SQL;
CREATE TRIGGER validate_Cat
AFTER INSERT OR UPDATE ON Category
FOR EACH ROW execute procedure validate_Cat();
SOLVED, had to change language to PLPGSQL