HiAll,
when I am running the below function getting the error as
ERROR: record "v_preorderrec" is not assigned yet.
Even I initialized v_preorderrec:=null;
Please help me.
CREATE OR REPLACE FUNCTION order_f1(p_order_num text)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
c_order CURSOR FOR
SELECT od.itemno,
it.item_cost,
it.item_code
FROM orderdtls od
LEFT OUTER JOIN itemdtls it ON (od.itemno = it.itemno)
WHERE od.order_num = p_order_num
v_orderrec RECORD;
v_preorderrec RECORD;
BEGIN
v_preorderrec:=null;
open c_order;
loop
fetch c_order
into v_orderrec;
IF NOT FOUND THEN EXIT; END IF;
if coalesce(v_preorderrec.itemno::text, '') = '' or
v_preorderrec.itemno !=
v_orderrec.itemno then
perform modorder(v_orderrec.itemno);
end if;
v_preorderrec := v_orderrec;
end loop;
close c_order;
END;
$function$
;
Thanks in advance.
Related
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
I have the following code: The code is to do a map from demoimage table to fitsheader table. I can run the code without complaining if I comment out the commit; line. But demoimage table doesn't change which make me confused these hours. I search a lot on the problem on google but got no result.
CREATE OR REPLACE FUNCTION public.linkjpeg(
)
RETURNS TABLE(matchtext text)
LANGUAGE 'plpgsql'
COST 100
VOLATILE
ROWS 1000
AS $BODY$
DECLARE
v_jpeghref demoImage.href%type;
v_fitsHREF fitsheader."HREF"%type;
v_uid varchar(500);
v_count bigint;
c_jpeg cursor for SELECT href from demoImage;
c_fitsHREF cursor for select t."HREF" from fitsheader t;
array_matches text[];
array_fitsHREF text[];
array_imgHref text[];
v_arrayDim bigint;
fileBone varchar(500);
fileBoneFits char(500);
c_checkUID cursor for select v_uid=any(array_matches);
c_checkfitsHREF cursor for select v_fitsHREF = any(array_fitsHREF);
c_matchrow cursor for select t.id_fitsheader from fitsheader t where t."HREF" like '%'||fileBoneFits||'%';
v_idfits bigint;
v_matchedFitsHREF fitsheader."HREF"%type;
i bigint;
_sql text;
BEGIN
i := 0;
open c_jpeg;
loop
fetch c_jpeg into v_jpegHref;
v_uid := substring(v_jpegHref from '/member.uid.*');
if(v_uid is not null) then
array_imgHref := string_to_array(v_uid,'.');
v_arrayDim := cardinality(array_imgHref);
fileBone := array_to_string(array_imgHref[1:v_arrayDim-3],'.');
-- replace xxxxx-x-xxxxxS. with null
fileBoneFits := regexp_replace(filebone,'\d\d\d\d.\d.\d\d\d\d\d.S-','');
open c_matchrow;
begin
fetch c_matchrow into v_idfits;
exception
when others then
raise notice '%','not found v_idfits';
end;
raise notice '%','v_idfits'||v_idfits;
begin
execute 'update demoImage set id_fitsheader='|| v_idfits ||' where href ='||quote_literal(v_jpegHref);
**commit;**
exception
when others then
raise notice '%','commit failed'||fileBoneFits;
end;
i := i+1;
raise notice '%', i;
close c_matchrow;
end if;
end loop;
close c_jpeg;
END
The problem is that I can never make the commit executed successfully. I can run the sql above the commit successfully in psql window separately. Can anyone help me figure out where I am wrong? Thanks in advance!
You cannot have transaction statements like COMMIT and ROLLBACK in a PL/pgSQL function; that feature will become available in v11.
I have got a cursor, it is pointing to a SELECT, but this select is generated dynamically. I want to assign the statement after the declarement.
I have done an example working and another example NOT working. This is a simple example to print some data only.
This is the table:
CREATE TABLE public.my_columns (
id serial NOT NULL,
"name" varchar(30) NOT NULL,
/* Keys */
CONSTRAINT my_columns_pkey
PRIMARY KEY (id)
) WITH (
OIDS = FALSE
);
CREATE INDEX my_columns_index01
ON public.my_columns
("name");
INSERT INTO public.my_columns
("name")
VALUES
('name1'),
('name2'),
('name3'),
('name4'),
('name5'),
('name6');
This is the function(I have put the working code and the code not working):
CREATE OR REPLACE FUNCTION public.dynamic_table
(
)
RETURNS text AS $$
DECLARE
v_sql_dynamic varchar;
--NOT WORKING:
--db_c CURSOR IS (v_sql_dynamic::varchar);
--WORKING:
db_c CURSOR IS (SELECT id, name from public.my_columns);
db_rec RECORD;
BEGIN
v_sql_dynamic := 'SELECT id, name from public.my_columns';
FOR db_rec IN db_c LOOP
RAISE NOTICE 'NAME: %', db_rec.name;
END LOOP;
RETURN 'OK';
EXCEPTION WHEN others THEN
RETURN 'Error: ' || SQLERRM::text || ' ' || SQLSTATE::text;
END;
$$ LANGUAGE plpgsql;
Any ideas?
Thank you.
Do you really need the explicit cursor? If you need iterate over dynamic SQL, then you can use FOR IN EXECUTE. It is loop over implicit (internal) cursor for dynamic SQL
FOR db_rec IN EXECUTE v_sql_dynamic
LOOP
..
END LOOP
Little bit more complex solution is described in documentation - OPEN FOR EXECUTE:
do $$
declare r refcursor; rec record;
begin
open r for execute 'select * from pg_class';
fetch next from r into rec;
while found
loop
raise notice '%', rec;
fetch next from r into rec;
end loop;
close r;
end $$;
With this kind of cursor, you cannot to use FOR IN
Is there some way to test an unassigned record for null? (Sorry, sqlfiddle doesn't like my DO block.) Thanks.
DO
$$
DECLARE
r record;
BEGIN
r := null;
if r is null -- ERROR: record "r" is not assigned yet
then
end if;
END
$$;
The error can be avoided by writing:
select null into r;
or alternatively:
r:=row(null);
such that r gets initialized with a tuple structure.
Still, be aware that record variables are unintuitive in other of ways concerning NULLs, and more generally hard to work with outside of their base use case (cursor-style iterating).
If you wrap the test with an exception handler you can use the "not initialized" error to do the check for you.
DO $$
DECLARE
r record;
BEGIN
r := null;
BEGIN
IF r IS NOT NULL THEN
raise notice 'R IS INITIALIZED';
END IF;
EXCEPTION
WHEN OTHERS THEN
raise notice 'R IS NOT INITIALIZED';
END;
END
$$;
select *
from t
where id = p_id
into r;
if found then
...
else
...
end if;
https://www.solvingsoftware.dev/testing-for-an-empty-record-variable-in-postgresql/
I would couple that var with the bool variable and set bool variable whenever record is set:
DO
$$
DECLARE
r record;
rSet bool;
BEGIN
if rSet then -- it is false at the beginning
end if;
-- whenever r is set do this as well
r := select a,b;
rSet = true;
-- whenever that value was consumed, set it to "NULL" again:
return next r.a, r.b;
rSet = false;
END
$$;
We have a function written in pl/sql(oracle) as below:
CREATE OR REPLACE PROCEDURE folder_cycle_check (folder_key IN NUMBER, new_parent_folder_key IN NUMBER) IS
parent_of_parent NUMBER;
ILLEGAL_CYCLE EXCEPTION;
CURSOR parent_c IS
SELECT parent_folder_key FROM folder
WHERE folder_key = new_parent_folder_key;
BEGIN
IF folder_key = new_parent_folder_key THEN
RAISE ILLEGAL_CYCLE;
END IF;
FOR parent_rec IN parent_c LOOP
BEGIN folder_cycle_check(folder_key, parent_rec.parent_folder_key); END;
END LOOP;
END;
Now, i have to rewrite this same procedure in pl/pgsql(PostgreSQL) to achieve similar functionality. Please help me and send that pl/pgsql function.
Edit (formatted code from the comments)
CREATE OR REPLACE FUNCTION folder_cycle_check(IN folder_key INTEGER, IN new_parent_folder_key INTEGER)
RETURNS VOID
AS $procedure$
DECLARE parent_of_parent INTEGER;
PARENT_C CURSOR FOR
SELECT parent_folder_key
FROM folder
WHERE folder_key = new_parent_folder_key;
BEGIN
IF folder_key = new_parent_folder_key THEN
RAISE EXCEPTION 'ILLEGAL_CYCLE';
END IF
FOR parent_rec IN (SELECT parent_folder_key FROM folder WHERE folder_key = new_parent_folder_key) LOOP
PERFORM folder_cycle_check(folder_key,parent_rec.parent_folder_key);
END LOOP;
RETURN;
END;
$procedure$
LANGUAGE plpgsql;
This should work:
CREATE OR REPLACE FUNCTION folder_cycle_check (p_folder_key INT4, p_new_parent_folder_key INT4) RETURNS VOID AS $$
DECLARE
v_parent_rec RECORD;
BEGIN
IF folder_key = new_parent_folder_key THEN
RAISE EXCEPTION 'ILLEGAL_CYCLE';
END IF;
FOR v_parent_rec IN SELECT parent_folder_key FROM folder WHERE folder_key = p_new_parent_folder_key LOOP
PERFORM folder_cycle_check(folder_key, v_parent_rec.parent_folder_key)
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;