When trying to compile in order to execute both functions(#directory) I keep getting a no data error on line 48. (or line 13 for the function). This is the error i receive. NOTE: SYSTEM is the server I'm connected to.
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at "SYSTEM.DEPTPROJECT", line 13
ORA-06512: at line 4
ORA-06512: at line 4
these are my function
SET ECHO ON;
SET FEEDBACK ON;
SET LINESIZE 100;
SET PAGESIZE 100;
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION DEPTPROJECT(departmentNumber IN NUMBER)
RETURN VARCHAR IS
dep# DEPARTMENT.D#%TYPE;
depName DEPARTMENT.DName%TYPE;
counter NUMBER(10,0);
empNumber CHAR(5);
empName VARCHAR2(30);
result VARCHAR2(600);
BEGIN
SELECT D#, DNAME INTO dep#, depName FROM DEPARTMENT WHERE DEPARTMENT.D# = dep#;
result := result || 'Department'|| dep# || '' || depName || chr (10);
FOR i IN(SELECT P# , PTitle , Budget FROM PROJECT WHERE PROJECT.D# = dep# ORDER BY BUDGET DESC)
LOOP
result:= result || chr(9)|| 'Project: ' || i.P# || '' || i.PTitle || i.Budget || chr(10);
FOR j IN(SELECT EMPLOYEE.Name, EMPLOYEE.E# FROM WORKSON INNER JOIN EMPLOYEE ON EMPLOYEE.E# = WORKSON.E# WHERE WORKSON.P# = i.P# ORDER BY EMPLOYEE.NAME ASC )
LOOP
result:= result || chr(10) || j.E# || '' || j.Name || chr(10);
END LOOP;
END LOOP;
RETURN result;
END;
/
BEGIN
FOR x IN(SELECT D# FROM DEPARTMENT)
LOOP
DBMS_OUTPUT.PUT_LINE(DEPTPROJECT(x.D#));
END LOOP;
END;
/
When you are selecting INTO a variable and there are no records returned you should get a NO DATA FOUND error. I believe the correct way to write the above code would be to wrap the SELECT statement with it's own BEGIN/EXCEPTION/END block.
Example:
SET ECHO ON;
SET FEEDBACK ON;
SET LINESIZE 100;
SET PAGESIZE 100;
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION DEPTPROJECT(departmentNumber IN NUMBER)
RETURN VARCHAR IS
dep# DEPARTMENT.D#%TYPE;
depName DEPARTMENT.DName%TYPE;
counter NUMBER(10,0);
empNumber CHAR(5);
empName VARCHAR2(30);
result VARCHAR2(600);
BEGIN
SELECT D#, DNAME INTO dep#, depName FROM DEPARTMENT WHERE DEPARTMENT.D# = departmentNumber;
result := result || 'Department'|| dep# || '' || depName || chr (10);
FOR i IN(SELECT P# , PTitle , Budget FROM PROJECT WHERE PROJECT.D# = dep# ORDER BY BUDGET DESC)
LOOP
result:= result || chr(9)|| 'Project: ' || i.P# || '' || i.PTitle || i.Budget || chr(10);
FOR j IN(SELECT EMPLOYEE.Name, EMPLOYEE.E# FROM WORKSON INNER JOIN EMPLOYEE ON EMPLOYEE.E# = WORKSON.E# WHERE WORKSON.P# = i.P# ORDER BY EMPLOYEE.NAME ASC )
LOOP
result:= result || chr(10) || j.E# || '' || j.Name || chr(10);
END LOOP;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
result := 'Record Not Found';
RETURN result;
END;
/
BEGIN
FOR x IN(SELECT D# FROM DEPARTMENT)
LOOP
DBMS_OUTPUT.PUT_LINE(DEPTPROJECT(x.D#));
END LOOP;
END;
/
I'd guess that line 13 should use the variable being passed to the function:
SELECT D#, DNAME INTO dep#, depName FROM DEPARTMENT WHERE DEPARTMENT.D# = dep#;
Should be:
SELECT D#, DNAME INTO dep#, depName FROM DEPARTMENT WHERE DEPARTMENT.D# = departmentNumber;
You are using following query to fetch dep# and depName but your where clause is WHERE DEPARTMENT.D# = dep# .
SELECT D#, DNAME INTO dep#, depName FROM DEPARTMENT WHERE DEPARTMENT.D# = dep#;
Here dep# is a variable which you have declared but not initialized.
So dep# is null and comparision to NULL will always lead to false which means your query is returning no record.
As you have used INTO in your query, Oracle is seeking for exactly one record from this query but it is returning no records. Hence, you are facing the issue.
According to your logic either change the WHERE clause to include departmentNumber in comparision or assign proper value to dep# before using it in your query.
Also, make sure to wrap your SELECT .. INTO query in BEGIN..EXCEPTION..END to avoid such a dynamic exception.
Cheers!!
In my schema, I've migrated about 250 tables from SQL Server to Oracle. The thing is, no sequences or triggers have been created for any of these tables.
Is there an easy way to generate all the table sequences and triggers rather than manually doing this for every table?
An example of a sequence I need would be:
CREATE SEQUENCE "SYSTEM"."SEC_USERS_ID_SEQ"
MINVALUE 0 MAXVALUE 999999999999999999999999
INCREMENT BY 1
START WITH 23
CACHE 20
NOORDER NOCYCLE NOPARTITION;
And the trigger:
create or replace TRIGGER SEC_USERS_TRIG
before INSERT
ON "SYSTEM"."SEC_USERS"
FOR EACH row
BEGIN
IF inserting THEN
IF :NEW."ID" IS NULL THEN
SELECT SEC_USERS_ID_SEQ.nextval INTO :NEW."ID" FROM dual;
END IF;
END IF;
END;
We can generate scripts using the Oracle data dictionary views (the equivalent of MSSQL INFORMATION_SCHEMA). Find out more.
This example generates CREATE SEQUENCE statements. I have followed your example and accepted the default values, which don't need to be coded. The sequence name is derived from table name concatenated with column name and suffixed with "_SEQ". Watch out for Oracle's thirty character limit on object names!
This loop dynamically queries the table to get the current maximum value of the Primary Key column, which is used to derive the STARTS WITH clause.
declare
curr_mx number;
begin
for lrec in ( select ucc.table_name
, ucc.column_name
from user_constraints uc
join user_cons_columns ucc
on ucc.table_name = uc.table_name
and ucc.constraint_name = uc.constraint_name
join user_tab_columns utc
on utc.table_name = ucc.table_name
and utc.column_name = ucc.column_name
where uc.constraint_type = 'P' -- primary key
and utc.data_type = 'NUMBER' -- only numeric columns
)
loop
execute immediate 'select max ('|| lrec.column_name ||') from ' ||lrec.table_name
into curr_mx;
if curr_mx is null then
curr_mx := 0;
end if;
dbms_output.put_line('CREATE SEQUENCE "'|| user || '"."'
|| lrec.table_name ||'_'|| lrec.column_name || '_SEQ" '
||' START WITH ' || to_char( curr_mx + 1 ) ||';'
);
end loop;
end;
/
This code uses DBMS_OUTPUT, so you can spool it to a file for later use. If you're using an IDE like SQL Developer you may need to enable DBMS_OUTPUT. Follow the guidance in this StackOverflow answer.
If you can guarantee that all your tables have a primary key which is a numeric column called ID then you can simplify the select statement. Contrariwise, if some of your primary keys are compound constraints you will need to handle that.
Obviously I plumped for generating sequences because they're simpler. Writing the more complex trigger implementation is left as an exercise for the reader :)
thanks for the script. I altered it a little bit and did the trigger implementation. Feel free to use it.
declare
curr_mx number;
counter number;
seq_name varchar2 (30);
trigger_name varchar2 (30);
begin
for lrec in ( select ucc.table_name
, ucc.column_name
from user_constraints uc
join user_cons_columns ucc
on ucc.table_name = uc.table_name
and ucc.constraint_name = uc.constraint_name
join user_tab_columns utc
on utc.table_name = ucc.table_name
and utc.column_name = ucc.column_name
where uc.constraint_type = 'P' -- primary key
and utc.data_type = 'NUMBER' -- only numeric columns
)
loop
execute immediate 'select (max ('|| lrec.column_name ||')+1) from ' ||lrec.table_name
into curr_mx;
IF curr_mx is null THEN
curr_mx := 0;
END IF;
IF counter is null THEN
counter := 0;
END IF;
/* check length of sequence name, 30 is max */
IF length(lrec.table_name ||'_'|| lrec.column_name || '_SEQ') > 30 THEN
IF length(lrec.column_name || '_SEQ') > 30 THEN
seq_name := counter || '_PKA_SEQ';
ELSE
seq_name := lrec.column_name || '_SEQ';
END IF;
ELSE
seq_name := lrec.table_name ||'_'|| lrec.column_name || '_SEQ';
END IF;
/* check length of trigger name, 30 is max */
IF length(lrec.table_name || '_PKA_T') > 30 THEN
trigger_name := counter || '_PKA_T';
ELSE
trigger_name := lrec.table_name || '_PKA_T';
END IF;
counter := counter +1;
dbms_output.put_line(
'CREATE SEQUENCE "' || seq_name || '"'
||' START WITH ' || to_char( curr_mx + 1 ) ||';'
);
dbms_output.put_line('/');
dbms_output.put_line(
'CREATE OR REPLACE TRIGGER "' || trigger_name || '"'
|| ' BEFORE INSERT ON "' || lrec.table_name || '"'
|| ' FOR EACH ROW '
|| ' BEGIN '
|| ' :new."' || lrec.column_name || '" := "' || seq_name || '".nextval;'
|| ' END;'
);
dbms_output.put_line('/');
end loop;
end;
I also checked if the names of the sequences and triggers are longer than 30 characters because oracle won´t accept these.
EDIT:
Had to put '/' after each line so you can execute all statements at one run.
I need to reference different fields from the same cursor, but I can't choose which name I'll display.
I already tried to put "... AS 'NAME1"
SET SERVEROUTPUT ON SIZE UNLIMITED;
SET FEEDBACK OFF;
DECLARE
CURSOR GET_TOOLKITS
IS
SELECT PROJ.NAME -- first name
, PROJ.SHORT_NAME
, SNAP.NAME SNAP_NAME --second name
, BRAN.NAME AS BRANCH_NAME -- third name
, SNAP.SNAPSHOT_ID
, PROJ.PROJECT_ID
, TRUNC(PROJ.CREATED_ON)
FROM LSW_SNAPSHOT SNAP
, LSW_PROJECT PROJ
, LSW_BRANCH BRAN
WHERE 1=1
AND SNAP.PROJECT_ID = PROJ.PROJECT_ID
AND SNAP.BRANCH_ID = BRAN.BRANCH_ID
AND PROJ.IS_TOOLKIT = 'T'
AND SNAP.IS_ARCHIVED = 'F'
AND SNAP.NAME IS NOT NULL;
CURSOR GET_SNAPSHOTS
IS
SELECT PROJ.NAME
, PROJ.SHORT_NAME
, SNAP.NAME SNAP_NAME
, BRAN.NAME AS BRANCH_NAME
, SNAP.SNAPSHOT_ID
, PROJ.PROJECT_ID
, TRUNC(PROJ.CREATED_ON)
FROM LSW_SNAPSHOT SNAP
, LSW_PROJECT PROJ
, LSW_BRANCH BRAN
WHERE 1=1
AND SNAP.PROJECT_ID = PROJ.PROJECT_ID
AND SNAP.BRANCH_ID = BRAN.BRANCH_ID
AND PROJ.IS_TOOLKIT = 'F'
AND SNAP.IS_ARCHIVED = 'F'
AND SNAP.NAME IS NOT NULL;
SET_TOOLKITS GET_TOOLKITS%ROWTYPE;
SET_SNAPSHOTS GET_SNAPSHOTS%ROWTYPE;
V_COUNT NUMBER:=0;
BEGIN
OPEN GET_TOOLKITS;
LOOP
FETCH GET_TOOLKITS INTO SET_TOOLKITS;
EXIT WHEN GET_TOOLKITS%NOTFOUND;
V_COUNT := V_COUNT+1;
DBMS_OUTPUT.PUT_LINE (SET_TOOLKITS.NAME --ref first name
|| ' '
|| SET_TOOLKITS.NAME --ref second name
|| ' '
|| SET_TOOLKITS.NAME --ref third name
|| ' ');
END LOOP;
END;
And if anyone can help me with that condition "IS_TOOLKIT" to dynamically change I would be thankful.
Should be this one:
DBMS_OUTPUT.PUT_LINE (SET_TOOLKITS.NAME --ref first name
|| ' '
|| SET_TOOLKITS.SNAP_NAME --ref second name
|| ' '
|| SET_TOOLKITS.BRANCH_NAME --ref third name
|| ' ');
Below is a PL/SQL. The problem is that the flow does not enter loop. I am unable to figure out what is the problem. Both the queries return results, i.e the query in the loop and the query within the loop does return results.
DECLARE
p_file_name VARCHAR2(4000) :='GHCPExtract_100_005_2011052218000700.csv';
v_file_name VARCHAR2(4000) :='' || '''' || p_file_name ||'''';
v_count NUMBER :=0;
v_loop NUMBER :=0;
begin
DBMS_OUTPUT.PUT_LINE( 'BEFORE LOOP');
FOR C IN (
SELECT
S.SOURCE_TRX_KEY_SEGMENT1 ,
S.SOURCE_TRX_KEY_SEGMENT2 ,
S.SOURCE_TRX_KEY_SEGMENT3 ,
S.SOURCE_TRX_KEY_SEGMENT4 ,
S.SOURCE_TRX_KEY_SEGMENT5 ,
S.SOURCE_TRX_KEY_SEGMENT6
FROM DM_RS.STG_GHCP_EXTRACT S
WHERE S.SOURCE_FILE_NAME = v_file_name
)
LOOP
DBMS_OUTPUT.PUT_LINE( 'IN LOOP');
BEGIN
select
1
into
v_count
from
(
select
T.SOURCE_TRX_KEY_SEGMENT1 ,
T.SOURCE_TRX_KEY_SEGMENT2 ,
T.SOURCE_TRX_KEY_SEGMENT3 ,
T.SOURCE_TRX_KEY_SEGMENT4 ,
T.SOURCE_TRX_KEY_SEGMENT5 ,
T.SOURCE_TRX_KEY_SEGMENT6
from
GTT_SEGMENT_ID t
WHERE 1=1
AND
T.SOURCE_TRX_KEY_SEGMENT1 = C.SOURCE_TRX_KEY_SEGMENT1
and
T.SOURCE_TRX_KEY_SEGMENT2 = C.SOURCE_TRX_KEY_SEGMENT2
and
T.SOURCE_TRX_KEY_SEGMENT3 = C.SOURCE_TRX_KEY_SEGMENT3
and
T.SOURCE_TRX_KEY_SEGMENT4 = C.SOURCE_TRX_KEY_SEGMENT4
and
T.SOURCE_TRX_KEY_SEGMENT5 = C.SOURCE_TRX_KEY_SEGMENT5
and
T.SOURCE_TRX_KEY_SEGMENT6 = C.SOURCE_TRX_KEY_SEGMENT6
and t.source_file_name = v_file_name
);
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_count := 0;
END;
IF (v_count = 1)
THEN
V_LOOP := V_LOOP +1;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE( V_LOOP);
END;
DBMS_OUTPUT
BEFORE LOOP
0
v_file_name VARCHAR2(4000) :='' || '''' || p_file_name ||'''';
....
WHERE S.SOURCE_FILE_NAME = '' || '''' || v_file_name ||''''
How many quotes contain SOURCE_FILE_NAME in table?
You can check count rows in query using temporary variable -
SELECT count(1)
INTO cnt_
FROM DM_RS.STG_GHCP_EXTRACT S
WHERE S.SOURCE_FILE_NAME = '''' || v_file_name ||'''';
DBMS_OUTPUT.put_line('rows count: '||cnt_);
Also, check that data in table commited and available in other session.
EDIT:
It's obvious that query in cycle returns no rows, because 'IN LOOP' doesn't printed in output.
How many rows return this query? -
SELECT count(1)
FROM dm_rs.stg_ghcp_extract s
WHERE s.source_file_name = '' || '''' || 'GHCPExtract_100_005_2011052218000700.csv' || ''''
Btw, if you need only count of compared rows you can use one select with EXISTS instead of cycle.
SELECT count(1)
into v_count
FROM dm_rs.stg_ghcp_extract c
WHERE c.source_file_name = v_file_name
AND EXISTS
(SELECT 1
FROM gtt_segment_id t
WHERE t.source_trx_key_segment1 = c.source_trx_key_segment1
AND t.source_trx_key_segment2 = c.source_trx_key_segment2
AND t.source_trx_key_segment3 = c.source_trx_key_segment3
AND t.source_trx_key_segment4 = c.source_trx_key_segment4
AND t.source_trx_key_segment5 = c.source_trx_key_segment5
AND t.source_trx_key_segment6 = c.source_trx_key_segment6
AND t.source_file_name = c.source_file_name)
Earlier our hosted environments were on Oracle but now we have recently switched to postgres. Porting the tables I believe was successfully since it has been working correct for a long time. I have been actually struggling with porting a procedure but is not working for some reason. I went through the documentation part of the ora2pg but I am not able to crack, which I believe is the last piece of the puzzle.
I've started with this one, which looks like this in Oracle:
create or replace
procedure c_audit(anonymous in boolean, aud_level IN varchar)
AUTHID CURRENT_USER IS
script varchar2(32000);
acc_select varchar(100);
open_cursor integer;
returnval integer;
p_id integer;
a_id integer;
p_name varchar2(100);
a_name varchar2(100);
v_count integer;
c_count integer;
doc_count integer;
curr_user varchar(100);
begin
for i in (select a.a_name a_name, a.a_id, p.name p_name, p.id p_id, ds.username username
from c_account a
inner join c_pro p on a.a_id = p.a_id
inner join c_dat_ds_xref x on p.id = x.p_id
inner join c_data ds on x.id_datasource = ds.id
inner join c_conntypes ct on x.id_conntype = ct.id_conntype
where ct.typeid = 'CAPTURE'
order by a.a_name, p.name)
LOOP
curr_user := i.username;
IF anonymous = true
THEN
acc_select := 'select ' || '''' || i.a_id || '''' || ' a_id,' || '''' || i.p_id || '''' || ' p_id';
ELSE
acc_select := 'select ' || '''' || i.a_name || '''' || ' a_name,' || '''' || i.p_name || '''' || ' p_name';
END IF;
IF upper(aud_level) = 'VERBATIM'
THEN
script:= acc_select || '
, count(distinct d.document_id) docCount
, sum(case when v.document_id is null or v.verbatim_type_value = ''NO_VERBATIM_TEXT'' then 0 else 1 end) VerbCount
, sum(case when v.document_id is null then (select to_number(prop_value) verbSize
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_pro = 0) else v.credits end) CreditCount
from ' || i.username || '.p_document d
left outer join (
select vi.document_id, t.verbatim_type_value
, case when dbms_lob.substr(vi.extracted_original,8) = ''<cbnull>''
or t.verbatim_type_value = ''NO_VERBATIM_TEXT''
then coalesce(s2.strucCredit, .25)
else ceil(vi.extracted_original_size/coalesce(s.verbSize, 2048)) end credits
from ' || i.username || '.p_verbatim vi
left outer join ' || i.username || '.pd_verbatim_type t on vi.verbatim_type_id = t.verbatim_type_id
, (
select to_number(prop_value) verbSize
from c_properties
where prop_name = ''METERING.MAXSIZE.VERBATIM''
and id_pro = 0
) s
, (
select to_number(prop_value) strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_pro = 0
) s2
) v on d.document_id = v.document_id';
ELSE
IF upper(aud_level) = 'DOCUMENT'
THEN
script:= acc_select || '
, count(distinct a.document_id) docCount
, sum(credits) creditCount
from (
select d.document_id, ceil(sum(v.extracted_original_size)/coalesce(s.verbSize,2048)) credits
from ' || i.username || '.p_document d
inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
, (
select to_number(prop_value) verbSize
from c_properties
where prop_name = ''METERING.MAXSIZE.VERBATIM''
and id_pro = 0
) s
where t.verbatim_type_value <> ''NO_VERBATIM_TEXT''
and dbms_lob.substr(v.extracted_original,8) <> ''<cbnull>''
group by d.document_id, s.verbSize
union
select d.document_id, coalesce(s2.strucCredit, .25)
from ' || i.username || '.p_document d
, (
select to_number(prop_value) strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_pro = 0
) s2
where d.document_id not in (select distinct v.document_id from ' || i.username || '.p_verbatim v)
union
select distinct d.document_id, coalesce(s2.strucCredit, .25)
from ' || i.username || '.p_document d
inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
, (
select to_number(prop_value) strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_pro = 0
) s2
where (t.verbatim_type_value = ''NO_VERBATIM_TEXT''
or dbms_lob.substr(v.extracted_original,8) = ''<cbnull>'')
) a';
ELSE
dbms_output.put_line('Invalid choice for audit level, no audit generated');
exit;
END IF;
END IF;
begin
open_cursor := dbms_sql.open_cursor;
DBMS_SQL.PARSE(open_cursor, script,
DBMS_SQL.NATIVE);
IF anonymous = true then
dbms_sql.define_column(open_cursor,1,a_id);
dbms_sql.define_column(open_cursor,2,p_id);
else
dbms_sql.define_column(open_cursor,1,a_name,100);
dbms_sql.define_column(open_cursor,2,p_name,100);
end if;
dbms_sql.define_column(open_cursor,3,doc_count);
dbms_sql.define_column(open_cursor,4,v_count);
IF upper(aud_level) = 'VERBATIM' then
dbms_sql.define_column(open_cursor,5,c_count);
end if;
returnval := DBMS_SQL.EXECUTE(open_cursor);
loop
if dbms_sql.fetch_rows(open_cursor) > 0 then
IF anonymous = true then
dbms_sql.column_value(open_cursor,1,a_id);
dbms_sql.column_value(open_cursor,2,p_id);
dbms_sql.column_value(open_cursor,3,doc_count);
dbms_sql.column_value(open_cursor,4,v_count);
IF upper(aud_level) = 'VERBATIM' then
dbms_sql.column_value(open_cursor,5,c_count);
dbms_output.put_line(a_id || ',' || p_id || ',' || doc_count || ',' || v_count || ',' || c_count);
else
dbms_output.put_line(a_id || ',' || p_id || ',' || doc_count || ',' || v_count);
end if;
else
dbms_sql.column_value(open_cursor,1,a_name);
dbms_sql.column_value(open_cursor,2,p_name);
dbms_sql.column_value(open_cursor,3,doc_count);
dbms_sql.column_value(open_cursor,4,v_count);
IF upper(aud_level) = 'VERBATIM' then
dbms_sql.column_value(open_cursor,5,c_count);
dbms_output.put_line(a_name || ',' || p_name || ',' || doc_count || ',' || v_count || ',' || c_count);
else
dbms_output.put_line(a_name || ',' || p_name || ',' || doc_count || ',' || v_count);
end if;
end if;
else
exit;
end if;
end loop;
exception
when others then
--dbms_output.put_line('Error occured. Please check if the current user has Select access to table ' || curr_user || '.p_document ' || curr_user || '.p_verbatim ' || curr_user || '.pd_verbatim_type');
dbms_output.put_line('Error occured. Please login as ' || curr_user || ' and run the following:');
dbms_output.put_line('GRANT SELECT ON ' || curr_user || '.P_DOCUMENT to ' || user ||';');
dbms_output.put_line('GRANT SELECT ON ' || curr_user || '.P_VERBATIM to ' || user ||';');
dbms_output.put_line('GRANT SELECT ON ' || curr_user || '.pd_verbatim_type to ' || user ||';');
end;
end loop;
end;
Does this procedure appears correct with respect to syntax?
CREATE OR REPLACE FUNCTION c_audit(anonymous boolean, aud_level text) RETURNS VOID AS $body$
DECLARE
script text;
acc_select text;
returnval integer;
p_id integer;
a_id integer;
i record;
p_name text;
a_name text;
v_count integer;
c_count integer;
doc_count integer;
curr_user text;
BEGIN
for i in (SELECT a.a_name a_name, a.a_id, p.name p_name, p.id p_id, ds.username username
from c_account a
inner join c_pro p on a.a_id = p.a_id
inner join c_dat_ds_xref x on p.id = x.p_id
inner join c_data ds on x.id_datasource = ds.id
inner join c_conntypes ct on x.id_conntype = ct.id_conntype
where ct.typeid = 'CAPTURE'
order by a.a_name, p.name)
LOOP
curr_user := i.username;
IF anonymous = true
THEN
acc_select := 'SELECT ' || '''' || i.a_id || '''' || ' AccountID,' || '''' || i.p_id || '''' || ' ProjectID';
ELSE
acc_select := 'SELECT ' || '''' || i.a_name || '''' || ' AccountName,' || '''' || i.p_name || '''' || ' ProjectName';
END IF;
IF upper(aud_level) = 'VERBATIM'
THEN
script:= acc_select || '
, count(distinct d.document_id) docCount
, sum(case when coalesce(CAST(v.document_id AS text), '') = '' or v.verbatim_type_value = ''NO_VERBATIM_TEXT'' then 0 else 1 end) VerbCount
, sum(case when coalesce(CAST(v.document_id AS text), '') = '' then (SELECT to_number(prop_value,''9999.99'') verbSize
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_project = 0) else v.credits end) CreditCount
from ' || i.username || '.p_document d
left outer join (
SELECT vi.document_id, t.verbatim_type_value
, case when substr(vi.extracted_original,8) = ''<cbnull>''
or t.verbatim_type_value = ''NO_VERBATIM_TEXT''
then coalesce(s2.strucCredit, .25)
else ceil(vi.extracted_original_size/coalesce(s.verbSize, 2048)) end credits
from ' || i.username || '.p_verbatim vi
left outer join ' || i.username || '.pd_verbatim_type t on vi.verbatim_type_id = t.verbatim_type_id
, (
select to_number(prop_value,''9999.99'') verbSize
from c_properties
where prop_name = ''METERING.MAXSIZE.VERBATIM''
and id_project = 0
) s
, (
select to_number(prop_value,''9999.99'') strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_project = 0
) s2
) v on d.document_id = v.document_id';
SELECT format(script) into script;
ELSE IF upper(aud_level) = 'DOCUMENT'
THEN
script:= acc_select || '
, count(distinct a.document_id) docCount
, sum(credits) creditCount
from (
SELECT d.document_id, ceil(sum(v.extracted_original_size)/coalesce(s.verbSize,2048)) credits
from ' || i.username || '.p_document d
inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
, (
SELECT to_number(prop_value,''9999.99'') verbSize
from c_properties
where prop_name = ''METERING.MAXSIZE.VERBATIM''
and id_project = 0
) s
where t.verbatim_type_value <> ''NO_VERBATIM_TEXT''
and substr(v.extracted_original,8) <> ''<cbnull>''
group by d.document_id, s.verbSize
union
select d.document_id, coalesce(s2.strucCredit, .25)
from ' || i.username || '.p_document d
, (
select to_number(prop_value,''9999.99'') strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_project = 0
) s2
where d.document_id not in (select distinct v.document_id from ' || i.username || '.p_verbatim v)
union
select distinct d.document_id, coalesce(s2.strucCredit, .25)
from ' || i.username || '.p_document d
inner join ' || i.username || '.p_verbatim v on d.document_id = v.document_id
inner join ' || i.username || '.pd_verbatim_type t on v.verbatim_type_id = t.verbatim_type_id
, (
select to_number(prop_value,''9999.99'') strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_project = 0
) s2
where (t.verbatim_type_value = ''NO_VERBATIM_TEXT''
or substr(v.extracted_original,8) = ''<cbnull>'')
) a';
SELECT format(script) into script;
ELSE
SELECT format(script) into script;
exit;
END IF;
END IF;
BEGIN
IF anonymous = true
THEN
IF upper(aud_level) = 'VERBATIM'
THEN
EXECUTE script into a_id, p_id, doc_count, v_count, c_count;
ELSE
EXECUTE script into a_id, p_id, doc_count, c_count;
END IF;
ELSE
IF upper(aud_level) = 'VERBATIM'
THEN
EXECUTE script into a_name, p_name, doc_count, v_count, c_count;
ELSE
EXECUTE script into a_name, p_name, doc_count, c_count;
END IF;
END IF;
GET DIAGNOSTICS returnval := ROW_COUNT;
LOOP
IF returnval > 0
THEN
IF anonymous = true
THEN
IF upper(aud_level) = 'VERBATIM'
THEN
SELECT format ('Information %s, %s, %s, %s, %s', a_id, p_id, doc_count, v_count, c_count);
ELSE
SELECT format ('Information %s, %s, %s, %s', a_id, p_id, doc_count, c_count);
END IF;
ELSE
IF upper(aud_level) = 'VERBATIM'
THEN
SELECT format ('Information %s, %s, %s, %s, %s', a_name, p_name, doc_count, v_count, c_count);
ELSE
SELECT format ('Information %s, %s, %s, %s', a_name, p_name, doc_count, c_count);
END IF;
END IF;
ELSE
EXIT;
END IF;
END LOOP;
EXCEPTION
WHEN others THEN
PERFORM format('Error occured. Please login as %s, %s' , curr_user , ' and run the following:');
PERFORM format('GRANT SELECT ON %s.P_DOCUMENT to %s', curr_user, user);
PERFORM format('GRANT SELECT ON %s.P_VERBATIM to %s', curr_user, user);
PERFORM format('GRANT SELECT ON %s.pd_verbatim_type to %s', curr_user, user);
END;
END LOOP;
END;
$body$
LANGUAGE PLPGSQL
;
ALTER FUNCTION cb_audit(boolean, text) OWNER TO USER;
-- REVOKE ALL ON FUNCTION cb_audit FROM PUBLIC;
The error for failure I get is -
ERROR: too many parameters specified for RAISE
Where: PL/pgSQL function "cb_audit" line 145 at RAISE
I found this to be a good link which I used as a reference
I believe that for porting DBMS_OUTPUT.PUT_LINE, RAISE NOTICE should be the right way. I had encountered another error which was for format of the - to_number(prop_value,'9999.99') which appears right as per the syntax mentioned here but then for some reason when I switched to to_number(prop_value,''9999.99''), I did not get the error but not sure why that should be or even if it should work correctly.
The version of Postgres -
PostgreSQL 9.1.10 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3), 64-bit
Edited:
I actually attempted to modify this function based on the suggestions from Patrick but due to some reason it is not displaying anything on the screen. I added format() at the end of every script to display the script as but it just executed the code and displays c_audit and null. Though I execute the individual subsql and they do return expected counts and results. Am I missing anything?
You are using two types of string "building": the concatenation || operator and the % placeholder. Both are perfectly legal, but use of the format() function is preferred because the code is cleaner and PG guards against things like SQL-injection behind the scenes.
The error you are getting is that one of your RAISE NOTICE commands has a number of % different from the arguments you supply; forgive me for not looking that up but line 145 is hard to find here on SO. In general, you should rewrite all of them like so:
RAISE NOTICE format('GRANT SELECT ON %I.pd_verbatim_type to %I', curr_user, user);
The %I placeholder takes a SQL identifier as input: it can not be NULL and it will be properly quoted to avoid SQL-injection and keyword collisions. (From your code I take it that curr_user is a schema name and user is a role name, both SQL identifiers.)
Note also that the PERFORM statement in a PL/pgSQL function is a SELECT statement that does not return data, but it is executed to examine a side effect, such as asserting that some data exists. As a consequence, there is no PERFORM privilege, use GRANT SELECT instead.
When quoting in PostgreSQL, SQL-identifiers use double quotes, while string literal values use single quotes. The to_number() function definitely requires single quotes.
A few more points to improve your code:
(1) The two sub-selects in the dynamic query like this one...
'select to_number(prop_value,''9999.99'') strucCredit
from c_properties
where prop_name = ''METERING.STRUCT.ONLY.CHARGE''
and id_project = 0'
... are STABLE: you always get the same result. Instead of leaving them in the dynamic SQL, create two variables and put the results in them before you do anything else:
DECLARE
...
verbSize numeric;
strucCredit numeric;
BEGIN
SELECT to_number(prop_value,'9999.99') INTO verbSize
FROM c_properties
WHERE prop_name = 'METERING.MAXSIZE.VERBATIM' AND id_pro = 0;
SELECT to_number(prop_value,'9999.99') INTO strucCredit
FROM c_properties
WHERE prop_name = 'METERING.STRUCT.ONLY.CHARGE' AND id_pro = 0;
...
Then use the varibale verbSize and strucCredit in your dynamic SQL. Like this you do these queries just once, instead of a few times for every iteration.
(2) All of the dynamic queries need a GROUP BY 1, 2 clause.
(3) The clause CASE WHEN coalesce(CAST(v.document_id AS text), '') = '' ... should be written like CASE WHEN v.document_id IS NULL ..., assuming that v.document_id can not be an empty string.
(4) You changed your RAISE NOTICE statements to SELECT format(...). That latter form produces no output, use RAISE NOTICE format(...) instead.
(5) Rewrite your dynamic SQL to use the format() function too.