PL/pgSQL Loops don't update current timestamp / now() - postgresql

I want to see how long a loop iteration takes inside a DO block in postgres. The basic layout is as follows:
DO $$
declare v_time timestamptz;
declare i record;
begin
for i in select generate_series(1, 5) t
loop
select current_timestamp into v_time;
perform pg_sleep(i.t);
-- something done here (pg_sleep to ensure some time passes)
raise notice '%', v_time - (select current_timestamp);
-- expect negative interval from RAISE.
end loop;
end; $$;
However, when I run this (have tried on Postgres 13 and 9), I get an interval of 0S returned:
NOTICE: 00:00:00
NOTICE: 00:00:00
NOTICE: 00:00:00
NOTICE: 00:00:00
NOTICE: 00:00:00
DO
Query returned successfully in 15 secs 389 msec.
I have done this previously and have never run into this issue before, so I guess my question is "what am I doing wrong this time?" instead of "why is postgres behaving unexpectedly?"

current_timestamp is defined to be:
the start time of the current transaction, their values do not change during the transaction
you probably want to use clock_timestamp() instead which returns a value that changes within a transaction, see the link above for a more complete description.

Related

PostgreSQL select now()::timestamp differs from default now()::timestamp

In my program every table has a column last_modified:
last_modified int8 DEFAULT (date_part('epoch'::text, now()::timestamp) * (1000)::double precision) NOT NULL
For update I added a trigger:
CREATE OR REPLACE FUNCTION sync_lastmodified() RETURNS trigger AS $$
BEGIN
NEW.last_modified := (date_part('epoch'::text, now()::timestamp) * (1000)::double precision);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER
sync_lastmodified
BEFORE UPDATE ON
ourtable
FOR EACH ROW EXECUTE PROCEDURE
sync_lastmodified();
They should write current time as a long value into the last_modified column on update/insert.
However, it is not working as I expected.
To reproduce the issue, I did update and got the following:
last_modified value equals 1543576224455 (Friday November 30, 2018 16:10:24 (pm) in time zone Asia/Tashkent (+05))
Almost at the same time I run the function now from pgAdmin:
SELECT now()
and got the result:
2018-11-30 11:10:36.891426+05
To check system time within a few seconds I run timedatectl status from terminal and got the following result:
The question is why the function now() gives a time with 5 hours
difference when I run it from trigger or as a default value when
insert?
epoch will give you the number of seconds since the epoch. As the documentation says:
epoch
For timestamp with time zone values, the number of seconds since 1970-01-01 00:00:00 UTC (can be negative)
Since you are offset by 5 hours from UTC, that explains the difference.

postgresql case inside function doesn't trigger

So, i have a couple of functions inside my db.
One function needs to run when the data in a specific table is older than 5 minutes.
I've tried doing it with:
PERFORM case when now() - '5 minutes'::interval > (select end_time from x order by end_route desc limit 1) then update_x() else null end;
When i run the command as a regular select query, it runs OK. But when i put that inside another function (The one being called, returns updated table that is no older than 5 minutes), it never runs. Also, if i just put update_x(), then it runs OK (but every time the function is called).
Does anyone have any idea on how i could fix this?
One idea is to just set a cron to run the function every 5 mins independently, but i'd rather the server is idle since the function is quite resource intensive, and it doesn't get called that often.
I'm on version 8.4(Due to my ISP, so can't change, though i am considering moving to VPS, so if this is something that would work on 9.5 and newer, i can wait).
The function now() gives the start time of the current transaction and is invariable inside it. Use clock_timestamp(), example:
do $$
begin
for i in 1..3 loop
perform pg_sleep(1);
raise notice 'now(): % clock_timestamp(): %', now(), clock_timestamp();
end loop;
end $$;
NOTICE: now(): 2017-12-06 10:22:40.422683+01 clock_timestamp(): 2017-12-06 10:22:41.437099+01
NOTICE: now(): 2017-12-06 10:22:40.422683+01 clock_timestamp(): 2017-12-06 10:22:42.452456+01
NOTICE: now(): 2017-12-06 10:22:40.422683+01 clock_timestamp(): 2017-12-06 10:22:43.468124+01
Per the documentation:
clock_timestamp() returns the actual current time, and therefore its value changes even within a single SQL command (...)
now() is a traditional PostgreSQL equivalent to transaction_timestamp().
I'm not sure why, but once i moved the boolean up one level it started working.
So now instead of having perform case query inside function, i'm sending boolean to the function, and performing the check in a view above this function.
CREATE VIEW x_view AS select * from get_x((clock_timestamp() - '5 minutes'::interval)::timestamp > (select end_route from gps_skole2 order by end_route desc limit 1));
And inside the function:
PERFORM case when $1 then update_x() else null end;

How to get execution time in postgres

I need, in a plpgsql script, to get the execution time of the last query in a variable for several purposes (calculation, display and storage), so the psql \timing option is not what I look for because I can't manipulate the result time. Do you know if there is anything like the "get diagnostics" command, but for execution time (or work around) ?:
get diagnostics my_var := EXECUTION_TIME;
I couldn't find anything else than row_count and result_oid...
You can compare clock_timestamp() before and after the query:
do $$
declare t timestamptz := clock_timestamp();
begin
perform pg_sleep(random());
raise notice 'time spent=%', clock_timestamp() - t;
end
$$ language plpgsql;
Sample result:
NOTICE: time spent=00:00:00.59173

How is compiled PL/pgSQL block?

do $$
declare
tm1 timestamp without time zone;
tm2 timestamp without time zone;
begin
select localtimestamp(0) into tm1;
for i in 1..200000000 loop
--just waiting several second
end loop;
select localtimestamp(0) into tm2;
raise notice '% ; %', tm1, tm2;
end;
$$ language plpgsql
Why gives this procedure same values for tm1 and tm2 ?
Is not executed this code step by step?
From the manual
These SQL-standard functions all return values based on the start time of the current transaction [...] Since these functions return the start time of the current transaction, their values do not change during the transaction. This is considered a feature: the intent is to allow a single transaction to have a consistent notion of the "current" time, so that multiple modifications within the same transaction bear the same time stamp
(Emphasis mine)
You probably want clock_timestamp()

Get function timings in pgAdminIII

I'd like to compare two functions in pgAdmin's SQL editor. Here's the script. However, when I run it, seems the start_time and end_time have the same value no matter how many iterations. (But, the Query returned successfully with no result in nnn ms. message does go higher with every increase in the loop size.) Why?
DO
$$
DECLARE
start_time timestamp;
end_time timestamp;
diff interval;
BEGIN
SELECT now() INTO start_time;
FOR i IN 1..1000 LOOP
--PERFORM uuid_generate_v1mc();
PERFORM id_generator();
END LOOP;
SELECT now() INTO end_time;
SELECT end_time - start_time INTO diff;
RAISE NOTICE '%', start_time;
RAISE NOTICE '%', end_time;
RAISE NOTICE '%', diff;
END
$$
From the manual
Since these functions [CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE] return the start time of the current transaction, their values do not change during the transaction.
And then further down:
transaction_timestamp() is equivalent to CURRENT_TIMESTAMP, but is named to clearly reflect what it returns. statement_timestamp() returns the start time of the current statement (more specifically, the time of receipt of the latest command message from the client). statement_timestamp() and transaction_timestamp() return the same value during the first command of a transaction, but might differ during subsequent commands. clock_timestamp() returns the actual current time, and therefore its value changes even within a single SQL command
So you probably want to use clock_timestamp()