PLS-00653 Error on an empty line (Aggregate/table functions are not allowed in PL/SQL scope) - plsql

I'm trying to run a PL/SQL Script I made but I'm getting an error:
PLS-00653: aggregate/table functions are not allowed in PL/SQL scope
The problem here is not the error in itself but the line where it's being thrown from.
Here's my PL/SQL block which throws this error:
DECLARE
TYPE l_code_arr_typ IS VARRAY(3) OF VARCHAR2(100);
l_code_arr l_code_arr_typ;
l_objet objet_interface_pkg.objet_interface_typ;
l_resultat CLOB;
l_valeur valeur_pkg.valeur_typ;
l_liens lien_objet_interface_pkg.lien_objet_interface_tab;
l_rows NUMBER;
BEGIN
l_code_arr := l_code_arr_typ('champ.fonctions.dossier.particulier',
'champ.fonctions.region.admin',
'champ.fonctions.ministere.organisme');
l_resultat := '{';
FOR l_idx IN 1 .. l_code_arr.count LOOP
l_objet := objet_interface_pkg.obtenir_fnc(p_code => l_code_arr(l_idx),
p_acron_sys => :acronyme);
l_resultat := l_resultat || '"' || l_objet.code || '":[';
l_liens := lien_objet_interface_pkg.obtenir_par_objet_fnc(p_id_objet => l_objet.id_obj);
FOR l_lien IN (SELECT * FROM TABLE(l_liens)) LOOP
l_valeur := valeur_pkg.obtenir_fnc(p_id => l_lien.id_valeur);
l_resultat := l_resultat || '"' || l_valeur.valeur || '"';
FOR l_enfant IN (SELECT *
FROM TABLE(valeur_pkg.obtenir_enfants_fnc(p_id_parent => l_lien.id_valeur,
p_code => l_valeur.valeur))) LOOP
l_resultat := l_resultat || ',"' || l_enfant.valeur || '"';
END LOOP;
END LOOP;
l_resultat := l_resultat || '],';
END LOOP;
<<<<<<<<<< ERROR THROWN HERE (EMPTY LINE)
l_resultat := substr(l_resultat, 1, length(l_resultat) - 1) || '}';
dbms_output.put_line(l_resultat);
END;
The error throws on 34:17 (line:column) which is the fourth line from the end of the code block. As you can see the real problem is that this line is an empty line. Moreover, none of the lines near that empty line contains a call to an aggregate function. So where's the aggregate/table function's call located?
I'm wondering if the problem really comes from my code or if my PL/SQL Developer is broken.
I hope someone can help me...

I found the solution!
The problem is that the function lien_objet_interface_pkg.obtenir_par_objet is PIPELINED and a pipelined return cannot be stored in a variable.
So these 2 lines...
l_liens := lien_objet_interface_pkg.obtenir_par_objet_fnc(p_id_objet => l_objet.id_obj);
FOR l_lien IN (SELECT * FROM TABLE(l_liens)) LOOP
...need to be merge into one single line like so:
FOR l_lien IN (SELECT * FROM TABLE(lien_objet_interface_pkg.obtenir_par_objet_fnc(p_id_objet => l_objet.id_obj))) LOOP
However, I still don't know why PL\SQL Developer said my error comes from line 34 and It'll probably remain a mystery. If anyone have answer to this mystery, please feel free to let me know.

Related

How to remove error code and print only message | PLSQL

I have to print only error message and remove code number
BEGIN
IF :MY_TEXT is null THEN
raise_application_error(-20001,'Text field cannot be empty')
END IF;
Exception
when others then
:MSG := replace(SQLERRM,'^ORA-+:','');
END;
Expected output :
Text filed cannot be empty
You need to use something like this SUBSTR(MSG, INSTR(MSG, ':', 1, 1)+2 )
DECLARE
MY_TEXT VARCHAR2(100);
MSG VARCHAR2(100);
BEGIN
IF MY_TEXT is null THEN
raise_application_error(-20001,'Text field cannot be empty');
END IF;
Exception
when others then
MSG := SQLERRM ;
dbms_output.put_line( SUBSTR(MSG, INSTR(MSG, ':', 1, 1)+2 ) );
END;
/
You could also just change your REPLACE call to a REGEXP_REPLACE as it appears you were trying to use a regular expression to match the error:
:MSG := REGEXP_REPLACE(SQLERRM,'^ORA-[0-9]+: ', NULL);
Match a pattern of 'ORA-' at the start of the line, followed by 1 or digits, then a colon followed by a space and replace with NULL.
Note technically the ', NULL' is not needed in this call but including makes it clear to future maintainers what the intention is here and I would argue would be best practice.

