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.
Related
I have error master table which contain description like 'Error in table abc in xyz column.' I need to format string for column name which is xyz here. Where ever I need to call this table I will pass column name and then I will get expected description.
Ex - Insert into errorTabl values(01,There is error in {0})
Whenever inside package I need to retrieve value of 01 then I will pass column name col1 so then expected value will be as below :
01 There is error in col1
Request you to please help me for insert and select both statements.
Though this doesn't make sense, maybe the code below could help you to start with something or to clarify your problem.
NOTE: the code below is here just to show you the basics - there is no sense in it for any kind of production. You are the one to adjust it to your context.
So, the package to put and get things into or from errors table:
CREATE OR REPLACE PACKAGE ERRS AS
Procedure putError(p_table IN VarChar2, p_column IN VarChar2);
Function getError(p_table VarChar2, p_column VarChar2) RETURN VarChar2;
END ERRS;
-- ---------------------------------------------------------------------------------
CREATE OR REPLACE PACKAGE BODY ERRS AS
Procedure putError(p_table IN VarChar2, p_column IN VarChar2) AS
BEGIN
Declare
mSql VarChar2(512) := '';
sq VarChar2(1) := '''';
Begin
mSql := 'Insert Into ERRORTABLE values( ' || sq || '01' || sq || ', ' || sq ||
'There is error in table ' || p_table || ' in ' || p_column || ' column' || sq || ')';
Execute Immediate mSql;
Commit;
End;
END putError;
-- -------------------------------------------------------------------------------
Function getError(p_table VarChar2, p_column VarChar2) RETURN VarChar2 IS
BEGIN
Declare
Cursor c IS
Select ERR_DESC From ERRORTABLE Where ERR_DESC Like('%table ' || p_table || ' in ' || p_column || '%');
mRet VarChar2(512) := '';
mDesc VarChar2(512) := '';
Begin
Open c;
LOOP
FETCH c into mDesc;
EXIT WHEN c%NOTFOUND;
mRet := '01 ' || mDesc || Chr(10);
END LOOP;
Close c;
RETURN RTRIM(mRet, Chr(10));
End;
END getError;
END ERRS;
Now the calling code to insert 5 records (once more - this is senseless) and to get you one of them...
set serveroutput on
DECLARE
errMsg VarChar2(512);
BEGIN
ERRS.putError('T_ABC', 'C_XYZ');
ERRS.putError('T_ABC', 'C_MNO');
ERRS.putError('T_ABC', 'C_PQR');
ERRS.putError('T_DEF', 'C_MNO');
ERRS.putError('T_DEF', 'C_XYZ');
--
errMsg := ERRS.getError('T_ABC', 'C_XYZ');
dbms_output.put_line(errMsg);
END;
/* R e s u l t :
anonymous block completed
01There is error in table T_ABC in C_XYZ column
*/
Just needed to pass double colon in insert query so then it will take single colon in table.
Ex - Insert into errorTabl values(01,There is error in ''{0}'')
In table it will be look like
**Id** **Description**
01 There is error in'{0}'.
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.
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.
I have to following oracle function, build_select, which create a select request. The return value is in the following format:
select col1 ||'|'||col2 ||'|'||col3 from table;
Below is the build_select function:
create or replace FUNCTION build_select (
p_table_name IN VARCHAR2
)
RETURN VARCHAR2
AS
l_ret VARCHAR2 (32767);
BEGIN
FOR eachcol IN ( SELECT column_name, data_type
, LEAD (column_name), LEAD (data_type)
OVER (
PARTITION BY table_name ORDER BY column_id
)
next_column
FROM all_tab_cols
WHERE table_name = p_table_name
ORDER BY column_id)
LOOP
IF eachcol.data_type = 'CLOB' THEN
l_ret := l_ret || dbms_lob.substr( eachcol.column_name, 3000, 1 ) || CASE WHEN eachcol.next_column IS NULL THEN NULL ELSE ' ||''|''||' END;
ELSE
l_ret := l_ret || eachcol.column_name || CASE WHEN eachcol.next_column IS NULL THEN NULL ELSE ' ||''|''||' END;
END IF;
END LOOP;
IF l_ret IS NULL
THEN
raise_application_error (-20001, 'table ' || p_table_name || ' not found');
END IF;
l_ret := 'select ' || l_ret || ' from ' || p_table_name || ';';
RETURN l_ret;
END build_select;
What I want to do is to test if the data type of the column is CLOB and if so then return it as
dbms_lob.substr( eachcol.column_name, 3000, 1 )
I have added the if else condition in the loop part. But I am getting the error :
PLS 00302 : component DATA_TYPE must de declared.
Any help pls?
I need to do so cause when I am doing a spool of the returned select, it is not returning all the columns cause of the CLOB data type.
I think your function should be this:
create or replace FUNCTION build_select (
p_table_name IN VARCHAR2
)
RETURN VARCHAR2
AS
l_ret VARCHAR2 (32767);
BEGIN
FOR eachcol IN ( SELECT column_name, data_type
FROM all_tab_cols
WHERE table_name = p_table_name
ORDER BY column_id)
LOOP
IF eachcol.data_type = 'CLOB' THEN
l_ret := l_ret || 'dbms_lob.substr( '||eachcol.column_name||', 3000, 1 ),';
ELSE
l_ret := l_ret || eachcol.column_name||',';
END IF;
END LOOP;
IF l_ret IS NULL
THEN
raise_application_error (-20001, 'table ' || p_table_name || ' not found');
END IF;
l_ret := 'select ' || regexp_replace(l_ret, ',$', NULL) || ' from ' || p_table_name || ';';
RETURN l_ret;
END build_select;
Note, ALL_TAB_COLS selects also system-generated hidden columns and invisible columns which could be a problem. Query ALL_TAB_COLUMNS if you like to filter them.
I have declared the following procedure:
CREATE OR REPLACE PROCEDURE MODIFY_NOT_NULL(
v_tbName IN VARCHAR2,
v_cName IN VARCHAR2,
v_defaultValue IN VARCHAR2 )
IS
v_is_null VARCHAR2(1);
BEGIN
SELECT nullable INTO v_is_null
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = v_tbName
AND COLUMN_NAME = v_cName;
IF v_is_null = 'Y' THEN
EXECUTE IMMEDIATE ('ALTER TABLE ' || v_tbName
|| ' MODIFY (' || v_cName
|| ' DEFAULT ' || v_defaultValue
|| ' NOT NULL )');
END IF;
END;
However when I execute my code:
BEGIN
modify_not_null('TABLE_NAME', 'COLUMN_NAME ' ,'0');
END;
/
I am getting a
"ORA-01403: No Data Found"
This exception will be usually thrown if the "SELECT INTO" statement does not return any value, however I will always get a value when I execute this:
Select nullable
from USER_TAB_COLUMNS
WHERE table_name = 'TABLE_NAME'
AND column_name = 'COLUMN_NAME';
When I execute the code above, I get "N" or "Y" as a result. So I always get a result. I don't know why this exception is thrown
Your call contains a trailing space:
modify_not_null('TABLE_NAME', 'COLUMN_NAME ' ,'0');
^
So proc throws no data found because 'COLUMN_NAME ' != 'COLUMN_NAME'
Use upper(trim(v_cName)) to prevent typos causing errors. Apply on all parameters.
You are passing v_defaultValue param to column name.
Change procedure to
SELECT nullable INTO v_is_null
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = v_tbName AND COLUMN_NAME = v_cName ;
Before your SELECT .... INTO, you have to make sure that there is something to select. Because depending on what user you are, and what parameters you give, there may be no data in your table.
A simple way would be to have a COUNT at the beginning before the SELECT:
CREATE OR REPLACE PROCEDURE MODIFY_NOT_NULL(
v_tbName IN VARCHAR2,
v_cName IN VARCHAR2,
v_defaultValue IN VARCHAR2 )
IS
v_is_null VARCHAR2(1);
v_count number;
BEGIN
-- added select count
SELECT count(1) INTO v_count FROM USER_TAB_COLUMNS WHERE TABLE_NAME = trim(v_tbName) AND COLUMN_NAME = trim(v_cName);
-- added if v_count=1
if v_count = 1 then
SELECT nullable INTO v_is_null FROM USER_TAB_COLUMNS WHERE TABLE_NAME = trim(v_tbName) AND COLUMN_NAME = trim(v_cName);
IF v_is_null = 'Y' THEN
EXECUTE IMMEDIATE ('ALTER TABLE ' || v_tbName || ' MODIFY (' || v_cName || ' DEFAULT ' || v_defaultValue || ' NOT NULL )');
END IF;
-- added
end if;
END;
/
Share and enjoy
stay classy :)
create or replace procedure modify_not_null(v_tbName in varchar2,
v_cName in varchar2,
v_defaultValue in varchar2) is
cursor c_tbl(cp_tbname in varchar2,
cp_cname in varchar2) is
select nullable
from user_tab_columns
where table_name = upper(cp_tbname)
and column_name = upper(cp_cname);
l_tbl c_tbl%rowtype;
begin
open c_tbl(cp_tbname => v_tbName,
cp_cname => v_cName);
fetch c_tbl into l_tbl;
close c_tbl;
if l_tbl.nullable = 'Y' then
execute immediate 'alter table ' || v_tbName || ' modify (' || v_cName ||
' default ' || v_defaultValue || ' not null )';
end if;
exception
when others then
raise_application_error(-20000, dbms_utility.format_error_stack);
end modify_not_null;