Concurrent Usage of Function - postgresql

I have a function called "ChallanNoGenerator" which created sequential number with combination with other columns.
This function is called from multiple other functions, the problem is that at concurrent call it generate duplicate "ChallanNo"
-- DROP FUNCTION "Fee"."ChallanNoGenerator"(uuid);
CREATE OR REPLACE FUNCTION "Fee"."ChallanNoGenerator"(
admissionformid uuid)
RETURNS text
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
declare "ChallanNumber" TEXT;
"CampusCode" TEXT;
"SessionCode" TEXT;
"BusinessUnitCode" TEXT;
"ChallanNumberEx" TEXT;
BEGIN
SELECT
CONCAT(bu."DigitCode", cm."DigitCode"), sn."FullName" INTO "CampusCode", "SessionCode"
FROM
"Admission"."AdmissionForm" AS af,
"Setup"."CampusProgramLink" AS cpl,
"Setup"."ProgramDetails" AS pd,
"Setup"."Campus" AS cm,
"Setup"."SubCity" AS sc,
"Setup"."City" AS ct,
"Setup"."Institution" AS ins,
"Setup"."Session" as sn,
"Setup"."BusinessUnit" AS bu
WHERE
af."CampusProgramId" = cpl."CampusProgramId"
AND pd."ProgramDetailId" = cpl."ProgramDetailId"
AND cpl."CampusId" = cm."CampusId"
AND cm."SubCityId" = sc."SubCityId"
AND sc."CityId" = ct."CityId"
AND cm."InstitutionId" = ins."InstitutionId"
AND cpl."SessionId" = sn."SessionId"
AND af."AdmissionFormId" = admissionformid;
-- SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT ((RIGHT(COALESCE(MAX("ChallanNo"), '000000'), 6)::INT4) + 1) INTO "ChallanNumber" FROM "Fee"."StudentChallan"
WHERE "ChallanNo" LIKE "CampusCode" || "SessionCode" || '%';
SELECT ((RIGHT(COALESCE(MAX("ChallanNo"), '000000'), 6)::INT4) + 1) INTO "ChallanNumberEx" FROM "Fee"."StudentChallanEx"
WHERE "ChallanNo" LIKE "CampusCode" || "SessionCode" || '%';
IF (("ChallanNumber"::INT4) < ("ChallanNumberEx"::INT4)) THEN
"ChallanNumber" := "ChallanNumberEx";
END IF;
IF(LENGTH("ChallanNumber") = 1) THEN
"ChallanNumber" := '00000' || "ChallanNumber";
ELSIF(LENGTH("ChallanNumber") = 2) THEN
"ChallanNumber" := '0000' || "ChallanNumber";
ELSIF(LENGTH("ChallanNumber") = 3) THEN
"ChallanNumber" := '000' || "ChallanNumber";
ELSIF(LENGTH("ChallanNumber") = 4) THEN
"ChallanNumber" := '00' || "ChallanNumber";
ELSIF(LENGTH("ChallanNumber") = 5) THEN
"ChallanNumber" := '0' || "ChallanNumber";
END IF;
RETURN "CampusCode" || "SessionCode" || "ChallanNumber";
END
$BODY$;
ALTER FUNCTION "Fee"."ChallanNoGenerator"(uuid)
OWNER TO postgres;
this is the first function that I have created to generate challan no.
Now I have modify it with
-- DROP FUNCTION "Fee"."ChallanNoGenerator"(uuid, uuid);
CREATE OR REPLACE FUNCTION "Fee"."ChallanNoGenerator"(
admissionformid uuid,
studentchallanid uuid)
RETURNS text
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
declare "ChallanNumber" TEXT;
"CampusCode" TEXT;
"SessionCode" TEXT;
"BusinessUnitCode" TEXT;
"ChallanNumberEx" TEXT;
BEGIN
SELECT pg_advisory_xact_lock(1982);
SELECT
CONCAT(bu."DigitCode", cm."DigitCode"), sn."FullName" INTO "CampusCode", "SessionCode"
FROM
"Admission"."AdmissionForm" AS af,
"Setup"."CampusProgramLink" AS cpl,
"Setup"."ProgramDetails" AS pd,
"Setup"."Campus" AS cm,
"Setup"."SubCity" AS sc,
"Setup"."City" AS ct,
"Setup"."Institution" AS ins,
"Setup"."Session" as sn,
"Setup"."BusinessUnit" AS bu
WHERE
af."CampusProgramId" = cpl."CampusProgramId"
AND pd."ProgramDetailId" = cpl."ProgramDetailId"
AND cpl."CampusId" = cm."CampusId"
AND cm."SubCityId" = sc."SubCityId"
AND sc."CityId" = ct."CityId"
AND cm."InstitutionId" = ins."InstitutionId"
AND cpl."SessionId" = sn."SessionId"
AND af."AdmissionFormId" = admissionformid;
-- SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT ((RIGHT(COALESCE(MAX("ChallanNo"), '000000'), 6)::INT4) + 1) INTO "ChallanNumber" FROM "Fee"."StudentChallan"
WHERE "ChallanNo" LIKE "CampusCode" || "SessionCode" || '%';
SELECT ((RIGHT(COALESCE(MAX("ChallanNo"), '000000'), 6)::INT4) + 1) INTO "ChallanNumberEx" FROM "Fee"."StudentChallanEx"
WHERE "ChallanNo" LIKE "CampusCode" || "SessionCode" || '%';
IF (("ChallanNumber"::INT4) < ("ChallanNumberEx"::INT4)) THEN
"ChallanNumber" := "ChallanNumberEx";
END IF;
SELECT CONCAT("CampusCode", "SessionCode", LPAD("ChallanNumber", 6, '000000')) INTO "ChallanNumber";
UPDATE "Fee"."StudentChallan" SET "ChallanNo" = "ChallanNumber" WHERE "StudentChallanId" = studentchallanid;
RETURN CONCAT('Challan No: ', "ChallanNumber", ' has been generated successfully');
END
$BODY$;
ALTER FUNCTION "Fee"."ChallanNoGenerator"(uuid, uuid)
OWNER TO postgres;
Can any one with good experience to achieve what I want

