I stuck with Oracle store procedure calling. The code looks simple, but I seriously don't know how to make it work.
This is my code for creating the procedure
DELIMITER ##
CREATE OR REPLACE PROCEDURE updateAward(_total_amount in Number, _no_of_sales in Number, _agent in NUMBER, _id in NUMBER) AS
BEGIN
update Award set total_amount = _total_amount, no_of_sales = _no_of_sales, agent_id = _agent where ID = _id ##
commit ##
So, when I execute it through NetBean (it is the only tool I have at this moment), the code run well.
I also tried to run the compile statement
alter PROCEDURE updateAward compile;
and then, use
select *
from user_errors
where name = 'ORG_SPGETTYPE'
The select return empty, proving that the compile process is ok. However, when I trigger the procedure
call updateAward(1,1,1,1);
It returns the error
Package or function UPDATEAWARD is in an invalid state
and the command
SELECT object_name FROM user_objects WHERE status='INVALID';
return the name of the procedure. How can I solve this problem ?
Update 1:
if I use
BEGIN
updateAward(1,1,1,1);
End;
I got error
Error code 6550, SQL state 65000: ORA-06550: line 2, column 20:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
:= . ( % ;
Update 2:
The reason I put the deliminator is because i got error with ";" when working through some vpn to the other network (still not sure why). So, i updated the code like your answer, but then, with the End; in the end of the procedure and then, get the Invalid SQL statement1. If i remove it and execute (through Netbean), the procedure is created successfully. However, after compiling and check the user_errors, it got the
"PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ; "
First things first, your procedure syntax looks wrong. Don't use DELIMITER as that syntax is specific to MySQL. Instead, try something like the following.
CREATE OR REPLACE PROCEDURE updateAward(_total_amount in Number, _no_of_sales in Number, _agent in NUMBER, _id in NUMBER) AS
BEGIN
update Award set total_amount = _total_amount, no_of_sales = _no_of_sales, agent_id = _agent where ID = _id;
commit;
END;
Firstly, there are a couple of things wrong with your procedure:
You're not using delimiters correctly. Delimiters should be used to terminate the whole procedure, not each line.
The NetBeans SQL window doesn't know SQL very well so it cannot tell when the procedure ends and something else begins. Normally, it uses semicolons (;) to tell when one statement ends and another begins, but stored procedures can contain semicolons within them so that wouldn't work. Instead, we change the delimiter to something else so that the NetBeans SQL window sends the entire stored procedure to the database in one go.
Variable names are not allowed to begin with an underscore (_). In particular, rule 5 in the list of Schema Object Naming Rules at this Oracle documentation page states that
Nonquoted identifiers must begin with an alphabetic character from your database character set.
Underscores are not alphabetic characters.
I've taken your procedure, fixed the use of delimiters and added an extra p onto the front of each parameter name (p for 'parameter'), and I got the following, which ran successfully in NetBeans and created a procedure without errors:
delimiter $$
CREATE OR REPLACE PROCEDURE updateAward(p_total_amount in Number, p_no_of_sales in Number, p_agent in NUMBER, p_id in NUMBER) AS
BEGIN
update Award set total_amount = p_total_amount, no_of_sales = p_no_of_sales, agent_id = p_agent where ID = p_id;
commit;
END;
$$
delimiter ;
Secondly, you write
[...] and then, use
select *
from user_errors
where name = 'ORG_SPGETTYPE'
The select return empty, proving that the compile process is ok.
Um, no. This proves that there are no errors in the procedure ORG_SPGETTYPE (or no procedure with that name exists). Your procedure is named updateAward, which Oracle will capitalise to UPDATEAWARD. Try
select *
from user_errors
where name = 'UPDATEAWARD';
instead.
Related
I am trying to get rows from table using SQLDeveloper by writing simple query:
SELECT * FROM agreements WHERE agreementkey = 1;
SELECT * FROM agreements WHERE agreementkey = 4;
but getting invalid character encountered error. It's not a problem with query(working using other keys, i.e. agreementkey = 3) but with XMLType column in this table - there is something wrong with data in some rows. Is there a way to select this affected row(I know keys of this affected rows) using queries? Maybe export to file or something? Solution of copying value manually is not acceptable.
Create an empty copy of the table and then run an INSERT into it based on a select from the original table but do it using the DML error logging clause.
This should show you any rows that fail to load and the reason for the failure.
CREATE TABLE test_agreements
AS SELECT * FROM agreements
WHERE ROWNUM <1;
INSERT INTO test_agreements
SELECT *
FROM agreements
LOG ERRORS REJECT LIMIT UNLIMITED
This will create you an error logging table called ERR$TEST_AGREEMENTS which you can query to find the problem rows.
Problem is in WHERE key = 1 cause key is a Reserve Word in Oracle. You should escape it like
SELECT * FROM table WHERE "key" = 1;
KEY is a reserved word so to overcome that you need to use double quotes "".
SELECT * FROM table WHERE "key" = 1;
I think the problem can be solved by putting the argument in quotes:
SELECT * FROM agreements WHERE agreementkey = "1";
I wish I were familiar with XML, but I have run into this with VARCHAR2 columns that are supposed to have all numeric values. Oracle looks at the request
where agreementkey = 1
and tries to convert agreementkey to a number rather than 1 to a varchar2.
If the database contains invalid characters I would try one the following:
Maybe the solution of BriteSponge will work, using an insert statemant with error logging clause.
Use datapump to export the data to a file. I think the log will contain information to identify the invalid columns.
There was a tool called "character set scanner" that checked the characters of the data of a table, here is some documentation: CSSCAN. Or maybe you can use the Database Migration Assistent for Unicode (DMU) mentioned in the same manual.
4- You can write a small PL/SQL program that retrieves the rows row by row and in case of an error catches the exception and notifies you about the row.
DECLARE
invalid_character_detected EXCEPTION;
PRAGMA EXCEPTION_INIT(invalid_character_detected, ???); begin
for SELECT rowid into rec FROM agreements do begin
for
SELECT * into dummy
FROM agreements
where rowid=rec.rowid
do
null;
end loop;
except
WHEN invalid_character_detected THEN
dbms_ouput.put_line(rec.rowid)
end;
end loop;
end;
I did not compile and test the program. ??? is the (negative) error code, e.g. -XXXXX if the error is ORA-XXXXX
This question already has an answer here:
Create user from string variables in a PL/SQL block
(1 answer)
Closed 6 years ago.
I am programming Java application on Oracle database. The PL/SQL statement I am using is:
DECLARE
USER_COUNT INTEGER;
BEGIN
SELECT COUNT(*) INTO USER_COUNT FROM dba_users WHERE username=?;
IF (USER_COUNT = 0) THEN
EXECUTE IMMEDIATE 'CREATE USER ? IDENTIFIED BY ?';
EXECUTE IMMEDIATE 'GRANT CREATE SESSION, CREATE TABLE, CREATE ANY INDEX TO ?';
ELSE
raise_application_error(-20101, 'User ? already exists. Please drop it or choose another username.');
END IF;
END;
/
But I got a lot of 'invalid column index' errors if there are quotes around question marks. For instance:
EXECUTE IMMEDIATE 'CREATE USER ? IDENTIFIED BY ?';
is not working, but
EXECUTE IMMEDIATE CREATE USER ? IDENTIFIED BY ?;
is good.
However if I choose to use second form, I got another syntax error:
java.sql.SQLException: ORA-06550: line 6, column 23:
PLS-00103: Encountered the symbol "CREATE" when expecting one of the following:
( - + case mod new not null <an identifier>
<a double-quoted delimited-identifier> <a bind variable>
continue avg count current exists max min prior sql stddev
sum variance execute forall merge time timestamp interval
date <a string literal with character set specification>
<a number> <a single-quoted SQL string> pipe
<an alternatively-quoted string literal with character set specification>
<an alternat
Please advise what to do.
If ? is a bind variable, you can't use those in a create user statement. It needs the complete literal text.
btw did you really mean CREATE ANY INDEX? That will let the new user index any table in the entire database. Normally plain CREATE INDEX should be sufficient.
It would better to define one or more roles in advance, and then just grant those to the new user, rather than hardcoding every possible privilege. Should the user be allowed to create views, procedures, types etc? If it's going to be a developer account, should it be able to debug code and define locks? And so on...
(Just out of interest, do you write Java in uppercase, or just PL/SQL?)
Try this.
DECLARE
USER_COUNT INTEGER;
BEGIN
SELECT COUNT (*)
INTO USER_COUNT
FROM dba_users
WHERE username =?;
IF USER_COUNT = 0 THEN
EXECUTE IMMEDIATE 'CREATE USER '||?|| 'IDENTIFIED BY '||?;
EXECUTE IMMEDIATE 'GRANT CREATE SESSION, CREATE TABLE, CREATE ANY INDEX TO '|| ?;
ELSE
raise_application_error(-20101, 'User ? already exists. Please drop it or choose another username.');
END IF;
END;
/
You cannot use bind variables when working with DDL in EXECUTE IMMEDIATE.
You can refer # below link, a better explaination is available.
Create user from string variables in a PL/SQL block
I am trying to execute a whole directory of .SQL files in Java.
I'm struggling to execute a stored procedure. I have found this so far (the most helpful) including a dead link, sadly. I have downloaded liquibase also, but I cannot figure out how I should use it for this purpose.
In my current code, I split the files including procedures into different statements:
(Statements split in a Vector[String] and executed in a for loop)
Example:
//File f;
//Statement st;
Vector<String> vProcedure = getProcedureStatements(f, Charset.defaultCharset(), "//");
for (Iterator<String> itr = vProcedure.iterator(); itr.hasNext();)
st.execute(itr.next());
System.out.println(f.getName() + " - done executing.");
The Vector contains the four elements (see SQL-Code #SPLIT x).
DROP PROCEDURE IF EXISTS `Add_Position`; #SPLIT 1
DELIMITER // #SPLIT 2
CREATE PROCEDURE `Add_Position`
(
IN iO_ID INT,
IN iCID INT,
IN iWID INT,
IN iA INT
)
BEGIN
#statements;
END
// #SPLIT 3
DELIMITER ; #SPLIT 4
Result when trying to execute #SPLIT 2:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER //' at line 1
Q: Could anyone tell me if there's a exteral library I could use, or how liquibase does work ? I can't get it to work the JDBC-way.
The statement DELIMITER \\ is not actually SQL - it is a command that is interpreted by the mysql command-line tool (and possibly also their GUI tool), but if you pass it straight to the execution engine, you will get this syntax error.
Unfortunately, Liquibase uses ; as the default statement separator, so it is difficult to get this sort of thing to work using SQL files and Liquibase. I have put in a pull request to allow setting the delimiter from the Liquibase command line - see https://github.com/liquibase/liquibase/pull/361.
I have declared package level type this way (using Oracle XE 11):
create or replace PACKAGE RM_TYPES
AS
TYPE RECPPART_ARR IS TABLE OF RM_RECEPCIONPARTIDAS%ROWTYPE;
END RM_TYPES;
I have SP like this:
create or replace PROCEDURE "RM_TRY_B" (partidas OUT RM_TYPES.RECPPART_ARR) as
begin
SELECT * BULK COLLECT INTO partidas FROM rm_recepcionpartidas;
end;
I have java code like this:
CallableStatement cstmt = conn.prepareCall("{call RM_TRY_B(?)}");
cstmt.registerOutParameter(1, OracleTypes.ARRAY, "RM_TYPES.RECPPART_ARR");
cstmt.execute();
Array a = cstmt.getArray(1);
It gives me an excepcion:
Exception in thread "main" java.sql.SQLException: invalid name pattern: RM_TYPES.RECPPART_ARR
I have already granted access to package to my user by issuing this command to oracle:
GRANT EXECUTE ON RM_TYPES TO myuser;
I used this as reference: https://docs.oracle.com/database/121/JJDBC/apxref.htm#JJDBC28913 (section named: Creating Java level objects for each row using %ROWTYPE Attribute
Where did I do wrong?
I've also try passing in this name in my java code: "RECPPART_ARR" or "MYSCHEMA.RM_TYPES.RECPPART_ARR" none of them works.
Then I read someone said this on stackoverflow: java - passing array in oracle stored procedure : "actually the problem is that any type created within a package is not visible by java. If I create the type at schema level then it works. "
Is it true?
Then maybe I should define an alias at schema level?
How? I tried "CREATE SYNONYM":
CREATE PUBLIC SYNONYM RECPPART_ARRAY FOR RM_TYPES.RECPPART_ARR;
And then (tried to modify my SP):
create or replace PROCEDURE "RM_TRY_B" (partidas OUT RECPPART_ARRAY) as
begin
SELECT * BULK COLLECT INTO partidas FROM rm_recepcionpartidas;
end;
But this time this SP wouldn't compile, with this error message in my SQLDeveloper: Error(1,36): PLS-00905: object MYSCHEMA.RECPPART_ARRAY is invalid.
Then I tried using the previous definition of my sp:
create or replace PROCEDURE "RM_TRY_B" (partidas OUT RM_TYPES.RECPPART_ARR) as
begin
SELECT * BULK COLLECT INTO partidas FROM rm_recepcionpartidas;
end;
And modified my Java code to use the synomim instead:
CallableStatement cstmt = conn.prepareCall("{call RM_TRY_B(?)}");
cstmt.registerOutParameter(1, OracleTypes.ARRAY, "RECPPART_ARRAY");
cstmt.execute();
Array a = cstmt.getArray(1);
Still, exception, with message: Fail to construct descriptor: Unable to resolve type: "MYSCHEMA.RECPPART_ARRAY"
ADDITION
Some other info I just found:
http://oracle.developer-works.com/article/5227493/%22invalid+name+pattern%22++when+trying+to+user+packaged+TYPE
Someone wrote: I had the same issue. Managed to solve it by creating public synonym and giving grants.
As you see, I did that already, but no luck for me.
ADDITION
Or ... maybe something like this in oracle (after reading this: http://docs.oracle.com/javadb/10.10.1.2/ref/rrefsqljgrant.html ):
create or replace PACKAGE RM_TYPES
AS
TYPE RECPPART_ARR IS TABLE OF RM_RECEPCIONPARTIDAS%ROWTYPE;
END RM_TYPES;
sqlplus (logged in as sys as SYSDBA)> GRANT USAGE ON TYPE RM_TYPES.RECPPART_ARR TO myuser;
CREATE PUBLIC SYNONYM RECPPART_ARRAY FOR RM_TYPES.RECPPART_ARR;
create or replace PROCEDURE "RM_TRY_B" (partidas OUT RM_TYPES.RECPPART_ARR) as
begin
SELECT * BULK COLLECT INTO partidas FROM rm_recepcionpartidas;
end;
....
I tried it..., even logged in using user "sys" as SYSDBA .... I got an error when issuing grant:
Error starting at line : 1 in command -
GRANT USAGE TYPE ON RM_TYPES.RECP_ARR TO myuser
Error report -
SQL Error: ORA-00990: missing or invalid privilege
00990. 00000 - "missing or invalid privilege"
*Cause:
*Action:
I'm running out of idea now.
JDBC Support for PL/SQL Data Types as Parameters is a new feature of Oracle 12c.
PL/SQL types look and act like regular types; they can be used in SQL and other contexts, they have a TYPE_OID and TYPECODE, and they have a data dictionary view (DBA_PLSQL_TYPES). One odd difference is that PL/SQL types do not show up in DBA_OBJECTS.
In older versions you must create a TYPE as a stand-alone object in order to use it outside of PL/SQL. Code like this can create the objects:
CREATE OR REPLACE TYPE RECPPART_REC IS OBJECT
(
--list RM_RECEPCIONPARTIDAS columns here. %ROWTYPE is not available in SQL.
);
CREATE OR REPLACE RECPPART_ARR IS TABLE OF RECPPART_REC;
You could make use of a little-known feature in PL/SQL: PIPELINED functions. An example:
create table tab (
id number(7)
);
/
insert into tab values (1);
insert into tab values (2);
create or replace package pkg
as
type typ is table of tab%rowtype;
end pkg;
/
create or replace procedure proc (param out pkg.typ) as
begin
select * bulk collect into param from tab;
end;
/
create or replace function func return pkg.typ pipelined as
begin
for rec in (select * from tab) loop
pipe row(rec);
end loop;
end;
/
select * from table(func);
The above will yield:
ID
--
1
2
So you can materialise the table type also easily from JDBC.
The reason for this is the fact that every pipelined function implicitly creates a top-level SQL type that is of the same type as your PL/SQL table type. In the above case something like:
create or replace type SYS_PLSQL_29848_13_1 as object (ID NUMBER(7));
create or replace type SYS_PLSQL_29753_9_1 as table of SYS_PLSQL_29848_13_1;
This is more of a side-note. In general, you should probably prefer the approach suggested by Jon Heller
I have a plpgsql script (editor's note: it's a function, actually) that contains a loop which drops the primary key constraint for some tables that were generated by eclipse-link. It looks something like this:
CREATE OR REPLACE FUNCTION remove_tables_constraints()
RETURNS boolean AS
$BODY$
DECLARE
constraint_statment text;
BEGIN
FOR constraint_statment IN
SELECT 'ALTER TABLE '||nspname||'.'||relname||' DROP CONSTRAINT '||conname
FROM pg_constraint
INNER JOIN pg_class ON conrelid=pg_class.oid
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace
where relname not in('exclude_table')
ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END,contype,nspname,relname,conname LOOP
raise notice 'remove_tables_constraints run [%]', constraint_statment;
EXECUTE constraint_statment;
END LOOP;
RETURN true;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE COST 100;
select remove_tables_constraints();
The script is executed using:
Statement st = connection.createStatement();
st.execute(scriptStringloadedFromFile);
The script worked (and under some circumstances still works) fine.
It stopped working after changing the primary key of the tables from an int to a uid. The loop halts in mid execution without displaying any error messages (debug is set to the finest level).
The weird part is that the script does work, even after the change, if I just paste it into the psql shell instead of executing it from code. Moreover, it works when executing it from the java code if I unpack the loop and just write all the statements that the loop performs inline.
I've spent a couple of days on this and I'm clueless as to how to continue. Any ideas ?
I see a couple of problems:
You need to sanitize identifiers or you can get exceptions or worse, open an attack path for SQL injection. Identifiers can be illegal strings unless double-quoted. There are several ways to let Postgres take care of that automatically.
I used two forms below:
format() with %I parameter conversion (Postgres 9.1+)
Let Postgres coerce a regclass type, which is even better for table names (IMO).
You function is dropping all constraints, while you only want to drop PK constraints (contype = 'p') according to your description.
You are not excluding the system catalog and other system schemas. This should fail, no matter what.
Do not quote the language name plpgsql. It's an identifier.
Everything put together it could look something like this:
CREATE OR REPLACE FUNCTION remove_tables_constraints()
RETURNS boolean AS
$func$
DECLARE
constraint_statment text;
BEGIN
FOR constraint_statment IN
SELECT format('ALTER TABLE %s DROP CONSTRAINT %I'
, c.oid::regclass, o.conname)
FROM pg_constraint o
JOIN pg_class c ON c.oid = o.conrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname <> 'exclude_table' -- just one? then <>
AND o.contype = 'p' -- only pk constraints
AND n.nspname NOT LIKE 'pg%' -- exclude system schemas!
AND n.nspname <> 'information_schema' -- exclude information schema!
ORDER BY n.nspname, c.relname, o.conname -- commented irrelevant item
LOOP
RAISE NOTICE 'remove_table_constraints run [%]', constraint_statment;
EXECUTE constraint_statment;
END LOOP;
RETURN TRUE;
END
$func$
LANGUAGE plpgsql;
Or maybe better, without loop. Here, I first aggregate into a single list of commands and execute that once:
CREATE OR REPLACE FUNCTION remove_tables_constraints()
RETURNS boolean AS
$func$
DECLARE
_sql text;
BEGIN
SELECT INTO _sql
string_agg(format('ALTER TABLE %s DROP CONSTRAINT %I'
, sub.tbl, sub.conname), E';\n')
FROM (
SELECT c.oid::regclass AS tbl, o.conname
FROM pg_constraint o
JOIN pg_class c ON c.oid = o.conrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname <> 'exclude_table' -- just one? then <>
AND o.contype = 'p' -- only pk constraints
AND n.nspname NOT LIKE 'pg%' -- exclude system schemas!
AND n.nspname <> 'information_schema' -- exclude information schema!
ORDER BY n.nspname, c.relname, o.conname -- commented irrelevant item
LIMIT 10
) sub;
RAISE NOTICE E'remove_table_constraints:\n%', _sql;
EXECUTE _sql;
RETURN TRUE;
END
$func$
LANGUAGE plpgsql;