Loop cursor of ALL_MVIEWS generate PL/SQL Error ORA-06502

i wrote a procedure that deals with writing the contents of the QUERY column of ALL_MVIEWS to a file:
DECLARE
v_out_dir_name VARCHAR2(30) := 'DIR_TEST';
v_out_dir_path VARCHAR2(60);
v_count_object_elab NUMBER := 0;
CURSOR c_mviews IS
SELECT
LOWER(MVIEW_NAME) || '.sql' AS FILE_NAME
, QUERY AS SCRIPT
FROM ALL_MVIEWS
;
v_file UTL_FILE.file_type;
BEGIN
FOR r_mview IN c_mviews LOOP
v_file := UTL_FILE.fopen (v_out_dir_name, r_mview.FILE_NAME, 'w');
UTL_FILE.putf (v_file, r_mview.SCRIPT);
UTL_FILE.fclose (v_file);
v_count_object_elab := v_count_object_elab + 1;
END LOOP;
IF v_count_object_elab = 0
THEN
DBMS_OUTPUT.PUT_LINE('NESSUN FILE ELABORATO');
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERRORE = ' || SQLERRM);
IF UTL_FILE.IS_OPEN (v_file) THEN
UTL_FILE.FCLOSE (v_file);
END IF;
RAISE;
END;
/
But the "FOR r_mview IN c_mviews LOOP" statement generates the following error:
Report error -
ORA-06502: PL/SQL: errore di numero o valore
ORA-06512: a line 35
ORA-06512: a line 16
ORA-06512: a line 16
06502. 00000 - "PL/SQL: numeric or value error%s"
*Cause: An arithmetic, numeric, string, conversion, or constraint error
occurred. For example, this error occurs if an attempt is made to
assign the value NULL to a variable declared NOT NULL, or if an
attempt is made to assign an integer larger than 99 to a variable
declared NUMBER(2).
*Action: Change the data, how it is manipulated, or how it is declared so
that values do not violate constraints.
The error is thrown on a materialized view that has QUERY_LEN = 39000.
How can I solve the problem?
Many thanks in advance.
I don't think you are using UTL_FILE correctly. The putf() procedure is for writing and formatting (using printf() style). Try using just put() instead. I did run your script on my database and it executed fine with putf() and put(), but put() is more appropriate.
Also, beware that QUERY is a LONG. If you have a query over 32k I think you will get the error you are seeing. There are some great write-ups online about how awful LONGs are to work with.
I think easiest thing to do is convert the LONG to CLOB with a CREATE TABLE, read that table and then drop it. Here is a routine to do just that. It will create a copy of all_mviews and convert the query to a CLOB and write that in chunks to a file per view.
DECLARE
v_out_dir_name VARCHAR2(30) := 'MVIEW';
v_count_object_elab NUMBER := 0;
v_cursor SYS_REFCURSOR;
v_file utl_file.file_type;
v_start INTEGER;
v_buffer VARCHAR2(1024);
v_file_name VARCHAR2(1024);
v_query CLOB;
table_or_view_does_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT(table_or_view_does_not_exist,
-00942);
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE all_mviews_clob';
EXCEPTION
WHEN table_or_view_does_not_exist THEN
NULL;
END;
EXECUTE IMMEDIATE 'CREATE TABLE all_mviews_clob AS SELECT mview_name, to_lob(query) AS query FROM all_mviews';
OPEN v_cursor FOR q'[SELECT lower(mview_name) || '.sql' AS file_name,
query AS script
FROM all_mviews_clob]';
LOOP
FETCH v_cursor
INTO v_file_name,
v_query;
EXIT WHEN v_cursor%NOTFOUND;
v_file := utl_file.fopen(location => v_out_dir_name,
filename => v_file_name,
open_mode => 'w');
v_start := 1;
FOR i IN 1 .. ceil(dbms_lob.getlength(lob_loc => v_query) / 1024)
LOOP
v_buffer := dbms_lob.substr(lob_loc => v_query,
amount => 1024,
offset => v_start);
IF v_buffer IS NOT NULL THEN
utl_file.put(file => v_file,
buffer => v_buffer);
utl_file.fflush(file => v_file);
END IF;
v_start := v_start + 1024;
END LOOP;
utl_file.fclose(v_file);
v_count_object_elab := v_count_object_elab + 1;
END LOOP;
IF v_count_object_elab = 0 THEN
dbms_output.put_line('no mviews');
END IF;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('eror = ' || SQLERRM);
IF utl_file.is_open(v_file) THEN
utl_file.fclose(v_file);
END IF;
RAISE;
END;
/