Related

Delete is not performed after an insertion - postgres

I'm writing a function where I can store specific fields from a table before to delete the record, to keep an historical.
The insertion is working well, but the Delete sometimes is returning Delete 0, even if I have hardcode the id field that I want to delete.
This is my function:
create or replace function deletefromtable(_schema text, _table text, _filter text, _userid int)
returns json as
$func$
DECLARE _record json;
DECLARE target text;
DECLARE mykey TEXT;
DECLARE newvalue TEXT;
DECLARE oldvalue TEXT;
DECLARE columnname TEXT;
begin
SET session_replication_role = replica;
execute format ('select row_to_json(t) from (select * from ' || _schema ||'.' || _table || ' WHERE ' || _filter || ' ) t') into _record;
raise notice 'record: %', _record;
FOR target IN SELECT col from track_settings(_table) LOOP
with vw_listing (new_record) as ( values
(_record::jsonb)
)
SELECT (new_record ->> target)::text INTO newvalue
FROM vw_listing LIMIT 1;
raise notice 'newvalue: %', newvalue;
execute format ('insert into track_history (created_at, table_name, column_name, table_id, user_id, new_val, old_val, pg_user)
values (''' || Now() || ''', ''' || _table || ''', ''' || target || ''', ''' || _filter || ''' ,' || _userid || ', null, ''' || newvalue || ''', current_user )');
END LOOP;
execute ('DELETE FROM public.user WHERE id = 1 ;'); -- THIS LINE IS NOT EXECUTED
SET session_replication_role = default;
RETURN _record;
COMMIT;
end
$func$ language plpgsql;
I tried to enable SET session_replication_role = replica; or SET session_replication_role = default; but is still not working.
The function hasn't any errors and is executing all the statements.
Can someone help me to fix it?

passing table name as parameter in postgresql

I have below table and records i have to pass the table names ("LandXML_QCC_ParcelMarks", "LandXML_QCC_ParcelInformation") as a input parameters (tblparcelmarks, tblparcelinfo) to the below function.
how can i use the this table name parameter in my query in below function. I tried but it is not working.
create table "LandXML_QCC_ParcelMarks"("DPID" TEXT,"From" TEXT,"Name" Text);
insert into "LandXML_QCC_ParcelMarks" values('1','ram','kumar');
create table "LandXML_QCC_ParcelInformation"("DPID" TEXT,"Pntref" Text);
insert into "LandXML_QCC_ParcelInformation" values('1','ram');
CREATE OR REPLACE FUNCTION public.getparcelnonparcelfinal(planid text, tblparcelmarks text, tblparcelinfo text)
RETURNS text AS
$BODY$
declare
tblmark RECORD;
parCount integer;
parNonCount integer;
totalParNonCount text;
tblCou integer;
begin
parCount = 0;
parNonCount = 0;
FOR tblmark IN (SELECT "From", "Name" FROM tblParcelMarks where "DPID" = planid) LOOP
SELECT COUNT(*) into tblCou FROM tblParcelInfo where "DPID" = PlanID and ("Pntref" like '%' || tblMark."From" || ',' || CAST(tblMark."Name" As text) || '%' or "Pntref" like '%' || CAST(tblMark."Name" As text) || ',' || tblMark."From" || '%');
IF tblCou > 0 THEN
parCount = parCount + 1;
ELSE
parNonCount = parNonCount + 1;
END IF;
END LOOP;
totalParNonCount = CAST(parCount As text) || ',' || CAST(parNonCount As text);
return totalParNonCount;
end;
$BODY$
LANGUAGE plpgsql;

Oracle to Postgres Conversion trouble shooting

Converted a Standalone Procedure from Oracle to Postgres but not sure why there is an run error even the code is successfully compiled
Converted the below code from Oracle to Postgres
CREATE OR REPLACE FUNCTION ssp2_pcat.pop_hoa_contracts_for_prod(
)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
DECLARE
C1 CURSOR for
SELECT MARKET_CODE, CONTRACT_COMBO_ID, COUNT(*) FROM
ssp2_pcat.VPF_HOA_CONTRACTS_FOR_PROD A
WHERE start_Date IN
(SELECT MAX(start_date)
FROM VPF_HOA_CONTRACTS_FOR_PROD b
WHERE A.MARKET_CODE = b.MARKET_CODE
AND A.CONTRACT_COMBO_ID = b.CONTRACT_COMBO_ID
AND A.CONTRACT_ID = B.CONTRACT_ID
AND b.start_date <= current_date
AND b.end_date > current_date )
GROUP BY MARKET_CODE, CONTRACT_COMBO_ID
ORDER BY MARKET_CODE, CONTRACT_COMBO_ID;
C2 CURSOR(iMktCode VARCHAR, iCombo integer) for
SELECT MARKET_CODE, CONTRACT_COMBO_ID, CONTRACT_ID
FROM ssp2_pcat.VPF_HOA_CONTRACTS_FOR_PROD A
WHERE start_Date IN
(SELECT MAX(start_date)
FROM ssp2_pcat.VPF_HOA_CONTRACTS_FOR_PROD b
WHERE A.MARKET_CODE = b.MARKET_CODE
AND A.CONTRACT_COMBO_ID = b.CONTRACT_COMBO_ID
AND A.CONTRACT_ID = B.CONTRACT_ID
AND b.start_date <= current_date
AND b.end_date > current_date )
AND MARKET_CODE = iMktCode
AND CONTRACT_COMBO_ID = iCombo
ORDER BY MARKET_CODE, CONTRACT_COMBO_ID, START_DATE;
Contracts VARCHAR(32000);
Contract_Val1 VARCHAR(4000) := NULL;
Contract_Val2 VARCHAR(4000) := NULL;
Contract_Val3 VARCHAR(4000) := NULL;
Contract_Val4 VARCHAR(4000) := NULL;
Contract_Val5 VARCHAR(4000) := NULL;
Contract_Val6 VARCHAR(4000) := NULL;
Contract_Val7 VARCHAR(4000) := NULL;
Contract_Val8 VARCHAR(4000) := NULL;
Num INTEGER;
Cont_Num INTEGER;
l_start TIMESTAMP := clock_timestamp();
l_end TIMESTAMP := clock_timestamp();
Time_Taken VARCHAR(20);
i record;
j record;
BEGIN
l_start := clock_timestamp();
DELETE FROM ssp2_pcat.HOA_CONTRACTS_KH;
FOR i IN C1 LOOP
BEGIN
Num := 0;
Contracts := NULL;
Cont_Num := 1;
FOR j IN C2 (i.MARKET_CODE, i.CONTRACT_COMBO_ID) LOOP
Num := Num + 1;
IF Num = 1 THEN
Contracts := '|' || j.CONTRACT_ID || '|';
ELSE
IF LENGTH(Contracts || j.CONTRACT_ID || '|') > 4000 THEN
PERFORM ssp2_pcat.Assign (Cont_Num, SUBSTRING(Contracts, 1,
LENGTH(Contracts)-1));
Num := 1;
Contracts := '|' || j.CONTRACT_ID || '|';
Cont_Num := Cont_Num + 1;
ELSE
Contracts := Contracts || j.CONTRACT_ID || '|';
END IF;
END IF;
END LOOP;
PERFORM ssp2_pcat.Assign (Cont_Num, Contracts);
IF Cont_Num > 5 THEN
raise notice'%', ('MARKET_CODE: ' || i.MARKET_CODE || ', CONTRACT_COMBO_ID: ' || i.CONTRACT_COMBO_ID || ' has more than 32K in size. These Contracts are left out: ' || Contracts);
END IF;
INSERT INTO HOA_CONTRACTS_KH
(
MARKET_CODE,
CONTRACT_COMBO_ID,
CONTRACT_ID,
CONTRACT_ID2,
CONTRACT_ID3,
CONTRACT_ID4,
CONTRACT_ID5,
LAST_UPDATED
)
VALUES
(
i.MARKET_CODE,
i.CONTRACT_COMBO_ID,
Contract_Val1,
Contract_Val2,
Contract_Val3,
Contract_Val4,
Contract_Val5,
CURRENT_TIMESTAMP::TIMESTAMP(0)
);
Contract_Val1 := NULL;
Contract_Val2 := NULL;
Contract_Val3 := NULL;
Contract_Val4 := NULL;
Contract_Val5 := NULL;
Contract_Val6 := NULL;
Contract_Val7 := NULL;
Contract_Val8 := NULL;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
raise notice'%', ('1) POP_HOA_CONTRACTS_FOR_PROD: ' || SQLERRM);
END;
END LOOP;
RAISE NOTICE 'Function excution time Took: %', l_start;
RAISE NOTICE 'Function excution time Took: %',l_end-l_start;
SELECT l_end-l_start INTO Time_Taken;
raise notice'%',('POP_HOA_CONTRACTS_FOR_PROD Took: ' || Time_Taken );
EXCEPTION
WHEN OTHERS THEN
raise notice'%', ('2) POP_HOA_CONTRACTS_FOR_PROD: ' || SQLERRM);
END;
$BODY$;
The code is compiled successfully, but giving a run time error as follows,
NOTICE: 2) POP_HOA_CONTRACTS_FOR_PROD: cannot begin/end transactions in PL/pgSQL
Debugged the whole code and looks like still I'm unable to identify the issue, can any one help me in making me understand more about Postgres as I'm new to this Database. Found out in unit testing that its not calling the assign function mentioned in the code,

