oracle %rowtype equivalent in postgresql - postgresql

Need to convert the %rowtype of oracle to equivalent in postgresql.
My try:
create table public.test
(
id int,
name varchar(20)
);
insert into test values (1,'A');
insert into test values (2,'B');
insert into test values (3,'C');
Note: I have a variable declare with table %rowtype using which we are checking multiple different column condition's as shown below in the example. It's not working in postgres.
do
$$
declare pky public.test%rowtype;
begin
if pky.id=1
then
raise info 'id:1';
elsif pky.name = 'B'
then
raise info 'name:B';
else
raise info 'false';
end if;
end;
$$;
pky is a input parameter of function in actual code.

The variable is correctly declared, but you have not assigned any value to it. You can do this with a simple assignment statement
do
$$
declare pky public.test;
begin
pky:= '(11,something)'::public.test;
raise info '%', pky;
end;
$$;
INFO: (11,something)
or in a query with into
do
$$
declare pky public.test;
begin
select *
into pky
from public.test
where id = 1;
raise info '%', pky;
end;
$$;
INFO: (1,A)
or use it as a loop variable
do
$$
declare pky public.test;
begin
for pky in
select *
from public.test
loop
raise info '%', pky;
end loop;
end;
$$;
INFO: (1,A)
INFO: (2,B)
INFO: (3,C)
As you can see, the %rowtype is not necessary.

The input parameter can be declared using the table name
CREATE OR REPLACE FUNCTION testfn(pky public.test)
...
For a column type, use %type
CREATE OR REPLACE FUNCTION testfn(pky_id public.test.id%TYPE)
...

Related

trying to create a script with a subblock in postgres

I am trying create this script where if it matches the current database , then do a chunk of work inside a sub block. As this is my first attempt, i cannot get this to work. Any thoughts?
DO
$do$
DECLARE
database CONSTANT text[] := array['prd1', 'prd2'];
BEGIN
IF current_database() = any(database)
THEN
**--execute the below sub block**
DECLARE
v_sql text;
BEGIN
v_sql :=
IF NOT EXISTS (
SELECT FROM pg_catalog.pg_roles -- SELECT list can be empty for this
WHERE rolname = 'dave') THEN
create role dave encrypted password 'md502bbddbc560b6470b360219ac95c13e2';
create schema authorization dave;
END IF;
END
);
-- end of sub block
END IF;
END
$do$;
SQL Error [42601]: ERROR: syntax error at or near "NOT" Position:
231
What i want do is a like where it does alot of actions in a sub block:
DO
$do$
DECLARE
database CONSTANT text[] := array['prd1', 'prd2'];
BEGIN
IF current_database() = any(database)
THEN
**--execute the below sub block**
DECLARE
v_sql text;
BEGIN
v_sql :=
IF NOT EXISTS (
SELECT FROM pg_catalog.pg_roles -- SELECT list can be empty for this
WHERE rolname = 'dave') THEN
create role dave encrypted password 'md502bbddbc560b6470b360219ac95c13e2';
create schema authorization dave;
END IF;
END
do
$$
begin
execute format('grant connect, temporary on database %I to %I', current_database(), 'user_monitor');
end;
$$;
CREATE OR REPLACE FUNCTION test.log_ddl()
RETURNS event_trigger AS $$
DECLARE
audit_query TEXT;
r RECORD;
BEGIN
[...]
END;
$$ LANGUAGE plpgsql;
**-- end of sub block**
END IF;
END
$do$;
Why use sub-block when you can use the IF conditional? 2) Why v_sql := ...? I see no point in assigning the query to a variable. Again all you want is to take CREATE actions based on the IF condition. –
Adrian Klaver

pg_event_trigger_ddl_commands() with DROP TABLE command

CREATE OR REPLACE FUNCTION ddl_command_test()
RETURNS event_trigger
AS $$
DECLARE
obj record;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
RAISE NOTICE '% commnad on oid: %',
tg_tag,
obj.objid;
RAISE NOTICE 'triggered';
END LOOP;
END; $$ LANGUAGE plpgsql;
CREATE EVENT TRIGGER test_ddl ON ddl_command_end
EXECUTE FUNCTION ddl_command_test();
While pg_event_trigger_ddl_commands() function returns info for table creation:
CREATE TABLE test_table(
col int
);
It not prints notification message when table is dropped:
DROP TABLE test_table;
Don't get why, because event-trigger-matrix shows that ddl_​command_​end includes DROP TABLE command also.
Although the documentation event-trigger-matrix says that ddl_command_end can be used for DROP statements, I also struggled with that issue in the past.
Thereby, I found this workaround, which involves creating a specific function that fetches FROM pg_event_trigger_dropped_objects(), to notify when the DROP statement is used.
CREATE OR REPLACE FUNCTION ddl_drop_command_test()
RETURNS event_trigger
AS $$
DECLARE
obj record;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
LOOP
RAISE NOTICE '% commnad on oid: %',
tg_tag,
obj.objid;
RAISE NOTICE 'triggered';
END LOOP;
END; $$ LANGUAGE plpgsql;
Further, you need to use ON sql_drop to create your event trigger. The WHEN TAG can be incremented with DROP SCHEMA and other Postgres objects.
CREATE EVENT TRIGGER drop_test ON sql_drop
WHEN TAG IN ('DROP TABLE')
EXECUTE PROCEDURE ddl_drop_command_test();

