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
Related
I am retrieving data from database using jdbc. In my code I am using 3-4 tables to get data. But sometimes if table is not present in database my code gives exception. How to handle this situation. I want my code to continue working for other tables even if one table is not present. Please help.
I have wrote a code like this
sql="select * from table"
now Result set and all.
If table is not present in database it give exception that no such table. I want to handle it. In this code I cannot take tables which are already present in advance . I want to check here itself if table is there or not.
Please do not mark it as a duplicate question. The link you shared doesnot give me required answer as in that question they are executing queries in database not through JDBC code
For Sybase ASE the easiest/quickest method would consist of querying the sysobjects table in the database where you expect the (user-defined) table to reside:
select 1 from sysobjects where name = 'table-name' and type = 'U'
if a record is returned => table exists
if no record is returned => table does not exist
How you use the (above) query is up to you ...
return a 0/1-row result set to your client
assign a value to a #variable
place in a if [not] exists(...) construct
use in a case statement
If you know for a fact that there won't be any other object types (eg, proc, trigger, view, UDF) in the database with the name in question then you could also use the object_id() function, eg:
select object_id('table-name')
if you receive a number => the object exists
if you receive a NULL => the object does not exist
While object_id() will obtain an object's id from the sysobjects table, it does not check for the object type, eg, the (above) query will return a number if there's a stored proc named 'table-name'.
As with the select/sysobjects query, how you use the function call in your code is up to you (eg, result set, populate #variable, if [not] exists() construct, case statement).
So, addressing the additional details provided in the comments ...
Assuming you're submitting a single batch that needs to determine table existence prior to running the desired query(s):
-- if table exists, run query(s); obviously if table does not exist then query(s) is not run
if exists(select 1 from sysobjects where name = 'table-name' and type = 'U')
begin
execute("select * from table-name")
end
execute() is required to keep the optimizer from generating an error that the table does not exist, ie, the query is not parsed/compiled unless the execute() is actually invoked
If your application can be written to use multiple batches, something like the following should also work:
# application specific code; I don't work with java but the gist of the operation would be ...
run-query-in-db("select 1 from sysobjects where name = 'table-name' and type = 'U'")
if-query-returns-a-row
then
run-query-in-db("select * from table-name")
fi
This is the way of checking if the table exists and drop it:
IF EXISTS (
SELECT 1
FROM sysobjects
WHERE name = 'a_table'
AND type = 'U'
)
DROP TABLE a_table
GO
And this is how to check if a table exists and create it.
IF NOT EXISTS (
SELECT 1
FROM sysobjects
WHERE name = 'a_table'
AND type = 'U'
)
EXECUTE("CREATE TABLE a_table (
col1 int not null,
col2 int null
)")
GO
(They are different because in table-drop a temporary table gets created, so if you try to create a new one you will get an exception that it already exists)
Before running the query which has some risk in table not existing, run the following sql query and check if the number of results is >= 1. if it is >= 1 then you are safe to execute the normal query. otherwise, do something to handle this situation.
SELECT count(*)
FROM information_schema.TABLES
WHERE (TABLE_SCHEMA = 'your_db_name') AND (TABLE_NAME = 'name_of_table')
I am no expert in Sybase but take a look at this,
exec sp_tables '%', '%', 'master', "'TABLE'"
Sybase Admin
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.
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;
I need to pass a schema name as a parameter to a stored procedure. But I end up with error ORA00942: table or view does not exist. I googled a lot but didn't find any solution.
Actually in our application we are writing a Stored procedure (SP) in One schema and referring the same SP for all other schemas.
Consider I have to find the stock of an item in a different schema (1 schema for 1 client). Then
select * from abc.stock_table where itemid=xxx;
In this query I want to replace abc with different schema names.
It's imposible to change scheme in compiled PLSQL code on the fly.
You should use dynamic SQL instead.
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/dynamic.htm
But you must understand that this option has side effect - each time when you call dynamic sql it compiles again. So it's slower and more risky.
Create public synonym for your object from different schema. Grant privilege on it.
You need use Dynamic SQL, there are tons of material on Internet.
e.g. on oracle website
CREATE OR REPLACE PROCEDURE query_invoice(
month VARCHAR2,
year VARCHAR2) IS
TYPE cur_typ IS REF CURSOR;
c cur_typ;
query_str VARCHAR2(200);
inv_num NUMBER;
inv_cust VARCHAR2(20);
inv_amt NUMBER;
BEGIN
query_str := 'SELECT num, cust, amt FROM inv_' || month ||'_'|| year
|| ' WHERE invnum = :id';
OPEN c FOR query_str USING inv_num;
LOOP
FETCH c INTO inv_num, inv_cust, inv_amt;
EXIT WHEN c%NOTFOUND;
-- process row here
END LOOP;
CLOSE c;
END;
/