I am trying to process an array of string values (single column) called from C++ code, and iterate over it using PL/SQL in a stored procedure.
For that I have created a new type:
CREATE OR REPLACE TYPE "DEVICELIST_OBJTYP" AS OBJECT
(DEVICE VARCHAR2(46 BYTE))
/
CREATE OR REPLACE TYPE devicelist_coltyp AS
TABLE OF devicelist_objtyp;
/
The PL/SQL procedure looks as follows (with seemingly irrelevant logic removed):
PROCEDURE pr_MyProc
( p_UserId IN USERSTAB.USRID%TYPE,
p_DeviceList IN DEVICELIST_OBJTYP,
p_ResCode OUT NUMBER,
p_ResMsg OUT VARCHAR2)
IS
BEGIN
...
FOR i IN 1 .. p_DeviceList.COUNT LOOP
dbms_output.put_line(i);
--more code to follow..
END LOOP;
...
END;
/
When I try to compile the procedure, I fail with:
Warning: Package Body created with compilation errors.
Errors for PACKAGE BODY PA_HANDLE_ASSETS:
LINE/COL ERROR
-------- -----------------------------------------------------------------
135/17 PL/SQL: Statement ignored
135/44 PLS-00302: component 'COUNT' must be declared
BEGIN
*
ERROR at line 1:
ORA-20002:
ORA-06512: at line 4
How should I iterate over the nested table object without providing the .COUNT attribute? When I tried without the .COUNT, I errored out with something in the line of ~"p_DeviceList is not a cursor".
Should I convert the p_DeviceList object to another type of data?
Related
I'm trying to create a package with procedures to insert rows in my tables.
The package header:
CREATE OR REPLACE PACKAGE pkg_developers
AS
PROCEDURE empty_tables;
PROCEDURE add_city(country IN CITIES.COUNTRY%type, zipcode IN CITIES.ZIPCODE%type, city IN CITIES.CITY%type);
END pkg_developers;
package body:
CREATE OR REPLACE PACKAGE BODY pkg_developers AS
PROCEDURE empty_tables
IS
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE cities CASCADE';
/* More truncate statements*/
DBMS_OUTPUT.put_line('Tables truncated successfully.');
END empty_tables;
PROCEDURE add_city(p_country IN cities.COUNTRY%type, p_zipcode IN cities.ZIPCODE%type, p_city IN cities.CITY%type)
IS
BEGIN
INSERT INTO CITIES(COUNTRY, ZIPCODE, city) VALUES(p_country, p_zipcode, p_city);
COMMIT;
END add_city;
END pkg_developers;
How I Execute the procedures:
BEGIN
pkg_developers.empty_tables();
pkg_developers.add_city('Country', '1111', 'City');
END;
The error I get:
[2022-04-16 11:41:48] [72000][4063]
[2022-04-16 11:41:48] ORA-04063: package body "PROJECT.PKG_DEVELOPERS" has errors
[2022-04-16 11:41:48] ORA-06508: PL/SQL: could not find program unit being called: "PROJECT.PKG_DEVELOPERS"
[2022-04-16 11:41:48] ORA-06512: at line 2
[2022-04-16 11:41:48] Position: 0
[2022-04-16 11:41:48] Summary: 1 of 1 statements executed, 1 failed in 15 ms (106 symbols in file)
I did find out that if I remove the parameters for the procedure add_city and hardcode the values it does work, but I can't for the life of me figure out what would be wrong with the parameters, I've looked at examples online and they seem identical to me.
I've also tried to call the exact same procedure but not inside the package and then it works fine as well.
Read what it says:
package body "PROJECT.PKG_DEVELOPERS" has errors
You can't call an invalid package (well, you can - as you already did - but you know the outcome).
So, what's wrong? At first sight, this:
Package specification says:
PROCEDURE add_city (country IN CITIES.COUNTRY%type,
zipcode IN CITIES.ZIPCODE%type,
city IN CITIES.CITY%type);
Package body says:
PROCEDURE add_city (p_country IN cities.COUNTRY%type,
p_zipcode IN cities.ZIPCODE%type,
p_city IN cities.CITY%type)
Find the difference. If you can't, then: in package body, all parameters have prefix p_ (which is correct, i.e. better than declaration in package specification). Whichever option you choose (I suggest the owe with a prefix), use it in both specification and body.
Here is a simple example using Toad for Data Analysts 3.0.1.1734. I have full permissions on the schema JSWEENEY.
Create the table
CREATE TABLE JSWEENEY.TEMP_SQL
(
SQL VARCHAR2(3000)
);
Create the procedure
CREATE OR REPLACE PROCEDURE JSWEENEY.SP_INSERT_SQL
IS
BEGIN
INSERT INTO JSWEENEY.TEMP_SQL(SQL) VALUES('SELECT * FROM TEMP_SQL');
COMMIT;
END JSWEENEY.SP_INSERT_SQL;
/
Execute the procedure:
BEGIN
JSWEENEY.SP_INSERT_SQL;
END;
The first error:
ORA-06550: line 2, column 11:
PLS-00905: object JSWEENEY.SP_INSERT_SQL is invalid
ORA-06550: line 2, column 2: PL/SQL: Statement ignored
Execute the procedure:
BEGIN
EXECUTE JSWEENEY.SP_INSERT_SQL;
END;
The second error:
ORA-06550: line 2, column 10:
PLS-00103: Encountered the symbol "JSWEENEY" when expecting one of the following: := . ( # % ; immediate The symbol ":=" was substituted for "JSWEENEY" to continue.
Any suggestions would be greatly appreciated.
When you compile the procedure you will get an error; if your client doesn't display that then you can query the user_errors view (or all_errors if you're creating it in a different schema) to see the problem. Here it will be complaining that:
LINE/COL ERROR
-------- -----------------------------------------------------------------
6/13 PLS-00103: Encountered the symbol "." when expecting one of the following:
;
It's valid to use the schema name in the create call; but not as part of the end. So if you need to specify the schema at all - which you don't if you're creating an object in your own schema, but your reference to permissions makes it sound like you aren't - then it should be:
CREATE OR REPLACE PROCEDURE JSWEENEY.SP_INSERT_SQL
IS
BEGIN
INSERT INTO JSWEENEY.TEMP_SQL(SQL) VALUES('SELECT * FROM TEMP_SQL');
COMMIT;
END SP_INSERT_SQL;
/
Your second error is because execute on its is a client command (in SQL*Plus and relations), not a PL/SQL statement. The error refers to immediate because PL/SQL does have an execute immediate statement which is used for dynamic SQL, not for making static calls to procedures. Your first syntax to run the procedure is correct, once the procedure itself is valid:
BEGIN
JSWEENEY.SP_INSERT_SQL;
END;
/
try this edited the SQL statement.
create table TEMP_SQL ( col1 varchar2(100));
CREATE OR REPLACE PROCEDURE SP_INSERT_SQL
AS
BEGIN
INSERT INTO TEMP_SQL SELECT * FROM TEMP_SQL;
COMMIT;
END SP_INSERT_SQL;
I am trying to execute a procedure which in turn should execute four other procedures one after the other. How do I acheive this?
Create or replace procedure mainproc
as
begin
tack(400);
phno_insert;
address_insert;
academics_insert;
commit;
end;
Error report:
PLS-00905: Object phno_insert is invalid. PL/SQL: Statement ignored.
PLS-00905: Object address_insert is invalid. PL/SQL: Statement
ignored. PLS-00905: Object academics_insert is invalid. PL/SQL:
Statement ignored.
The problem seems to be in the fact that you have a procedure doing a DDL over an object that is statically referenced in another procedure; for example, if I define:
create table runtimeTable as select 1 as one from dual;
create or replace procedure createTable is
begin
execute immediate 'drop table runtimeTable';
execute immediate 'create table runtimeTable as select 1 as one from dual';
end;
create or replace procedure useTable is
vVar number;
begin
select one
into vVar
from runtimeTable;
--
dbms_output.put_line(vVar);
end;
create or replace procedure createAndUseTable is
begin
createTable;
useTable;
end;
/
when I try to execute createAndUseTable I get:
ORA-04068: existing state of packages has been discarded ORA-04065:
not executed, altered or dropped stored procedure "ALEK.USETABLE"
ORA-06508: PL/SQL: could not find program unit being called:
"ALEK.USETABLE" ORA-06512: at "ALEK.CREATEANDUSETABLE", line 4
ORA-06512: at line 1
If you strictly need to do a DDL runtime, you need to use dynamic SQL to reference the modified object; for example if I define the procedure useTable this way
create or replace procedure useTable is
vVar number;
begin
execute immediate
'select one
from runtimeTable'
into vVar;
--
dbms_output.put_line(vVar);
end;
the call to createAndUseTable will work:
SQL> exec createAndUseTable
1
I have added two fields to a table called CartRequests. We store JSON text in this table sent from our e-commerce package for each of our online orders. This helps us to be able to rebuild orders that fail for any reason. I am adding these 2 fields to facilitate faster searches because, currently, we have to only search the json (CR_JSON) text for values and it takes anywhere from 30 secs to a minute to find anything using a like clause. Most of the time, we are searching for either the CustomerID or OrderID.
I have the following package that puts the sent JSON into the CartRequests table, it was written
by another developer more familiar with PL/SQL:
CREATE OR REPLACE PACKAGE BODY CIC3.CartJSON AS
gCr_id number;
gRequestMsg varchar2(32000);
gResponseMsg varchar2(32000);
gMessageJSON w_MessageJSON_t;
gJSON JSON.JSONStructObj;
procedure SaveRequest is
begin
select CartRequestsSeq.nextval into gCr_id from dual;
insert into CartRequests (cr_id, cr_date ,cr_json) values (gCr_id, sysdate, gRequestMsg);
end;
procedure ExtractRequest is
i number;
vJSON JSON.JSONStructObj;
begin
i:=-1;
vJSON:=JSON.String2JSON(gRequestMsg);
NewJMessageJSON(gMessageJSON,vJSON,i);
gJSON:=JSON.String2JSON(gMessageJSON.ObjectSent);
update CartRequests set
cr_CartTime=gMessageJSON.CartTime,
cr_ObjectSent=gMessageJSON.ObjectSent
-- ***************************************************************
-- I'd like to add these fields but getting following errors:
-- PLS-00487 (39: 27): PLS-00487: Invalid reference to variable 'OBJECTSENT'
-- ORA-00904 (39: 27): PL/SQL: ORA-00904: "GMESSAGEJSON"."OBJECTSENT"."CUSTOMERID": invalid identifier
-- ***************************************************************
-- cr_OrderID=gMessageJSON.ObjectSent.OrderID, -- Adding this field
-- cr_CustomerID=gMessageJSON.ObjectSent.CustomerID -- Adding this field
where cr_id=gCr_id;
end;
END CartJSON;
/
How can I add these 2 fields with no errors?
CREATE OR REPLACE type CIC3.W_MESSAGEJSON_T as object (
CartTime Varchar2(30),
ObjectSent Varchar2(4000),
/
PLS-00487 (39: 27): PLS-00487: Invalid reference to variable 'OBJECTSENT'
ORA-00904 (39: 27): PL/SQL: ORA-00904: "GMESSAGEJSON"."OBJECTSENT"."CUSTOMERID": invalid identifier
The PL/SQL compiler error message above indicates that variable gMessageJSON (that is type of w_MessageJSON_t) doesn't have a member ObjectSent that would have member OrderID or CustomerID. You don't show us w_MessageJSON_t (could be an object type or a record) so it's impossible to say anything more precise.
Maybe the correct syntax would be cr_OrderID = gMessageJSON.OrderID?
I'm stuck with some simple procedure and I can't figure out why.
This is my code, which I'm running in sqlplus:
CREATE OR REPLACE PROCEDURE NormalizeName(fullname IN NVARCHAR2)
IS
BEGIN
SELECT TRIM(fullname) INTO fullname FROM DUAL;
DBMS_OUTPUT.PUT_LINE(fullname);
END NormalizeName;
/
BEGIN
NormalizeName('Alice Wonderland ');
END;
/
When I run it, I get the error:
Warning: Procedure created with compilation errors.
NormalizeName('Alice Wonderland ');
*
ERROR at line 2:
ORA-06550: line 2, column 2:
PLS-00905: object SYSTEM.NORMALIZENAME is invalid
ORA-06550: line 2, column 2:
PL/SQL: Statement ignored
What's wrong?
1) Never create objects in the SYS or SYSTEM schema. Those are reserved for Oracle. If you want to create objects, create a new schema first.
2) When you see that a procedure has been created with compilation errors in SQL*Plus, type show errors to see the errors.
3) The error appears to be that your SELECT statement is trying to write to the fullname parameter. But that parameter is defined as an IN parameter, not IN OUT, so it is read-only. If you define the parameter as IN OUT, though, you could not pass a string constant to the procedure, you'd need to define a local variable in your calling block. It doesn't make a lot of sense to have a procedure that doesn't do anything other than call dbms_output since there is no guarantee that anyone will see the data written to that buffer. My guess is that you really want a function that returns a normalized name. Something like
CREATE OR REPLACE FUNCTION NormalizeName( p_full_name IN VARCHAR2 )
RETURN VARCHAR2
IS
BEGIN
RETURN TRIM( p_full_name );
END;
which you can then call
DECLARE
l_normalized_name VARCHAR2(100);
BEGIN
l_normalized_name := NormalizeName( 'Alice Wonderland ' );
dbms_output.put_line( l_normalized_name );
END;
If you really need a procedure because this is a homework assignment
CREATE OR REPLACE PROCEDURE NormalizeName( p_fullname IN VARCHAR2 )
AS
BEGIN
dbms_output.put_line( TRIM( p_fullname ));
END;
In the real world, you should only be using procedures when you want to manipulate the state of the database (i.e. you're doing INSERT, UPDATE, DELETE, MERGE, etc.). You use functions when you want to perform calculations without changing the state of the database or when you want to manipulate data passed in parameters.