Index not getting created using stored procedure - Oracle - oracle

I have this stored procedure to create index on table:
CREATE OR REPLACE PROCEDURE create_index (
in_tb VARCHAR2,
in_index VARCHAR2,
in_columns VARCHAR2,
lc_status OUT NUMBER
) AS
lc_affected NUMBER;
lc_stmt VARCHAR2(1500);
BEGIN
lc_stmt := 'BEGIN EXECUTE IMMEDIATE ''CREATE INDEX '
|| in_index
|| ' ON '
|| in_tb
|| ' ('
|| in_columns
|| ')''; END;';
dbms_output.put_line(lc_stmt);
dbms_utility.exec_ddl_statement(lc_stmt);
lc_affected := SQL%rowcount;
dbms_output.put_line('AFFECTED -->' || lc_affected);
IF ( lc_affected > 0 ) THEN
lc_status := 1;
ELSE
lc_status := 1;
END IF;
END create_index;
/
I execute the stored procedure using:
SET SERVEROUTPUT ON;
DECLARE
lc_status NUMBER;
BEGIN
create_index('TABLE_1_LOAD', 'ON_RUN_INDEX', 'MY_ID', lc_status);
END;
However, the index is not getting created in table TABLE_1_LOAD.
The output is:
BEGIN EXECUTE IMMEDIATE 'CREATE INDEX ON_RUN_INDEX ON TABLE_1_LOAD (MY_ID)'; END;
AFFECTED -->
PL/SQL procedure successfully completed.
I am not able to understand why the stored procedure is not creating indexes. Can you please help?

The dynamic statement you are trying to run via exec_ddl_statement is not DDL. It contains DDL, but embedded in an anonymous PL/SQL block, which is not the same thing. It looks like the dbms_utility procedure is just silently ignoring it for that reason.
If you simplify your statement to remove the unnecessary block then it will work:
...
BEGIN
lc_stmt := 'CREATE INDEX '
|| in_index
|| ' ON '
|| in_tb
|| ' ('
|| in_columns
|| ')';
...
Demo:
create table table_1_load (my_id number);
Table TABLE_1_LOAD created.
CREATE OR REPLACE PROCEDURE create_index (
in_tb VARCHAR2,
in_index VARCHAR2,
in_columns VARCHAR2,
lc_status OUT NUMBER
) AS
lc_affected NUMBER;
lc_stmt VARCHAR2(1500);
BEGIN
lc_stmt := 'CREATE INDEX '
|| in_index
|| ' ON '
|| in_tb
|| ' ('
|| in_columns
|| ')';
dbms_output.put_line(lc_stmt);
dbms_utility.exec_ddl_statement(lc_stmt);
lc_affected := SQL%rowcount;
dbms_output.put_line('AFFECTED -->' || lc_affected);
IF ( lc_affected > 0 ) THEN
lc_status := 1;
ELSE
lc_status := 1;
END IF;
END create_index;
/
Procedure CREATE_INDEX compiled
SET SERVEROUTPUT ON;
DECLARE
lc_status NUMBER;
BEGIN
create_index('TABLE_1_LOAD', 'ON_RUN_INDEX', 'MY_ID', lc_status);
END;
/
CREATE INDEX ON_RUN_INDEX ON TABLE_1_LOAD (MY_ID)
AFFECTED -->
PL/SQL procedure successfully completed.
The 'affected' number is still null, because execute_ddl_statement doesn't cause SQL%rowcount to be set, so you can't rely on that to tell you anything. But the index has been created:
select object_type, object_name from user_objects where created > trunc(sysdate);
OBJECT_TYPE OBJECT_NAME
------------------- ------------------------------
TABLE TABLE_1_LOAD
PROCEDURE CREATE_INDEX
INDEX ON_RUN_INDEX
You could run your original statement with execute immediate, and that would actually set SQL%rowcount, but as you still haven't run any DML it's meaningless really. To show that, with the (still unnecessary) anonymous block you get 1; without the block, using the same simplified statement as above, you get 0.

Related

Why do I get "ORA-00933: SQL command not properly ended" error ( execute immediate )?

