I've a problem with postgresql function that is executed from multiple sessions. The function foo is created in multiple sessions at the exact same time. That causes the login function to be executed at the same time.
Inside the login function (see below) there is a if-statement IF token does not exists THEN that also is executed at the exact same time. Of course, it will be false for all the function since the token never has been created yet.
This is not the behaviour I want. Is it some how possible to make the login function being "thread-safe". Like synchronized in Java?
Pseudo function:
CREATE OR REPLACE FUNCTION foo() RETURNS VARCHAR AS
$body$
BEGIN
token = login();
result = doStuffWithTheLogin(token);
IF result = LoginFailed THEN
token = login(true);
RETURN 'RETRY';
END IF;
END;
$body$
LANGUAGE 'plpgsql'
SECURITY DEFINER;
The login function:
CREATE OR REPLACE FUNCTION login(forceReload BOOLEAN) RETURNS VARCHAR AS
$body$
BEGIN
IF NOT forceReload THEN
token = getTokenFromStorage();
END IF;
IF token does not exists THEN
token = createNewToken();
saveTokenToStorage(token);
END IF;
RETURN token;
END;
$body$
LANGUAGE 'plpgsql'
SECURITY DEFINER;
I've tried with table locks. But that didn't really do the job. The login function takes about 2-3 seconds to finish, if there is 100 foo tasks, the table will be locked for a long time.
It's not very clear what issue you're having or trying to solve, but it sounds like you're looking for an advisory lock:
http://www.postgresql.org/docs/current/static/explicit-locking.html
And perhaps exception handling:
http://www.postgresql.org/docs/current/static/plpgsql-errors-and-messages.html
Related
Is there anything similar to setTimeout setTimeInterval in PostgreSQL which allows to execute piece of code (FUNCTION) at specified time interval?
As far as I know only thing that can execute a FUNCTION according to certain event is Triggers but it is not time based but operation driven (INSERT / UPDATE / DELETE / TRUNCATE)
While I could do this in application code, but prefer to have it delegated to database. Anyway I could achieve this in PostgreSQL? May be an extension?
Yes, there is a way to do this. It's called pg_sleep:
CREATE OR REPLACE FUNCTION my_function() RETURNS VOID AS $$
BEGIN
LOOP
PERFORM pg_sleep(1);
RAISE NOTICE 'This is a notice!';
END LOOP;
END;
$$ LANGUAGE plpgsql;
SELECT my_function();
This will raise the notice every second. You can also make it do other things instead of raising a notice.
OR
You can use PostgreSQL's Background Worker feature.
The following is a simple example of a background worker that prints a message every 5 seconds:
CREATE OR REPLACE FUNCTION print_message() RETURNS VOID AS $$
BEGIN
RAISE NOTICE 'Hello, world!';
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION schedule_print_message() RETURNS VOID AS $$
DECLARE
job_id BIGINT;
BEGIN
SELECT bgw_start_recurring_job(
'print-message',
'print_message',
'5 seconds'
) INTO job_id;
END;
$$ LANGUAGE plpgsql;
SELECT schedule_print_message();
I almost never use superuser. In rare cases that I use a function that executes the command with SECURITY DEFINER permissions. However, a subscriber cannot be executed inside a function. What is a workaround for this without create_slot= false?
CREATE OR REPLACE FUNCTION public.exec_su(text)
RETURNS void
LANGUAGE 'plpgsql'
SECURITY DEFINER
AS $BODY$
BEGIN
EXECUTE $1;
END
$BODY$;
select public.exec_su('CREATE SUBSCRIPTION n1_subscription CONNECTION 'host=pg12x_aaa port=5432 user=rep password=aaa dbname=aaa' PUBLICATION n1_publication;');
Those are the two work arounds. Either do it outside of a function (and outside of a transaction block), or do it with create_slot=false. There are no extra choices.
I have a procedure that checks for a condition (if a session is fully booked). I want to make a trigger that checks if the session is fully booked before inserting a new booking.
What I imagine it would be like is something like this.
CREATE OR REPLACE FUNCTION check_if_session_is_full_trigger()
RETURNS trigger AS
$BODY$
BEGIN
IF (EXECUTE session_is_full(NEW.session_id)) THEN
RAISE EXCEPTION 'SESSION IS FULLY BOOKED';
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
But I get this
ERROR: syntax error at or near "session_is_full"
LINE 5: IF (EXECUTE session_is_full(NEW.session_id) = TRUE) THE...
So obviously I'm not doing something right.
session_is_full(int) returns boolean.
Am I on the right track, and how to fix this?
IF (session_is_full(NEW.session_id)) should work – #a_horse_with_no_name
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
In Postgresql, I have an UPDATE rule on a table which only needs to call a dctUpdate function without doing a whole SQL statement, since the SQL statement is actually done in the function. The only way I know of calling the function is through SELECT dctUpdate(windowId):
create or replace function infoUpdate(windowId in numeric) returns void as $$
begin
if windowId is null then
update info_timestamp set timestamp = now();
else
update info_timestamp set timestamp = now() where window_id = windowId;
end if;
end;
$$ LANGUAGE plpgsql;
create or replace rule info_update_rule as on update to some_table do also select infoUpdate(NEW.window_id);
However, on the command line, when that rule gets triggered because I updated a row in some_table, I get useless output from the SELECT clause that calls the function :
db=# update some_table set name = 'foobar' where window_id = 1;
infoupdate
-----------
(1 row)
UPDATE 1
Is there a way to have info_update_rule call the infoUpdate function without it displaying dummy output?
I've found no options to implement this using rules, but there is an alternative way of implementing this usign triggers.
So, you define trigger function as following:
CREATE OR REPLACE FUNCTION ur_wrapper_trg()
RETURNS trigger AS
$BODY$
begin
perform infoUpdate(NEW.window_id);
RETURN NEW;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION ur_wrapper_trg() OWNER TO postgres;
Note PERFORM syntax is used. This syntax is identical to SELECT syntax except it supresses all output.
Than you define a trigger
CREATE TRIGGER some_table_utrg
BEFORE UPDATE
ON some_table
FOR EACH ROW
EXECUTE PROCEDURE ur_wrapper_trg();
In the end, you remve your rule.
Haven't tested with null, but with actual windos_ids works as expected, without any unwanted output.
Consult with Triggers and Rules vs triggers for detailed description.
The closes solution to which I came is to call \t \a before select function() and right after it. The only remaining thing is a new line for each call.