Triggers in Postgres: Access NEW fields by name at runtime

In Postgres, someone knows how to substitute the value of the variable in a NEW.variable in a trigger?
For instance, I have a variable with value order_code. I want to execute NEW.variable so that it's getting in fact NEW.order_code.
In detailed:
I have a function to obtain the primary key column of a table:
CREATE FUNCTION getPrimaryKey(_table_name VARCHAR(50))
RETURNS SETOF VARCHAR(50) AS $$
DECLARE
primary_key VARCHAR(50);
BEGIN
FOR primary_key IN SELECT a.attname
FROM pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid
AND a.attnum = ANY(i.indkey)
WHERE i.indrelid = _table_name::regclass
AND i.indisprimary LOOP
RETURN NEXT primary_key;
END LOOP;
END;
$$ LANGUAGE plpgsql;
Then I have a trigger to collect some info when an INSERT is done in a table. The procedure in the trigger is called from several triggers from different tables. That's why it's so generic and I have this need.
What I want is to obtain the primary key of the object inserted.
CREATE FUNCTION logAudit()
RETURNS trigger AS $$
DECLARE primary_key VARCHAR(50);
BEGIN
primary_key := getprimarykey(TG_TABLE_NAME::VARCHAR(50));
INSERT INTO test VALUES (TG_TABLE_NAME);
INSERT INTO test VALUES (NEW.primary_key);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER audit_in_client
AFTER INSERT ON tb_client
FOR EACH STATEMENT EXECUTE PROCEDURE logAudit();
The NEW.primary_key is what is causing me issues. I expect primary_key to be the column name of the source table where the insert happened. What I want in NEW.primary_key is to actually use the value in the variable.
Here is the example of anonymous pl/pgsql block which doing something what you want:
do $$
declare
v pg_database = (pg_database) from pg_database where datname = 'template1';
fname text = 'datname';
n text;
begin
n := to_jsonb(v)->>fname;
raise info '%', n;
end $$;
Output:
INFO: template1
It is working example. In your trigger function it could be something like
declare
pk_name text;
pk_value text;
begin
pk_name := getprimarykey(TG_TABLE_NAME::VARCHAR(50));
pk_value := to_jsonb(NEW) ->> pk_name;
-- Do what you want with pk_value here
return null;
end $$;

stored procedure - pass multiple values for IN clause

I'm writing a stored procedure for Postgres, just do select * from table where id in (value1, value2, ...).
These values will be getting from the variable.
My code:
CREATE OR REPLACE PROCEDURE record_example(v_name varchar(100), v_id int)
LANGUAGE plpgsql
AS $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN select id, updated from mytable where names in (v_name) and id=v_id
LOOP
RAISE INFO 'id = % and updated = %', rec.id, rec.updated;
END LOOP;
END;
$$;
This actually works if I use single value for v_name.
Ex:
call record_example('myname',101);
But if I do multiple values, its not working.
HELP 1 :
call record_example('myname, your_name',101);
It just returned CALL, that's it. Nothing happened.
HELP 2:
Sometimes the v_id variable is optional, so that time the FOR loop should inclue the id=v_id
Ex:
FOR rec IN select id, updated from mytable where names in (v_name)
LOOP
RAISE INFO 'id = % and updated = %', rec.id, rec.updated;
END LOOP;

For loop with dynamic table name in Postgresql 9.1?

I have a plpgslq function which does some data processing and would like to write a for loop, however my table name is not known at design time. Is there any possible way to achieve this? Here is sample code snippet of what I want to achieve:
-- Function: check_data()
-- DROP FUNCTION check_data();
CREATE OR REPLACE FUNCTION check_data()
RETURNS character varying AS
$BODY$declare
dyn_rec record;
tbl_name record;
begin
-- sample dynamic tables
tbl_name := 'cars';
tbl_name := 'trucks';
tbl_name := 'bicycles';
for dyn_rec in select * from format($$s%$$,tbl_name) loop
raise notice 'item is %',dyn_rec.item_no;
end loop;
return 'Processing Ok';
end;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION check_data()
OWNER TO postgres;
You cannot use a variable as table or column identifier in plpgsql embedded SQL ever. A solution is dynamic SQL - EXECUTE or FOR IN EXECUTE statements:
DO $$
DECLARE
tables text[] = ARRAY['table1','table2'];
table_name text;
rec record;
BEGIN
FOREACH table_name IN ARRAY tables
LOOP
FOR r IN EXECUTE format('SELECT * FROM %I', table_name)
LOOP
RAISE NOTICE '%', rec;
END LOOP;
END LOOP;
END; $$