How to use CURRENT_TIMESTAMP for a timestamptz field in Postgres? - postgresql

tl;dr
NEW.updated_at := NOW(); is not working, where updated_at stores a timestamptz value.
I have this trigger function, where the error seems to happen in the first line. The updated_at field stores a timestamptz value, which NOW() should be, but doesn't seem to work (throws error). I have tried both CURRENT_TIMESTAMP and NOW() and neither seems to work. Does anyone know what might be causing this issue?
BEGIN
NEW.updated_at := NOW();
UPDATE public.projects
SET updated_at = NEW.updated_at
WHERE id = NEW.project_id;
INSERT INTO public.document_contributor (document_id, contributor)
VALUES (NEW.id, NEW.created_by)
ON CONFLICT
DO NOTHING;
INSERT INTO public.commits (message, created_by, project_id, document_id, created_at, previous_content, current_content)
VALUES (NEW.note, NEW.updated_by, NEW.project_id, NEW.id, NEW.updated_at, OLD.content, NEW.content);
RETURN NEW;
END
Error
Event message
invalid input syntax for type timestamp with time zone: "Invalid Date"
I've noticed that the query being made looks like this.
{
"query" : "\n-- source: dashboard\n-- user: 201e7b7c-bb29-409f-9e01-65ca849999e6\n-- date: 2022-10-09T01:44:28.100Z\n\nupdate public.documents set (created_at,updated_at,note,data) = (select created_at,updated_at,note,data from json_populate_record(null::public.documents, '{\"created_at\":\"Invalid Date\",\"updated_at\":\"Invalid Date\",\"note\":null,\"data\":[{\"type\":\"title\",\"children\":[{\"text\":\"Untitled\"}]},{\"type\":\"paragraph\",\"children\":[{\"text\":\"Pool 입니다.\"}]}]}')) where id = 'c5e2348b-9da7-4db3-8d5e-9d669cfbd7cb' returning *;\n"
}

BEGIN
NEW.updated_at = NOW();
UPDATE public.projects
SET updated_at = NEW.updated_at
WHERE id = NEW.project_id;
INSERT INTO public.document_contributor (document_id, contributor)
VALUES (NEW.id, NEW.created_by)
ON CONFLICT
DO NOTHING;
INSERT INTO public.commits (message, created_by, project_id, document_id, created_at, previous_content, current_content)
VALUES (NEW.note, NEW.updated_by, NEW.project_id, NEW.id, NEW.updated_at, OLD.content, NEW.content);
RETURN NEW;
END

Turns out the "time" of the trigger function was the issue.
In the official document, the operation happens BEFORE.
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE FUNCTION emp_stamp();
But I have done the opposite, which was AFTER an update happens.
SO when I recreated the function changing from AFTER to BEFORE, it works fine.
The reason? Well I am guessing that updating the record by RETURN NEW, AFTER the operation finishes violates some rule. (But I am not sure)

Related

Postgresql trigger IF condition to see if destination table has this record