postgresql insert dynamic query result into table in cursor

i have the following function, that generates dynamic query and at the end i want to insert result of dynamic query into table, but the error i get is `
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function report_get_result(integer) line 46 at SQL statement
SQL statement "SELECT report_get_result(20150131)"
PL/pgSQL function inline_code_block line 2 at PERFORM
********** Error **********'
body of the function is:
CREATE OR REPLACE FUNCTION report_get_result (
datekey integer
) returns setof logic_result_rcd
AS
$body$
DECLARE
LogicID integer;
SheetName text;
Row_ID text;
Column_ID text;
FromTable text;
Operation text;
Amount text;
CriteriaType_1 text;
Function_1 text;
Criteria_1 text;
CriteriaType_2 text;
Function_2 text;
Criteria_2 text;
CriteriaType_3 text;
Function_3 text;
Criteria_3 text;
sql text;
INC Integer;
begin
DROP TABLE IF EXISTS loans;
create temp table loans as
select * from loan.vfact_state_principal where "DateKey" = datekey;
DECLARE cursor_logic REFCURSOR;
BEGIN
OPEN cursor_logic for execute ('SELECT "LogicID" FROM logic_table_rcd');
LOOP
FETCH cursor_logic INTO INC;
if not found then exit;
end if;
BEGIN
SELECT LogicID = "LogicID"
,SheetName = "SheetName"
,Row_ID = "Row_ID"::text
,Column_ID = "Column_ID"::text
,FromTable = "FromTable"
,Operation = "Operation"
,Amount = "Amount"
,CriteriaType_1 = CASE WHEN "CriteriaType_1" <> '' THEN ('WHERE
' || "CriteriaType_1") ELSE '' END
,Function_1 = CASE WHEN "Function_1" is null THEN 'Empty' ELSE
"Function_1" END
,Criteria_1 = CASE WHEN "Criteria_1" is null THEN 'Empty' ELSE
"Criteria_1" END
,CriteriaType_2 = CASE WHEN "CriteriaType_2" <> '' THEN ' AND '
|| "CriteriaType_2" ELSE '' END
,Function_2 = CASE WHEN "Function_2" is null THEN 'Empty' ELSE
"Function_2" END
,Criteria_2 = CASE WHEN "Criteria_2" is null THEN 'Empty' ELSE
"Criteria_2" END
,CriteriaType_3 = CASE WHEN "CriteriaType_3" <> '' THEN ' AND '
|| "CriteriaType_3" ELSE '' END
,Function_3 = CASE WHEN "Function_3" is null THEN 'Empty' ELSE
"Function_3" END
,Criteria_3 = CASE WHEN "Criteria_3" is null THEN 'Empty' ELSE
"Criteria_3" END
FROM public.logic_table_rcd WHERE "LogicID" = INC;
sql:= 'INSERT INTO public.logic_result_rcd SELECT ' || INC::text || ', 1, '
|| EntityID::text || ', ' || DateKey::text || ', ' || 'RCD' || ', ' ||
SheetName::text || ', ' || Row_ID::text || ', ' || Column_ID::text || ', '
|| Operation || '(' || Amount || ')' || ' FROM ' || FromTable
|| CriteriaType_1 || ' ' || Function_1 || ' ' || Criteria_1
|| CriteriaType_2 || ' ' || Function_2 || ' ' || Criteria_2
|| CriteriaType_3 || ' ' || Function_3 || ' ' || Criteria_3;
RETURN QUERY EXECUTE sql;
END;
END LOOP;
CLOSE cursor_logic;
END;
END;
$body$
LANGUAGE plpgsql;
assign value with VAR_NAME := ''; construct, here is an example:
t=# create function f(_i int) returns table (a int) as $$
declare sql text;
begin
sql := format('select %s',_i);
return query execute sql;
end;
$$
language plpgsql
;
CREATE FUNCTION
t=# select * From f(3);
a
---
3
(1 row)

Running PostgreSQL stored procedures in SQL console

I have following stored procedure
CREATE FUNCTION runMortalityModel(a_user_id integer) RETURNS integer AS $$
DECLARE
t1 RECORD;
t2 RECORD;
numberOfDeaths integer;
BEGIN
SELECT person.id personId, person.age, condprobmin, condprobmax, random() experiment
INTO t1
FROM person, mortality_cond_prob
WHERE (user_id = a_user_id) and
(person.age = mortality_cond_prob.age);
SELECT personId
INTO t2
FROM t1
WHERE (tmp.condprobmin <= experiment) and (experiment <= tmp.condprobmax);
SELECT COUNT(*)
INTO numberOfDeaths
FROM t2;
RAISE 'numberOfDeaths=%', numberOfDeaths;
EXECUTE
'DELETE '
|| 'FROM person '
|| 'WHERE person.id IN '
|| t2;
RETURN numberOfDeaths;
END
$$ LANGUAGE plpgsql;
When I try to run this stored procedure using
SELECT runMortalityModel(1);
I get the error Relation »t1« doesn't exist.
How can I fix it?
Update 1: Changed the stored procedure declaration to
CREATE OR REPLACE FUNCTION runMortalityModel(a_user_id integer) RETURNS integer AS $$
DECLARE
t1 RECORD;
t2 RECORD;
numberOfDeaths integer;
BEGIN
EXECUTE 'SELECT person.id personId, person.age, condprobmin, condprobmax, random() experiment '
|| 'FROM person, mortality_cond_prob '
|| 'WHERE (user_id = ' || a_user_id || ') and '
|| '(person.age = mortality_cond_prob.age)'
INTO t1;
EXECUTE 'SELECT personId '
|| 'FROM ' || t1
|| ' WHERE (tmp.condprobmin <= experiment) and (experiment <= tmp.condprobmax)'
INTO t2;
EXECUTE 'SELECT COUNT(*) '
|| 'FROM ' || t2
INTO numberOfDeaths;
RAISE 'numberOfDeaths=%', numberOfDeaths;
EXECUTE
'DELETE '
|| 'FROM person '
|| 'WHERE person.id IN '
|| t2;
RETURN numberOfDeaths;
END
$$ LANGUAGE plpgsql;
I see several issues with original code:
You're trying to use RECORD variable as a relation, you should do ... FROM (SELECT t1.*) s instead;
I see no point to select 1 record, then do a query on that record and then perform count(*), you will always have either 0 or 1 as a result.
You second version looks much better, go for it.
This one seems to work. If you have better ideas, please tell them.
CREATE FUNCTION runMortalityModel(a_user_id integer) RETURNS integer AS $$
DECLARE
t1 RECORD;
curRecord RECORD;
numberOfDeaths integer;
BEGIN
numberOfDeaths := 0;
FOR curRecord IN
SELECT person.id personId, condprobmin, condprobmax, random() experiment
FROM person, mortality_cond_prob
WHERE (user_id = a_user_id) and
(person.age = mortality_cond_prob.age)
LOOP
IF (curRecord.condprobmin <= curRecord.experiment) AND (curRecord.experiment <= curRecord.condprobmax) THEN
EXECUTE
'DELETE '
|| 'FROM person '
|| 'WHERE person.id = ' || curRecord.personId;
numberOfDeaths := numberOfDeaths + 1;
END IF;
END LOOP;
RETURN numberOfDeaths;
END
$$ LANGUAGE plpgsql;