Returning table results from a dynamic PL/SQL query

Trying to dynamically generate and execute a PL/SQL statement. It doesn't return results, but executing okay. What I am trying is to get the table name from the schema in first statement (got it right!), and append that to a select statement, and then execute it to return the table results.
DECLARE
LATEST_TABLE VARCHAR2(256);
PostalCode ADM.POSTAL_CODE_201801%ROWTYPE;
BEGIN
SELECT TO_CHAR(max(table_name)) INTO LATEST_TABLE FROM all_tables WHERE owner = 'ADM' AND table_name LIKE 'POSTAL_CODE_%';
LATEST_TABLE := 'begin Select POSTALCODE,LONGITUDE,LATITUDE,MUNICIPALITY_FULL_NAME,LOCAL_NAME,SZONE_NAME,ZONE_NAME,RHA_CODE,RHA_NAME,URBAN,ZONE_RURAL from ADM.'||LATEST_TABLE||' ;end;';
execute immediate LATEST_TABLE into PostalCode;
Exception
When others then
Null;
END;
Why am I not getting any results? Adding
dbms_output.put_line(PostalCode.LONGITUDE || PostalCode.LATITUDE); after execute immediate is also not generating results!
I see a couple of issues here; your code is something like:
declare
vSQL varchar2(1000);
vTabName varchar2(30);
vResult number;
begin
select table_name into vTabName from user_tables;
vSQL := 'begin select a from ' || vTabName || '; end;';
execute immediate vSQL into vResult;
dbms_output.put_line('REsult: ' || vResult);
exception
when others then
null
end;
If you run this, you see nothing, because the dynamic part gives error, but the (dangerous) exception handling hides it; if you would edit the null; into something like
dbms_output.put_line('Error: ' || sqlerrm);
you would get:
Error: ORA-06550: line 1, column 7:
PLS-00428: an INTO clause is expected in this SELECT statement
In fact you dynamic code is like
begin select a from someTable; end;
and this gives error.
A way to do what you need could be:
...
vSQL := 'select a from ' || vTabName;
execute immediate vSQL into vResult;
...

Oracle APEX: Execute immediate on collection

I have the following code:
declare
y pls_integer := 0;
v_msg varchar2(4000);
plsql varchar(4000);
begin
if not apex_collection.collection_exists(p_collection_name=>'P16_COLLECTION') then
wwv_flow.debug('No Apex collection found!');
else
for x in (select * from apex_collections where collection_name = 'P16_COLLECTION' and seq_id > 1 order by seq_id)
loop
y := y+1;
FOR i IN 1..25
LOOP
plsql := 'begin apex_collection.update_member_attribute (p_collection_name=> ''P16_COLLECTION'', p_seq=>' || TO_CHAR(x.seq_id) || ',p_attr_number =>' || TO_CHAR(i) || ',p_attr_value=>wwv_flow.g_f' || TO_CHAR(i, 'FM00') || '(' || TO_CHAR(y) || ')); end;';
wwv_flow.debug(plsql);
EXECUTE IMMEDIATE plsql;
END LOOP;
end loop;
end if;
exception when others then
v_msg := ''||sqlerrm;
wwv_flow.debug('ERR: '||v_msg);
end;
This code is very similar to the one proposed here, but I loop through 25 columns. The issue with Oracle Apex is the max number of chars PL/SQL is allowed to have, so I am unable to just write 25 update_member_attribute - calls.
But instead of a it excecuting I get an error no data found.
I triple checked that the collection P16_COLLECTION exists.
The issue with Oracle Apex is the max number of chars PL/SQL is allowed to have
I'm not sure I understood this statement. It is PL/SQL you use. You declared a local PLSQL variable as VARCHAR2(4000). Why didn't you specify its max allowed size, 32767? Would that help?
Furthermore, saying that you got NO-DATA-FOUND exception: are you sure that this piece of code raised it? Because, there's no SELECT statement in there ... the one you used in a cursor FOR loop can't raise NO-DATA-FOUND; UPDATE either. Therefore, it must be something else, I presume.
Enable DEBUG, run the page and - when you get the error - view debug results and locate the culprit.