I have a straight forward trigger function that is set to run on UPDATE or INSERT in a table.
When this trigger runs, I want to insert the record into another table, only if it doesn't already exist there.
I could simply ignore checking and let the insert fail, but I feel like that's not the best approach.
-- Trigger
CREATE TRIGGER archivelogic_trigger AFTER INSERT OR UPDATE ON entsf.et4ae5__individualemailresult__c
FOR EACH ROW EXECUTE PROCEDURE entsf.archivelogicfunc();
-- Function
CREATE OR REPLACE FUNCTION entsf.archivelogicfunc() RETURNS TRIGGER AS $result_table$
BEGIN
BEGIN
IF (DATE(NEW.et4ae5__datesent__c) < NOW() - INTERVAL '180 days'
AND DATE(NEW.et4ae5__datesent__c) > NOW() - INTERVAL '540 days'
AND NEW.id NOT IN (SELECT id FROM archive.individualemailresult__c)) -- this seems expensive
THEN
INSERT INTO archive.individualemailresult__c
(dateopened__c,
numberoftotalclicks__c,
datebounced__c,
fromname__c,
hardbounce__c,
fromaddress__c,
softbounce__c,
name,
lastmodifieddate,
opened__c,
ownerid,
subjectline__c,
isdeleted,
contact__c,
systemmodstamp,
lastmodifiedbyid,
datesent__c,
dateunsubscribed__c,
createddate,
createdbyid,
lead__c,
tracking_as_of__c,
numberofuniqueclicks__c,
senddefinition__c,
mergeid__c,
triggeredsenddefinition__c,
sfid,
id,
_hc_lastop,
_hc_err)
VALUES
(NEW.et4ae5__dateopened__c,
NEW.et4ae5__numberoftotalclicks__c,
NEW.et4ae5__datebounced__c,
NEW.et4ae5__fromname__c,
NEW.et4ae5__hardbounce__c,
NEW.et4ae5__fromaddress__c,
NEW.et4ae5__softbounce__c,
NEW.name,
NEW.lastmodifieddate,
NEW.et4ae5__opened__c,
NEW.ownerid,
NEW.et4ae5__subjectline__c,
NEW.isdeleted,
NEW.et4ae5__contact__c,
NEW.systemmodstamp,
NEW.lastmodifiedbyid,
NEW.et4ae5__datesent__c,
NEW.et4ae5__dateunsubscribed__c,
NEW.createddate,
NEW.createdbyid,
NEW.et4ae5__lead__c,
NEW.et4ae5__tracking_as_of__c,
NEW.et4ae5__numberofuniqueclicks__c,
NEW.et4ae5__senddefinition__c,
NEW.et4ae5__mergeid__c,
NEW.et4ae5__triggeredsenddefinition__c,
NEW.sfid,
NEW.id,
NEW._hc_lastop,
NEW._hc_err);
END IF;
RETURN NULL;
END;
I added the line in my logic that checks to see if that ID exists in the other table, but I'm not sure if this is the best way to handle it?
AND NEW.id NOT IN (SELECT id FROM archive.individualemailresult__c)) -- this seems expensive

Stack depth Limit INSERT trigger

I have this table on PostgreSQL and I'd like to make a trigger that every time I edit one row of the table publications the last_edit_date is updated to now():
CREATE FUNCTION trigger_update_question_timestamp()
RETURNS "trigger" AS $func$
BEGIN
UPDATE publications SET last_edit_date = now() WHERE publicationid = NEW.publicationid;
RETURN NULL;
END;
$func$ LANGUAGE plpgsql;
CREATE TRIGGER answer_update_question_timestamp AFTER INSERT OR UPDATE ON publications
FOR EACH ROW EXECUTE PROCEDURE trigger_update_question_timestamp();
CREATE TABLE publications
(
publicationid SERIAL PRIMARY KEY,
body VARCHAR(1000) NOT NULL ,
creation_date TIMESTAMP DEFAULT now() NOT NULL,
userid INTEGER NOT NULL,
last_edit_date TIMESTAMP,
CONSTRAINT body_length CHECK (CHAR_LENGTH(body) >= 10 AND CHAR_LENGTH(body) <= 1000),
CONSTRAINT "FK_publications_users"
FOREIGN KEY ("userid") REFERENCES users ("userid") ON DELETE SET NULL ON UPDATE CASCADE
);
I'm using PhpStorm and when I manually edit a line I get the following error:
[54001] ERROR: stack depth limit exceeded Hint: Increase the configuration parameter "max_stack_depth" (currently 2048kB), after ensuring the platform's stack depth limit is adequate. Where: SQL statement "SELECT 1 FROM ONLY "public"."users" x WHERE "userid" OPERATOR(pg_catalog.=) $1 FOR KEY SHARE OF x" SQL statement "UPDATE publications SET last_edit_date = now() WHERE publicationid = NEW.publicationid" PL/pgSQL function trigger_update_question_timestamp()
What does this exactly means? Does it have to do with my trigger or any other definition?
Your trigger is recursive, that is, the trigger function executes an UPDATE that calls the trigger function again.
The solution here is to use a BEFORE INSERT OR UPDATE trigger, that modifies NEW as follows:
BEGIN
NEW.last_edit_date := current_timestamp;
RETURN NEW;
END;