I created a function and it uses a dynamic sql:
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := '
select sign(count(1))
into :l_res
from '|| table_name ||'
where '|| code_name ||' = :code_value
';
execute immediate l_query
using in code_value, out l_res;
return l_res;
end;
But when I try to use it I get an exception "ORA-00933: SQL command not properly ended"
What is wrong with this code?
You can use EXECUTE IMMEDIATE ... INTO ... USING ... to get the return value and DBMS_ASSERT to raise errors in the case of SQL injection attempts:
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := 'select sign(count(1))'
|| ' from ' || DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)
|| ' where ' || DBMS_ASSERT.SIMPLE_SQL_NAME(code_name)
|| ' = :code_value';
execute immediate l_query INTO l_res USING code_value;
return l_res;
end;
/
Which, for the sample data:
CREATE TABLE abc (a, b, c) AS
SELECT 1, 42, 3.14159 FROM DUAL;
Then:
SELECT CHECK_REF_VALUE('abc', 42, 'b') AS chk FROM DUAL;
Outputs:
CHK
1
And:
SELECT CHECK_REF_VALUE('abc', 42, '1 = 1 OR b') AS chk FROM DUAL;
Raises the exception:
ORA-44003: invalid SQL name
ORA-06512: at "SYS.DBMS_ASSERT", line 160
ORA-06512: at "FIDDLE_UVOFONEFDEHGDQJELQJL.CHECK_REF_VALUE", line 10
As for your question:
What is wrong with this code?
Using SELECT ... INTO is only valid in an SQL statement in a PL/SQL block and when you run the statement via EXECUTE IMMEDIATE it is executed in the SQL scope and not a PL/SQL scope.
You can fix it by wrapping your dynamic code in a BEGIN .. END PL/SQL anonymous block (and reversing the order of the bind parameters in the USING clause):
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := '
BEGIN
select sign(count(1))
into :l_res
from '|| DBMS_ASSERT.SIMPLE_SQL_NAME(table_name) ||'
where '|| DBMS_ASSERT.SIMPLE_SQL_NAME(code_name) ||' = :code_value;
END;
';
execute immediate l_query
using out l_res, in code_value;
return l_res;
end;
/
(However, that is a bit more of a complicated solution that just using EXECUTE IMMEDIATE ... INTO ... USING ....)
db<>fiddle here

Can we use collection variable as a table in execute immediate select statement? [duplicate]