Assign value to a field of rowtype where `field name` is a string

I want to assign a value to a rowtype's field but I don't know how to do it.
Suppose that I have a table X inside my database.
Suppose also that I have the following variables
a ( X%ROWTYPE ), representing a row of the table X
b ( VARCHAR2 ), containing a column name of the table X
c ( VARCHAR2 ), containing what I want to store inside a.b
What I want to do : something like a.b := c.
I've come up with something like this :
EXECUTE IMMEDIATE 'SELECT '|| c || ' INTO a.' || b || ' FROM DUAL';
Apparently, this isn't the right way to go. I get a ORA-0095: missing keyword error.
Can anyone help me with this ?
Here is the complete code :
DECLARE
tRow MyTable%ROWTYPE;
col_name VARCHAR(10) := 'Length';
nValue NUMBER(12,4) := 0.001;
dynamic_request VARCHAR(300);
BEGIN
dynamic_request := 'SELECT '|| nValue || ' INTO tRow.' || col_name || ' FROM DUAL';
EXECUTE IMMEDIATE dynamic_request;
END;
Ok, I solved it !
Short answer : Using a global variable does the trick
Answer Development
Let us consider two facts about dynamic PL/SQL blocks (i.e., PL/SQL blocks written as strings, to be executed trough an EXECUTE IMMEDIATE statement)
[1] There is no such thing as variable scope when you create a dynamic PLSQL block. What I mean by that is, if you do something like this :
CREATE OR REPLACE PROCEDURE DynamicVariableAssignment(
theString IN VARCHAR2
)
IS
BEGIN
EXECUTE IMMEDIATE 'BEGIN theString := ''test''; END; ';
END;
it will simply not work because the scope of theString is not transfered to the dynamic PL/SQL block. In other words, the dynamic PL/SQL block doesn't "inherit" of any variable, wherever it is executed.
[2] You might say "OK, no panic, I can give input/output arguments to my dynamic PL/SQL block, right ?". Sure you can, but guess what : you can only give SQL types as in/out ! True PL/SQL types on the other hand, such as a myTable%rowtype, are not accepted as an input for a dynamic PL/SQL block. So the answer of hmmftg won't work either :
-- I've reduced the code to the interesting part
dynamic_request := 'BEGIN :t_row.' || col_name || ':= 0.001; END;';
EXECUTE IMMEDIATE dynamic_request USING IN OUT tRow;
-- (where tRow is of type myTable%ROWTYPE)
since tRow is of MyTable%ROWTYPE, it is not a valid SQL type and is therefore not valid as an input to the dynamic PL/SQL block.
The Solution Who would have thought that global variables would come and save the day ? As we said in [1], we have no reference to any variable outside the dynamic PL/SQL block. BUT we can still access global variables defined in package headers !
Let us assume that I have a package kingPackage in which I define the following :
tempVariable myTable%ROWTYPE;
Then I can do this :
FINAL CODE (body only)
-- Copy tRow into temp variable
kingPackage.tempVariable := tRow;
-- We modify the column of the temp variable
vString := 'BEGIN kingPackage.tempVariable.' || col_val || ' := ' || TO_CHAR(vNumber) ||'; END;';
EXECUTE IMMEDIATE vString;
-- The column value has been updated \o/
tRow := kingPackage.tempVariable;
There you go, fellas !
Have a nice day
try this:
CREATE OR REPLACE PROCEDURE ROW_CHANGER(
tRow IN MyTable%ROWTYPE,
col_name IN VARCHAR,
nValue IN NUMBER)
AS
dynamic_request VARCHAR(300);
BEGIN
dynamic_request := 'BEGIN :t_row.'||COL_NAME ||':= :n_value; END;';
EXECUTE IMMEDIATE dynamic_request
USING IN OUT TROW, IN nValue;
END;
this is because in your EXECUTE IMMEDIATE the tRow MyTable%ROWTYPE is not defined,
so we defined it with using statement.

Resources