Infinite recursion detected when updating from function (psycopg2, python 2.7, postgres 9.3)

I have a simple table:
CREATE TABLE IF NOT EXISTS someTable (
row_id smallserial PRIMARY KEY,
name text NOT NULL,
creation_date timestamp with time zone DEFAULT current_timestamp,
last_updated_date timestamp with time zone DEFAULT current_timestamp,
created_by text DEFAULT "current_user"(),
last_updated_by text DEFAULT "current_user"()
);
with the following rule:
CREATE OR REPLACE RULE log_update_some_table AS
ON UPDATE TO someTable
DO ALSO
UPDATE someTable
SET last_updated_date = current_timestamp,
last_updated_by = current_user;
and a very simple function in plpgsql:
CREATE OR REPLACE FUNCTION test_update ()
RETURNS void AS $$
BEGIN
UPDATE someTable
SET name = 'test'
WHERE row_id = 1;
END;
$$ LANGUAGE plpgsql;
One would think the function would run just fine, but I get the following error:
psycopg2.ProgrammingError: infinite recursion detected in rules for relation "sometable"
CONTEXT: SQL statement "UPDATE someTable
SET name = 'test'
WHERE row_id = 1"
PL/pgSQL function test_update() line 3 at SQL statement
Why isn't this working and how do I fix it? Thanks!
So your update rule on someTable triggers an update on someTable which executes the rule which updates someTable which executes the rule...
I'd use a simple trigger instead, something like this:
create or replace function log_update_some_table() returns trigger as $$
begin
NEW.last_updated_date = current_timestamp;
NEW.last_updated_by = current_user;
return NEW;
end;
$$ language plpgsql;
create trigger log_update_some_table_trigger
before update on someTable
for each row execute procedure log_update_some_table();
should do the trick. That will modify the row before the update happens rather than adding another update (which triggers the recursion problem) to the queue.

In postgresql, how to check the table with a function if already a record satisfies my conditions exist, then update it or insert new one?

