what is the best way to rename a column if exists inside a pl/sql script using psql.exe and without rolling back the whole script:
transaction block (begin/commit)
begin;
alter table table1 rename column c2 to c3;
commit;
or anonymous code block with exception handling (do/begin/exception/end)
do
$$
begin
alter table table1 rename column c2 to c3;
exception
when undefined_column then raise notice 'column c2 does not exist';
end;
$$;
Related
How can we capture modified dates for all the tables in PGSQL (apart from writing data triggers on each table for inserts /deletes).Can we do it at a generic level for all the tables using event triggers.
for eg: the below query captures modified dates for every function in a generic .
`select now(), nspname, proname, command_tag, prosrc
from pg_event_trigger_ddl_commands() e
join pg_proc p on p.oid = e.objid
join pg_namespace n on n.oid = pronamespace;`
Likewise ,is there any to capture last modified date logs for all tables ?
Have Tried ,
CREATE FUNCTION test_event_trigger_table_rewrite_oid()
RETURNS event_trigger
LANGUAGE plpgsql AS
$$
BEGIN
RAISE NOTICE 'rewriting table % for reason %',
pg_event_trigger_table_rewrite_oid()::regclass,
pg_event_trigger_table_rewrite_reason();
END;
$$;
CREATE EVENT TRIGGER test_table_rewrite_oid
ON table_rewrite
EXECUTE FUNCTION test_event_trigger_table_rewrite_oid();
but the above code only captures time when table DDL is changed.Want it to happen at inserts /deletes
To capture the time when DML statements are run, use a trigger AFTER INSERT OR UPDATE OR DELETE defined FOR EACH STATEMENT.
Hopefully I'm just missing something here, but I would like to be able to do the following:
call a procedure from another procedure.
the parent procedure has a rollback in an exception handler.
the child has an exception handler as well to conceal the inner error from the parent.
the child writes to a table in the error handler.
the child RAISES a new error in the error handler.
the parent catches this error and issues a rollback.
the child procedure's write to a table is preserved.
Possible?
I wrote the below test showing my issue. Please execute the segments separate. Thank you so much ahead of time.
create schema dbo;
drop table dbo.test_transaction_table;
drop table dbo.test_transaction_errortable;
create table dbo.test_transaction_table(a int);
create table dbo.test_transaction_errortable(a int);
/**********************************************************/
create or replace PROCEDURE dbo.test_transaction()
LANGUAGE plpgsql
AS $$
DECLARE v_returnvalue int;
BEGIN
insert into dbo.test_transaction_table(a) values(1);
call dbo.test_transaction_inner(v_returnvalue);
insert into dbo.test_transaction_table(a) values(3);
commit;
EXCEPTION WHEN OTHERS THEN
rollback;
END; $$;
create or replace PROCEDURE dbo.test_transaction_inner(INOUT v_returnvalue INTEGER)
LANGUAGE plpgsql
AS $$
BEGIN
insert into dbo.test_transaction_table(a) values(2);
v_returnvalue = 1 / 0;
v_returnvalue = 3;
EXCEPTION WHEN OTHERS THEN
--I WANT THIS BELOW INSERT TO ALWAYS HAPPEN
--I WANT TO RAISE AN ERROR SO THE PARENT PROCEDURE CAN STILL DO A ROLLBACK IF NECESSARY
insert into dbo.test_transaction_errortable(a) values(1);
COMMIT;
RAISE 'There was one or more errors.';
END; $$;
/**********************************************************/
delete from dbo.test_transaction_table;
delete from dbo.test_transaction_errortable;
call dbo.test_transaction();
select * from dbo.test_transaction_table;
select * from dbo.test_transaction_errortable;
/***********************************************************/
--I expect the dbo.test_transaction_errortable to have 1 row.
--I expect the dbo.test_transaction_table to have 0 rows.
If any of you created/tried Triggers on Greenplum, Please help me to resolve this
I have a table where a "id" column has some value, and i want to put a trigger
Before insert any data in this table, it should call a function/trigger to check
a) if the data is available for "id" in Parent table or not
b) there is already a row available for given "id"
--Table DDL
create table test_trigger(id integer, details text);
--Trigger Function
create or replace function insert_row_trigger() returns trigger as $$
begin
if exists (SELECT 1 FROM test_trigger WHERE id = NEW.id)
Then
Return NULL;
else
Return NEW;
End If;
End;
$$ language plpgsql;
--Trigger Creation
create trigger my_trigger before insert on test_trigger for each row execute procedure insert_row_trigger();
--Drop Trigger
drop trigger my_trigger on test_trigger
ERROR
ERROR: function cannot execute on segment because it accesses
relation "jiodba.test_trigger" (functions.c:151) (seg1
SRDCB0002GPM02:40001 pid=11366) (cdbdisp.c:1477) DETAIL: SQL
statement "SELECT exists (SELECT 1 FROM test_trigger WHERE id = $1 )"
PL/pgSQL function "insert_row_trigger" line 2 at if
********** Error **********
ERROR: function cannot execute on segment because it accesses relation
"jiodba.test_trigger" (functions.c:151) (seg1 SRDCB0002GPM02:40001
pid=11366) (cdbdisp.c:1477) SQL state: XX000 Detail: SQL statement
"SELECT exists (SELECT 1 FROM test_trigger WHERE id = $1 )" PL/pgSQL
function "insert_row_trigger" line 2 at if
Please help me on this.
~I also read somewhere that triggers are not supported in GP
Trigger is a function executed on the segment level for each of the input data rows. The issue is that in Greenplum you cannot execute any query from the segment level as it would require each segment to reconnect to the master to execute it separately, which will cause a connection bloat for a big systems.
The way to overcome this is for instance this way:
Have an unique index on the Parent table
In a single transaction, execute two statements: first, insert into parent select all the rows that does not exist in parent table. Second, insert into target table all the input rows with the keys just inserted to the parent table.
In general, you will have the same logic, but without trigger
Create a RULE statement is a alternative to triggers in GP.
Try this:
https://gpdb.docs.pivotal.io/5280/ref_guide/sql_commands/CREATE_RULE.html#:~:text=The%20Greenplum%20Database%20rule%20system,used%20on%20views%20as%20well.
I'm trying to dynamically partition log entries in Postgres. I have 53 child tables (1 for each week's worth of log entries), and would like to route INSERTs to a child table using a trigger.
I run the function with INSERT INTO log5 VALUES (NEW.*), and it works.
I run the function with the EXECUTE statement instead, and it fails. Within the EXECUTE statement, it's recognizing NEW as a table name and not a variable passed to the trigger function. Any ideas on how to fix? Thanks!
The error:
QUERY: INSERT INTO log5 VALUES (NEW.*)
CONTEXT: PL/pgSQL function log_roll_test() line 6 at EXECUTE statement
ERROR: missing FROM-clause entry for table "new" SQL state: 42P01
My function:
CREATE FUNCTION log_roll_test() RETURNS trigger AS $body$
DECLARE t text;
BEGIN
t := 'log' || extract(week FROM NEW.updt_ts); --child table name
--INSERT INTO log5 VALUES (NEW.*);
EXECUTE format('INSERT INTO %I VALUES (NEW.*);', t);
RETURN NULL;
END;
$body$ LANGUAGE plpgsql;
My trigger:
CREATE TRIGGER log_roll_test
BEFORE INSERT ON log FOR EACH ROW
EXECUTE PROCEDURE log_roll_test();
CREATE FUNCTION log_roll_test()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('INSERT INTO %I SELECT ($1).*' -- !
, to_char(NEW.updt_ts, '"log"WW')) -- child table name
USING NEW; -- !
RETURN NULL;
END
$func$;
You cannot reference NEW inside the query string. NEW is visible in the function body, but not inside EXECUTE environment. The best solution is to pass values in the USING clause.
I also substituted the equivalent to_char(NEW.updt_ts, '"log"WW') for the table name. to_char() is faster and simpler here.
I have created simple trigger :
CREATE OR REPLACE FUNCTION emplacement_libre() RETURNS TRIGGER AS $BODY$
DECLARE
i int;
curseur CURSOR FOR
SELECT COUNT(*) FROM beta2.astres a
INNER JOIN beta2.planetes p ON a.id = p.astre_id
WHERE a.galaxie = NEW.galaxie AND
a.ss = NEW.ss AND
a.position = NEW.position;
BEGIN
OPEN curseur;
LOOP
FETCH curseur INTO i;
IF i>0 THEN
RAISE EXCEPTION 'la planète est déjà occupée';
END IF;
EXIT WHEN NOT FOUND;
END LOOP;
CLOSE curseur;
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;
CREATE TRIGGER emplacement_libre
BEFORE INSERT OR UPDATE ON beta2.astres
FOR EACH ROW EXECUTE PROCEDURE emplacement_libre();
In PostgreSQL doc, it is written about the name of a trigger :
The name to give the new trigger. This must be distinct from the name of any other trigger for the same table. The name cannot be schema-qualified — the trigger inherits the schema of its table. For a constraint trigger, this is also the name to use when modifying the trigger's behavior using SET CONSTRAINTS.
So I expected my trigger to get schema beta2 because I use it in my CREATE TRIGGER request but it's not the case, my trigger is created inside default public schema...why ?
I think the confusion is over the schema the trigger exists in vs the search_path of the trigger, i.e. the schema the function you call is.
The trigger is stored in the same schema as the table. However the search path for the procedure is handled at creation time based on your current search_path settings. So if you want to call a function in a specific schema, you probably want to call by the schema-qualified name.