I am building a function on PL/SQL using Oracle 11g.
I am trying to use a table variable within an EXECUTE IMMEDIATE statement, but it is not working, as you can see:
ERROR at line 1:
ORA-00904: "CENTER_OBJECTS": invalid identifier
ORA-06512: at "HIGIIA.KNN_JOIN", line 18
The code I am using is...
First, the type definitions
CREATE TYPE join_t IS OBJECT (
inn char(40),
out char(40)
);
/
CREATE TYPE join_jt IS TABLE OF join_t;
/
CREATE TYPE blob_t IS OBJECT (
id CHAR(40),
fv BLOB
);
/
CREATE TYPE blob_tt IS TABLE OF blob_t;
/
The function is:
create or replace FUNCTION knn_join (tab_inn IN varchar2, tab_out IN varchar2, blob_col1 IN varchar2, blob_col2 IN varchar2, dist_alg in VARCHAR2, kv in NUMBER ) RETURN join_jt
IS
var_fv BLOB;
var_id CHAR(40);
center_objects blob_tt := blob_tt();
retval join_jt := join_jt ();
join_table join_jt := join_jt();
sql_stmt1 varchar2(400);
sql_stmt2 varchar2(400);
BEGIN
sql_stmt1 := 'SELECT blob_t(ROWIDTOCHAR(rowid),' || blob_col1 || ') FROM ' || tab_out;
sql_stmt2 := 'SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) FROM ' || tab_inn || ' r WHERE ' || dist_alg || '_knn(r.' || blob_col2 || ', center_objects(idx).' || blob_col1 || ')<=' || kv;
dbms_output.put_line(sql_stmt2);
EXECUTE IMMEDIATE sql_stmt1 BULK COLLECT INTO center_objects;
for idx in center_objects.first()..center_objects.last()
loop
--SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) BULK COLLECT INTO join_table FROM londonfv r WHERE manhattan_knn(r.fv, center_objects(idx).fv) <=5;
EXECUTE IMMEDIATE sql_stmt2 BULK COLLECT INTO join_table;
for idx2 in join_table.first()..join_table.last()
loop
retval.extend();
retval(retval.count()) := join_table(idx2);
end loop;
end loop;
RETURN retval;
END;
/
To run the function:
select * from TABLE(knn_join('london','cophirfv','fv','fv','manhattan',5));
I am trying to use run the statement 'SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) BULK COLLECT INTO join_table FROM london r WHERE manhattan_knn(r.fv, center_objects(idx).fv) <=5' using the EXECUTE IMMEDIATE, but it does not work because I am using a variable in it.
Can someone give me a hand on it?
Thanks in advance!
You can't refer to a local PL/SQL variable inside a dynamic SQL statement, because it is out of scope within the SQL context used by the dynamic call. You could replace your first call:
SELECT join_t(ROWIDTOCHAR(r.rowid), center_objects(idx).id) FROM ' ...
with a bind variable:
SELECT join_t(ROWIDTOCHAR(r.rowid), :id FROM ' ...
EXECUTE IMMEDIATE ... USING center_objects(idx).id ...
but you can't do what when the object attribute is variable too:
... ', center_objects(idx).' || blob_col1 || ')<='...
although - at least in the example you've shown - the only object attribute name available is fv, regardless of the table column names passed in to the function - so that could be hard-coded; and thus a bind variable could be used:
... ', :fv)<='...
EXECUTE IMMEDIATE ... USING center_objects(idx).id, center_objects(idx).fv ...
and the kv value should also be a bind variable, so you'd end up with:
create or replace FUNCTION knn_join (tab_inn IN varchar2, tab_out IN varchar2,
blob_col1 IN varchar2, blob_col2 IN varchar2, dist_alg in VARCHAR2, kv in NUMBER )
RETURN join_jt
IS
center_objects blob_tt := blob_tt();
retval join_jt := join_jt ();
join_table join_jt := join_jt();
sql_stmt1 varchar2(400);
sql_stmt2 varchar2(400);
BEGIN
sql_stmt1 := 'SELECT blob_t(ROWIDTOCHAR(rowid),' || blob_col1 || ') FROM ' || tab_out;
sql_stmt2 := 'SELECT join_t(ROWIDTOCHAR(r.rowid), :id) FROM ' || tab_inn || ' r WHERE '
|| dist_alg || '_knn(r.' || blob_col2 || ', :fv)<= :kv';
dbms_output.put_line(sql_stmt1);
dbms_output.put_line(sql_stmt2);
EXECUTE IMMEDIATE sql_stmt1 BULK COLLECT INTO center_objects;
for idx in center_objects.first()..center_objects.last()
loop
EXECUTE IMMEDIATE sql_stmt2 BULK COLLECT INTO join_table
USING center_objects(idx).id, center_objects(idx).fv, kv;
for idx2 in join_table.first()..join_table.last()
loop
retval.extend();
retval(retval.count()) := join_table(idx2);
end loop;
end loop;
RETURN retval;
END;
/
As far as I can tell you could still do the join within the dynamic SQL statement, and eliminate the loops and the need for the intermediate center_objects and join_table collections:
create or replace FUNCTION knn_join (tab_inn IN varchar2, tab_out IN varchar2,
blob_col1 IN varchar2, blob_col2 IN varchar2, dist_alg in VARCHAR2, kv in NUMBER )
RETURN join_jt
IS
retval join_jt;
sql_stmt varchar2(400);
BEGIN
sql_stmt := 'SELECT join_t(ROWIDTOCHAR(tinn.rowid), ROWIDTOCHAR(tout.rowid))'
|| ' FROM ' || tab_inn || ' tinn JOIN ' || tab_out || ' tout'
|| ' ON ' || dist_alg || '_knn(tinn.fv, tout.fv) <= :kv';
dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt BULK COLLECT INTO retval USING kv;
RETURN retval;
END;
/
When you call it as you've shown:
select * from TABLE(knn_join('london','cophirfv','fv','fv','manhattan',5));
that's the equivalent of the hard-coded:
SELECT join_t(ROWIDTOCHAR(tinn.rowid), ROWIDTOCHAR(tout.rowid))
FROM london tinn
JOIN cophirfv tout
ON manhattan_knn(tinn.fv, tout.fv) <= 5
... so I guess you can verify whether that hard-coded version gives you the results you expect first. (Adding sample data and expected results to the question would have helped, of course).
That join condition may be expensive, depending on what the function is doing, how may rows are in each table (as every row in each table has to be compared with every row in the other), whether you actually have other filters, etc. The loop version would be even worse though. Without more information there isn't much to be done about that anyway.
As an aside, using varchar2 instead of char for the object attributes would be more normal; that's also the data type returned by the rowidtochar() function.

Execute immediate in stored procedure: command not properly ended

I got this procedure:
create or replace procedure TEST_PROCEDURE(
time_in in varchar2,
repository_in in varchar2,
iteration_in in varchar2,
tar_table_in in varchar2
)
is
delete_stmt varchar2(2000);
insert_stmt varchar2(2000);
begin
delete_stmt := 'delete from ' || tar_table_in || ' where time=' || time_in ||' and repository=' || repository_in || '
and iteration=' || iteration_in || '; commit;';
execute immediate delete_stmt;
insert_stmt := 'insert into ' || tar_table_in || ' (some columns)
SELECT ' || repository_in || ' as repository, ' || iteration_in || ' as iteration, t1.* FROM dual
left join
json_table((select json_response from TEST_TABLE where repository=repository_in), ''$[*]''
COLUMNS
time varchar2(64) PATH ''$.time'',
session_id varchar2(256) PATH ''$.session_id''
) t1
on 1=1';
execute immediate insert_stmt;
end;
It currently throws an ORA-00933 error at the "execute immediate delete_stmt;" line, which hints to something foul in the query string.
I can't seem to find the location of either a missing quote, or semi-colon that would end the command. Anyone able to spot what I'm missing?
If you are using SQL Dynamic, then your values in your dynamic query are missing quotes.
Don't use commit inside execute immediate.
You do the delete, then the insert, then you commit the transaction.
It should be
create or replace procedure TEST_PROCEDURE(
time_in in varchar2,
repository_in in varchar2,
iteration_in in varchar2,
tar_table_in in varchar2
)
is
delete_stmt varchar2(2000);
insert_stmt varchar2(2000);
begin
delete_stmt := 'delete from ' || tar_table_in || ' where time= ''' || time_in || ''' and repository= ''' || repository_in || '''
and iteration = ''' || iteration_in || ''' ';
execute immediate delete_stmt;
insert_stmt := 'insert into ' || tar_table_in || ' (some columns)
SELECT ''' || repository_in || ''' as repository, ''' || iteration_in || ''' as iteration, t1.* FROM dual
left join
json_table((select json_response from TEST_TABLE where repository=repository_in), ''$[*]''
COLUMNS
time varchar2(64) PATH ''$.time'',
session_id varchar2(256) PATH ''$.session_id''
) t1
on 1=1';
execute immediate insert_stmt;
commit;
end;
Whenever trying to debug dynamic sql, assign the statement to a variable (which you do already) then use dbms_output to show the exact statement that will be executed:
SQL> create or replace procedure my_proc (time_in in varchar2,
2 repository_in in varchar2,
3 iteration_in in varchar2,
4 tar_table_in in varchar2)
5 as
6 v_sql varchar2(1000);
7 begin
8 v_sql:='delete from ' ||
9 tar_table_in ||
10 ' where time=' ||
11 time_in ||
12 ' and repository=' ||
13 repository_in || '
14 and iteration=' ||
15 iteration_in ||
16 '; commit;';
17
18 dbms_output.put_line('====== begin debug line ======');
19 dbms_output.put_line(v_sql);
20 dbms_output.put_line('====== end debug line ======');
21 end;
22 /
Procedure created.
SQL> show errors
No errors.
SQL> set serverout on
SQL> exec my_proc('aaaa','bbbb','cccc','dddd');
====== begin debug line ======
delete from dddd where time=aaaa and repository=bbbb
and iteration=cccc;
commit;
====== end debug line ======
PL/SQL procedure successfully completed.
SQL> --
SQL> drop procedure my_proc;
Procedure dropped.

Retrieving password from an oracle package

I have the following code, my question is how can I be sure the encryption key in l_key has not been changed?
create or replace PACKAGE BODY "ENCRYPT_DECRYPT_PASSWORD"
AS
l_key RAW(128) := utl_raw.cast_to_raw('secret');
------------------------------------------------------------------------
FUNCTION encrypt_val( p_val IN VARCHAR2 ) RETURN VARCHAR2
IS
l_encrypted RAW(2048);
l_val RAW(2048) := utl_raw.cast_to_raw(p_val);
BEGIN
l_encrypted := dbms_crypto.encrypt
( src => l_val,
typ => dbms_crypto.des_cbc_pkcs5,
key => l_key );
return utl_raw.cast_to_varchar2(l_encrypted);
END encrypt_val;
-----------------
-----------------
-----------------
FUNCTION decrypt_val( p_val IN varchar2 ) RETURN VARCHAR2
IS
l_decrypted RAW(2048);
l_val RAW(2048) := utl_raw.cast_to_raw(p_val);
BEGIN
l_decrypted := dbms_crypto.decrypt
( src => l_val,
typ => dbms_crypto.des_cbc_pkcs5,
key => l_key );
return utl_raw.cast_to_varchar2(l_decrypted);
END decrypt_val;
-----------------
-----------------
-----------------
PROCEDURE encrypt_table_passwords(table_name IN varchar2,
column_name IN varchar2,
table_id IN varchar2) IS
BEGIN
EXECUTE IMMEDIATE
'begin
for c1 in (select * from ' || table_name ||') loop
update ' || table_name || ' set ' || column_name || ' = ENCRYPT_DECRYPT_PASSWORD.encrypt_val(c1.' || column_name || ') where ' || table_id || ' = c1.'||table_id||' and ' || column_name ||
' is not null; end loop; end;';
END encrypt_table_passwords;
-----------------
-----------------
-----------------
FUNCTION get_decrypted_password( table_name IN varchar2,column_name IN varchar2,table_id IN varchar2,table_id_val IN varchar2 ) RETURN VARCHAR2
IS
encrypted_pas varchar2(100);
decrypted_pas varchar2(100);
BEGIN
EXECUTE IMMEDIATE 'select ' || column_name || ' from ' || table_name || ' where ' || table_id || ' = ' || table_id_val
INTO encrypted_pas;
Select decrypt_val(encrypted_pas) into decrypted_pas from dual;
--return decrypt_val(encrypted_pas);
return decrypted_pas;
END get_decrypted_password;
END encrypt_decrypt_password;
I tried the following which I found on the web, but it appears it doesn't work for my oracle version:
DECLARE
v_x RAW(128);
BEGIN
SELECT ENCRYPT_DECRYPT_PASSWORD.l_key x
INTO v_x
FROM DUAL;
DBMS_OUTPUT.put_line (v_x);
END;
/
I get "component 'L_KEY' must be declared" and "invalid identifier".
The reason I want to investigate is that I had decryption errors which went away after I changed the password column from varchar(99) to nvarchar2(100) and after I regenerated the encrypted password.
I get "component 'L_KEY' must be declared" and "invalid identifier".
L_KEY is declared in your package BODY. That means its scope is private, restricted to the code of the body. Only things declared in the package SPEC are public and can be accessed outside the package scope.
If you need to check its actual value you need to extend your package with a function which returns L_KEY. You probably don't want to expose it in Production so be careful. You may wish to consider using conditional compilation just to be sure nothing accidentally leaks.

DBMS_SQL.TO_REFCURSOR equivalent in Oracle 10g

I have the following code :
procedure Replace(sUser in Varchar2,sNomTable in varchar2,sColonne in varchar2,sID in Varchar2,nbCharAlterer IN NUMBER) is
l_cursor NUMBER;
l_return NUMBER;
l_ref_cursor SYS_REFCURSOR;
TYPE t_tab IS TABLE OF VARCHAR2(4000);
l_tab t_tab;
l_tab_Id t_tab;
sChaine VARCHAR2(4000 CHAR);
sqlReq CONSTANT VARCHAR2(1000):= 'select ' || sId || ',' || sColonne || ' from ' || sUser || '.' || sNomTable ;
begin
--
l_cursor := DBMS_SQL.open_cursor;
DBMS_SQL.parse(l_cursor, sqlReq, DBMS_SQL.NATIVE);
l_return := DBMS_SQL.EXECUTE(l_cursor);
-- Connvert from DBMS_SQL to a REF CURSOR.
l_ref_cursor := DBMS_SQL.to_refcursor(l_cursor);
Here I am getting the following error :
pls 00302 component 'TO_REFCURSOR' must be declared
since my oracle version is 10g.
Any idea of how to do the equivalent in Oracle 10g?
Here's how you could use native dynamic sql:
PROCEDURE p_replace(suser IN VARCHAR2,
snomtable IN VARCHAR2,
scolonne IN VARCHAR2,
sid IN VARCHAR2,
nbcharalterer IN NUMBER) IS
v_validate_sid_col_name VARCHAR2(32);
v_validate_scolonne_col_name VARCHAR2(32);
v_validate_suser VARCHAR2(32);
v_validate_snomtable VARCHAR2(32);
sqlreq VARCHAR2(2000);
refcur sys_refcur;
BEGIN
-- Check the input values are valid identifiers (to avoid sql injection)
-- N.B. this does not check they are valid object names!
v_validate_sid_col_name := dbms_assert.qualified_sql_name(sid);
v_validate_scolonne_col_name := dbms_assert.qualified_sql_name(scolonne);
v_validate_suser := dbms_assert.qualified_sql_name(suser);
v_validate_snomtable := dbms_assert.qualified_sql_name(scolonne);
sqlReq := 'select ' || v_validate_sid_col_name || ',' ||
v_validate_scolonne_col_name ||
' from ' || v_validate_suser || '.' || v_validate_snomtable;
-- or maybe you want to use execute immediate to bulk collect into arrays?
OPEN refcur FOR sqlreq;
...
END p_replace;
Note that I've changed the name of the procedure since "replace" is the name of a pre-existing built-in function, and therefore not a very good name to use.
You don't mention what it is you're going to do with the results of your query, so I wasn't sure if opening a ref cursor is what you actually need, or whether bulk collecting via execute immediate would work better for you.

Resources