I'm using postgresql and I want to insert or update records with a function in my program. But the thing I have to know is, if a record with conditions I look for is already in database, I will update it, else I will insert a new record. With details:
Table :
CREATE TABLE running_check
(
"UID" character varying(100) NOT NULL,
"CameraIP" character varying(100),
"ErrorStatus" integer,
"IsRunning" boolean,
"CheckTime" timestamp without time zone
);
Some example records:
UID CameraIP ErrorStatus IsRunnning CheckTime
------------------------------------------------------------------
12E 10.0.0.26 0 true now()
C26 10.0.0.22 0 true now()
454 10.0.0.13 3 false now()
I need a function like:
InsertRunningCheckInfo(character varying, character varying, integer, boolean )
And when I call the function, firstly I need to check the records in table
if a record with the same UID already exist,
then if its "IsRunning" value is true, just update the "CheckTime",
else update its ErrorStatus, IsRunning and CheckTime values,
if a record with the same UID doesn't exist,
insert a new record.
Actually, the problem I face is about not knowing how to use a Select query in a function to check its fields then do work, because I'm too new to Postgresql, searched for it for a while but couldn't find something useful for me. Maybe another way is available for this task in Postgresql that I don't know, so wanted to ask you.
Thanks in advance.
In the body of your stored procedure, you can do something like:
SELECT UID
FROM running_check
WHERE UID = myparameter;
IF FOUND THEN
-- UPDATE running_check SET ... WHERE UID = myparameter
ELSE
-- INSERT INTO running_CHECK ...
END IF;
The found boolean checks the previous statement for results. I do not have a Postgres environment available to me anymore, so I can't check this. But this should push you in the right direction.
You can combine both the update and the insert into a single statement with a data modifying common table expression, in which the CTE attempts to update the table and an insert in the main clause add a row if the update returned no rows (ie. if it did not find a row to update).
The code would be similar to:
with
cte_update_attempt as (
update t
set col2 = 'f'
where col1 = 1
returning *)
insert into t (
col1,
col2)
select
1,
'f'
where
not exists (
select null
from cte_update_attempt);
http://sqlfiddle.com/#!12/7d846/2
Good or Bad, my solution to my problem:
CREATE OR REPLACE FUNCTION insertrunningcheckinfo(character varying, character varying, integer, boolean) RETURNS void
LANGUAGE plpgsql
AS $_$
DECLARE
record_number INTEGER;
BEGIN
--LOOP
Select Into record_number "RecordNo" from "running_check" where "UID" = $1 order by "RecordNo" DESC limit 1 ;
UPDATE "running_check"
SET "CheckTime" = now()
WHERE "RecordNo" = record_number and ("IsRunning" = true and $4 = true);
IF found THEN
RETURN;
END IF;
UPDATE "running_check"
SET "CheckTime" = now()
WHERE "RecordNo" = record_number and ("IsRunning" = false and $4 = false) and "Status" = $3;
IF found THEN
RETURN;
END IF;
--BEGIN --BEGIN INSERT
INSERT INTO "running_check"(
"UID",
"CameraIP",
"Status",
"IsRunning",
"CheckTime")
VALUES ($1,
$2,
$3,
$4,
now()
);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- Do nothing
--END; --END INSERT
--END LOOP;
END;
$_$;

Trigger not letting me DELETE in PostgreSQL

I am trying to capture data changes on a table and am executing the following trigger function AFTER INSERT OR UPDATE as well as BEFORE UPDATE OR DELETE:
CREATE OR REPLACE FUNCTION cdc_test_function()
RETURNS trigger AS
$BODY$
DECLARE op cdc_operation_enum;
BEGIN
op = TG_OP;
IF (TG_WHEN = 'BEFORE') THEN
IF (TG_OP = 'UPDATE') THEN
op = 'UPDATE_BEFORE';
END IF;
INSERT INTO cdc_test VALUES (DEFAULT, DEFAULT, op, DEFAULT, DEFAULT, OLD.*);
ELSE
IF (TG_OP = 'UPDATE') THEN
op = 'UPDATE_AFTER';
END IF;
INSERT INTO cdc_test VALUES (DEFAULT, DEFAULT, op, DEFAULT, DEFAULT, NEW.*);
END IF;
RETURN NEW;
END;
My change table (CDC_TEST) is capturing everything properly and I can both INSERT and UPDATE records just fine in my TEST table. However, when I try to DELETE, it records the DELETE entry perfectly in CDC_TEST, but the record remains in my TEST table. If I disable the trigger, then I can DELETE from TEST just fine. Here is the code I used to create my tables as well as the code for my enum:
CREATE TABLE test
(
test_id serial NOT NULL,
value text NOT NULL,
CONSTRAINT test_pkey PRIMARY KEY (test_id )
)
CREATE TABLE cdc_test
(
cdc_test_id bigserial NOT NULL,
cdc_timestamp timestamp with time zone DEFAULT now(),
cdc_opeation cdc_operation_enum,
cdc_user name DEFAULT "current_user"(),
cdc_transaction_id bigint DEFAULT txid_current(),
test_id integer,
value text,
CONSTRAINT cdc_test_pkey PRIMARY KEY (cdc_test_id )
)
CREATE TYPE cdc_operation_enum AS ENUM( 'DELETE', 'INSERT', 'UPDATE_BEFORE', 'UPDATE_AFTER', 'UPDATE' );
Return OLD when the trigger runs for a deletion, NEW for an update.