How to call Oracle MD5 hash function? - sql

I have below code. I am using Oracle 11g.
SELECT DBMS_OBFUSCATION_TOOLKIT.md5 (input => UTL_RAW.cast_to_raw(
FIRST_NAME
||LAST_NAME
)) md5_key ,
FIRST_NAME ,
LAST_NAME
FROM C_NAME_TAB
WHERE PKEY='1234'
How can i call this code? Can i directly execute this code in sqldeveloper?

In Oracle 12c you can use the function STANDARD_HASH. It does not require any additional privileges.
select standard_hash('foo', 'MD5') from dual;
The dbms_obfuscation_toolkit is deprecated (see Note here). You can use DBMS_CRYPTO directly:
select rawtohex(
DBMS_CRYPTO.Hash (
UTL_I18N.STRING_TO_RAW ('foo', 'AL32UTF8'),
2)
) from dual;
Output:
ACBD18DB4CC2F85CEDEF654FCCC4A4D8
Add a lower function call if needed. More on DBMS_CRYPTO.

I would do:
select DBMS_CRYPTO.HASH(rawtohex('foo') ,2) from dual;
output:
DBMS_CRYPTO.HASH(RAWTOHEX('FOO'),2)
--------------------------------------------------------------------------------
ACBD18DB4CC2F85CEDEF654FCCC4A4D8

#user755806 I do not believe that your question was answered. I took your code but used the 'foo' example string, added a lower function and also found the length of the hash returned. In sqlplus or Oracle's sql developer Java database client you can use this to call the md5sum of a value. The column formats clean up the presentation.
column hash_key format a34;
column hash_key_len format 999999;
select dbms_obfuscation_toolkit.md5(
input => UTL_RAW.cast_to_raw('foo')) as hash_key,
length(dbms_obfuscation_toolkit.md5(
input => UTL_RAW.cast_to_raw('foo'))) as hash_key_len
from dual;
The result set
HASH_KEY HASH_KEY_LEN
---------------------------------- ------------
acbd18db4cc2f85cedef654fccc4a4d8 32
is the same value that is returned from a Linux md5sum command.
echo -n foo | md5sum
acbd18db4cc2f85cedef654fccc4a4d8 -
Yes you can call or execute the sql statement directly in sqlplus or sql developer. I tested the sql statement in both clients against 11g.
You can use any C, C#, Java or other programming language that can send a statement to the database. It is the database on the other end of the call that needs to be able to understand the sql statement. In the case of 11 g, the code will work.
#tbone provides an excellent warning about the deprecation of the dbms_obfuscation_toolkit. However, that does not mean your code is unusable in 12c. It will work but you will want to eventually switch to dbms_crypto package. dbms_crypto is not available in my version of 11g.

To calculate MD5 hash of CLOB content field with my desired encoding without implicitly recoding content to AL32UTF8, I've used this code:
create or replace function clob2blob(AClob CLOB) return BLOB is
Result BLOB;
o1 integer;
o2 integer;
c integer;
w integer;
begin
o1 := 1;
o2 := 1;
c := 0;
w := 0;
DBMS_LOB.CreateTemporary(Result, true);
DBMS_LOB.ConvertToBlob(Result, AClob, length(AClob), o1, o2, 0, c, w);
return(Result);
end clob2blob;
/
update my_table t set t.hash = (rawtohex(DBMS_CRYPTO.Hash(clob2blob(t.content),2)));

Related

Look up SDO_GEOMETRY validation error code using SQL

I have a query that validates an SDO_GEOMETRY in Oracle 18c:
select
sdo_geom.validate_geometry_with_context(
sdo_geometry ('polygon ((676832.320 4857578.086, 665287.423 4857578.086, 665277.423 4878109.585,
676832.320 4878119.585, 676842.320 4857588.086))', 26917)
, 0.005) as validation
from
dual
VALIDATION
-----------------------------
13348 [Element <1>] [Ring <1>]
(1 row selected.)
db<>fiddle
The query produces an error code in a text column, but it doesn't describe what the code means.
I am able look up the error manually in the docs: 82 ORA-12700 to ORA-19400
ORA-13348: polygon boundary is not closed
Cause: The boundary of a
polygon does not close.
Action: Alter the coordinate values or the
definition of the SDO_GTYPE or SDO_ETYPE attribute of the geometry.
But manually looking up those error codes is inconvenient.
Is there a way to enhance the query so that it returns the full error description? (get the description from the database)
Assuming you can parse the string to pull out the error message, you can pass it to sqlerrm to get the text of the error (note that you're apparently getting a positive value, you'd need to negate that value to pass it to sqlerrm). I would assume that you could just look for everything before the first space to get the error number but I don't have a huge sample set to work with.
declare
l_message varchar2(1000);
begin
l_message := sqlerrm( -13348 );
dbms_output.put_line( l_message );
end;
/
will print
ORA-13348: polygon boundary is not closed
Building on #JustinCave's answer, here's a custom function that gets the error description from the validation text:
with function error_description(validation in varchar2) return varchar2 is
begin
return sqlerrm(substr(validation, 1, instr(validation,' ') - 1) * -1); --Multiply by -1. Oracle error codes seem to be "negative".
end;
select
error_description(validation) as error_description
from
(select
sdo_geom.validate_geometry_with_context(
sdo_geometry ('polygon ((676832.320 4857578.086, 665287.423 4857578.086, 665277.423 4878109.585, 676832.320 4878119.585, 676842.320 4857588.086))', 26917), 0.005) as validation
from dual)
ERROR_DESCRIPTION
-------------------
ORA-13348: polygon boundary is not closed
Edit:
As pointed out by #SolomonYakobson in a related post, the SQLERRM() function can also be used in a SELECT query (without the need for a custom function).
Are certain kinds of Oracle functions only available in PL/SQL, not
SQL?
Many of the functions are defined in Oracle supplied package
SYS.STANDARD.
Example: SELECT SYS.STANDARD.SQLERRM(-1422) FROM DUAL;
So we just need to fully qualify the function: SYS.STANDARD.SQLERRM()
select
sys.standard.sqlerrm(substr(validation, 1, instr(validation,' ') - 1) * -1) error_description
from
(select
sdo_geom.validate_geometry_with_context(
sdo_geometry ('polygon ((676832.320 4857578.086, 665287.423 4857578.086, 665277.423 4878109.585, 676832.320 4878119.585, 676842.320 4857588.086))', 26917), 0.005) as validation
from dual)
ERROR_DESCRIPTION
---------------------
ORA-13348: polygon boundary is not closed

My secret password in oracle won't output

I'm trying to output my procedure, with a secret password. When i try to run my code it doesn't work.
CREATE OR replace Procedure hiddenPasswords(
p_MA_ID IN MitarbeiterAccounts.MitarbetierAccountID%TYPE,
p_M_Login IN MitarbeiterAccounts.Mitarbeiter_Login%TYPE,
p_M_Password IN MitarbeiterAccounts.Mitarbeiter_Password%TYPE)
IS
BEGIN
INSERT INTO MitarbeiterAccounts(MitarbeiterAccountsID,
MitarbeiterAccounts_Login, Mitarbeiter_Password)
VALUES(p_MA_ID, p_M_Login, HASHBYTES('SHA2_512', p_M_Password));
END;
/
EXEC hiddenPasswords p_MA_ID = 4, p_M_Login = 'Admin' p_M_Password = N'123';
I'm getting that HASHBYTES is invalid identifier
Well, Oracle doesn't have any built-in function called HASHBYTES. It is there in SQL SERVER but not in Oracle
instead you can use DBMS_CRYPTO.HASH if you have that privilege for the same.
DBMS_CRYPTO provides an interface to encrypt and decrypt stored data, and can be used in conjunction with PL/SQL programs running network communications
DBMS_CRYPTO
Update
For eg., I have used RAW here. You can check other Overloaded functions in the above link where you can use BLOB, CLOB as well.
DECLARE
l_pwd VARCHAR2(19) := 'mysecretpassword';
l_ccn_raw RAW(128) := utl_raw.cast_to_raw(l_pwd);
l_encrypted_raw RAW(2048);
BEGIN
dbms_output.put_line('CC: ' || l_ccn_raw);
l_encrypted_raw := dbms_crypto.hash(l_ccn_raw, 1);
dbms_output.put_line('MD4: ' || l_encrypted_raw);
l_encrypted_raw := dbms_crypto.hash(l_ccn_raw, 2);
dbms_output.put_line('MD5: ' || l_encrypted_raw);
l_encrypted_raw := dbms_crypto.hash(l_ccn_raw, 3);
dbms_output.put_line('SH1: ' || l_encrypted_raw);
END;
/
OUTPUT
CC: 6D7973656372657470617373776F7264
MD4: BBBA2CBC2F6E0F158D06B34F819DB5F6
MD5: 4CAB2A2DB6A3C31B01D804DEF28276E6
SH1: 08CD923367890009657EAB812753379BDB321EEB

How to Insert data using cursor into new table having single column containing XML type data in Oracle?

I'm able to insert values into table 2 from table 1 and execute the PL/SQL procedure successfully but somehow the output is clunky. I don't know why?
Below is the code :
create table airports_2_xml
(
airport xmltype
);
declare
cursor insert_xml_cr is select * from airports_1_orcl;
begin
for i in insert_xml_cr
loop
insert into airports_2_xml values
(
xmlelement("OneAirport",
xmlelement("Rank", i.Rank) ||
xmlelement("airport",i.airport) ||
xmlelement("Location",i.Location) ||
xmlelement("Country", i.Country) ||
xmlelement("Code_iata",i.code_iata) ||
xmlelement("Code_icao", i.code_icao) ||
xmlelement("Total_Passenger",i.Total_Passenger) ||
xmlelement("Rank_change", i.Rank_change) ||
xmlelement("Percent_Change", i.Percent_change)
));
end loop;
end;
/
select * from airports_2_xml;
Output:
Why it is showing &lt ,&gt in the output ? And why am I unable to see the output fully?
Expected output:
<OneAirport>
<Rank>3</Rank>
<Airport>Dubai International</Airport>
<Location>Garhoud</Location>
<Country>United Arab Emirates</Country>
<Code_IATA>DXB</Code_IATA>
<Code_ICAO>OMDB</Code_ICAO>
<Total_passenger>88242099</Total_passenger>
<Rank_change>0</Rank_change>
<Percent_Change>5.5</Percent_Change>
</OneAirport>
The main issue is how you are constructnig the XML. You have an outer XMLElement for OneAirport, and the content of that element is a single string.
You are generating individual XMLElements from the cursor fields, but then you are concenating those together, which gives you a single string which still has the angle brackets you're expecting. So you're trying to do something like, simplified a bit:
select
xmlelement("OneAirport", '<Rank>1</Rank><airport>Hartsfield-Jackson</airport>')
from dual;
XMLELEMENT("ONEAIRPORT",'<RANK>1</RANK><AIRPORT>HARTSFIELD-JACKSON</AIRPORT>')
--------------------------------------------------------------------------------
<OneAirport><Rank>1</Rank><airport>Hartsfield-Jackson</airp
and by default XMLElement() escapes entities in the passed-in values, so the angle-brackets are being converted to 'safe' equivalents like <. If it didn't do that, or you told it not to with noentityescaping:
select xmlelement(noentityescaping "OneAirport", '<Rank>1</Rank><airport>Hartsfield-Jackson</airport>')
from dual;
XMLELEMENT(NOENTITYESCAPING"ONEAIRPORT",'<RANK>1</RANK><AIRPORT>HARTSFIELD-JACKS
--------------------------------------------------------------------------------
<OneAirport><Rank>1</Rank><airport>Hartsfield-Jackson</airport></OneAirport>
then that would appear to be better, but you still actually have a single element with a single string (with characters that are likely to cause problems down the line), rather than the XML structure you almost certainly intended.
A simple way to get an zctual structure is with XMLForest():
xmlelement("OneAirport",
xmlforest(i.Rank, i.airport, i.Location, i.Country, i.code_iata,
i.code_icao, i.Total_Passenger, i.Rank_change, i.Percent_change)
)
You don't need the cursor loop, or any PL/SQL; you can just do:
insert into airports_2_xml (airport)
select xmlelement("OneAirport",
xmlforest(i.Rank, i.airport, i.Location, i.Country, i.code_iata,
i.code_icao, i.Total_Passenger, i.Rank_change, i.Percent_change)
)
from airports_1_orcl i;
The secondary issue is the display. You'll see more data if you issue some formatting commands, such as:
set lines 120
set long 32767
set longchunk 32767
Those will tell your client to retrieve and show more of the long (XMLType here) data, rather the default 80 characters it's giving you now.
Once you are generating a nested XML structure you can use XMLSerialize() to display that more readable when you query your second table.
Try this below block :
declare
cursor insert_xml_cr is select * from airports_1_orcl;
v_airport_xml SYS.XMLTYPE;
begin
for i in insert_xml_cr
loop
SELECT XMLELEMENT ( "OneAirport",
XMLFOREST(i.Rank as "Rank"
,i.airport as "Airport"
,i.Location as "Location"
,i.Country as "Country"
,i.code_iata as "Code_iata"
,i.code_icao as "code_icao"
,i.Total_Passenger as "Total_Passenger"
, i.Rank_change as "Rank_change"
,i.Percent_change as "Percent_Change"
))
into v_airport_xml
FROM DUAL;
insert into airports_2_xml values (v_airport_xml);
end loop;
end;

Returning multiple values using function causing multiple query runs

We have kiosks for customers to check their purchase volume for two different categories of items. They will input their mobile number, which will send an OTP to their mobile numbers and they will input it back to authenticate, the system has to check the data and display for them. As a developer, the kiosk supplier has provided us with a limited functionality development kit by which we can execute select statement on the database and display the returned values on the kiosk.
I have created an object type as follows:
CREATE OR REPLACE TYPE rebate_values
AS
OBJECT (ASales_total number,
ACurrent_Rebate_Percent number,
ANeeded_Sales number,
ANext_Rebate_Percent number,
BSales_total number,
BCurrent_Rebate_Percent number,
BNeeded_Sales number,
BNext_Rebate_Percent number);
A function to which I will pass customers' mobile to get their sales and rebate information:
CREATE OR REPLACE FUNCTION AA_rebate_function (P_phone IN NUMBER)
RETURN rebate_values
IS
A_P_Sales_total NUMBER;
A_P_Current_Rebate_Percent NUMBER;
A_P_Needed_Sales NUMBER;
A_P_Next_Rebate_Percent NUMBER;
B_P_Sales_total NUMBER;
B_P_Current_Rebate_Percent NUMBER;
B_P_Needed_Sales NUMBER;
B_P_Next_Rebate_Percent NUMBER;
P_CODE VARCHAR (10);
BEGIN
SELECT CC_CODE
INTO P_CODE
FROM CUSTOMERS
WHERE C_MOBILE = P_phone;
FOR OUTDATA
IN (
--My Query to retrieve the data
Select ................
)
LOOP
IF OUTDATA.CLASS = 'X'
THEN
A_P_Sales_total := OUTDATA.SALES_TOTAL;
A_P_Current_Rebate_Percent := OUTDATA.CURRENT_REBATE_PERCENT;
A_P_Needed_Sales := OUTDATA.NEEDED_SALES_FOR_HIGHER_REBATE;
A_P_Next_Rebate_Percent := OUTDATA.NEXT_HIGHER_REBATE_PERCENT;
END IF;
IF OUTDATA.CLASS = 'Y'
THEN
B_P_Sales_total := OUTDATA.SALES_TOTAL;
B_P_Current_Rebate_Percent := OUTDATA.CURRENT_REBATE_PERCENT;
B_P_Needed_Sales := OUTDATA.NEEDED_SALES_FOR_HIGHER_REBATE;
B_P_Next_Rebate_Percent := OUTDATA.NEXT_HIGHER_REBATE_PERCENT;
END IF;
END LOOP;
RETURN rebate_values (A_P_Sales_total,
A_P_Current_Rebate_Percent,
A_P_Needed_Sales,
A_P_Next_Rebate_Percent,
B_P_Sales_total,
B_P_Current_Rebate_Percent,
B_P_Needed_Sales,
B_P_Next_Rebate_Percent);
END;
/
The query takes 27 seconds to retrieve the values for each customer. Each customer will have 2 rows, so that's why I have used LOOP to collect the values.
When I execute the function:
SELECT AA_rebate_function (XXXXXXXXXX) FROM DUAL;
I get data as follows in a single column within 27 seconds:
(XXXX, X, XXXX, X, XXXX, X, XXXX, X)
But when I execute the function to get the values in different columns, it takes 27 x 8 seconds = 216 seconds, i.e., approximately 3.6 minutes which is a big issue as the customer cannot wait for 3.6 minutes on the kiosk to view the data.
SELECT x.c.ASales_total,
x.c.ACurrent_Rebate_Percent,
x.c.ANeeded_Sales,
x.c.ANext_Rebate_Percent,
x.c.BSales_total,
x.c.BCurrent_Rebate_Percent,
x.c.BNeeded_Sales,
x.c.BNext_Rebate_Percent
FROM (SELECT AA_rebate_function (XXXXXXXXXX) c FROM DUAL) x;
I have tried using stored procedure with OUT values but it doesn't fit in my environment as I cannot program to execute stored procedures from the kiosk development toolkit because it only supports select statements, checked with the supplier and they don't have any plan to add that support in near future.
I tried converting the single field into multiple columns using REGEXP_SUBSTR but I get a type conversion error as it is an array.
The query is very complex and has to calculate data for the last 10 years and has millions of rows, 27 seconds is actually the optimum time to get the desired results.
Interesting! I didn't realize that when you query a function that returns an object, it runs the function once for each column you reference the object in. That's awkward.
The easiest solution I could find for this is to switch your function to be PIPELINED. You'll need to create a nested table type to do this.
create type rebate_values_t is table of rebate_values;
/
CREATE OR REPLACE FUNCTION AA_rebate_function (P_phone IN NUMBER)
RETURN rebate_values_t PIPELINED
IS
... your code here ...
PIPE ROW (rebate_values (A_P_Sales_total,
A_P_Current_Rebate_Percent,
A_P_Needed_Sales,
A_P_Next_Rebate_Percent,
B_P_Sales_total,
B_P_Current_Rebate_Percent,
B_P_Needed_Sales,
B_P_Next_Rebate_Percent));
RETURN;
END;
/
SELECT x.ASales_total,
x.ACurrent_Rebate_Percent,
x.ANeeded_Sales,
x.ANext_Rebate_Percent,
x.BSales_total,
x.BCurrent_Rebate_Percent,
x.BNeeded_Sales,
x.BNext_Rebate_Percent
FROM TABLE(AA_rebate_function (XXXXXXXXXX)) x;
For some reason, this should only execute the function once, and take 27 seconds.

How to decrypt PBEWithMD5AndDES String in Oracle?

In my user table, the username column is encoded via PBEWithMD5AndDES algorithm. Now I want something like this:
WITH TEMP
AS (SELECT E.UAC_USER_ID AS ID,
E.UAC_USER_USERNAME AS USERNAME,
E.UAC_USER_FIRSTNAME || ' ' || E.UAC_USER_LASTNAME AS NAME,
E.UAC_USER_PERSONELCODE AS PERSONELCODE
FROM UAC_USERS E
WHERE E.UAC_USER_ISENABLED = 1)
SELECT *
FROM TEMP
WHERE 1 = 1 AND DECODE (USERNAME) = 'admin'
is there any embedded function in oracle that can decode the selected field?
I can only give you direction to solve problem, not answer.
Oracle has package dbms_crypto which may be can help you.
See examples https://docs.oracle.com/database/121/ARPLS/d_crypto.htm#ARPLS65690
You can find encryption_type
encryption_type PLS_INTEGER := -- total encryption type
DBMS_CRYPTO.ENCRYPT_AES256
+ DBMS_CRYPTO.CHAIN_CBC -- description in doc
+ DBMS_CRYPTO.PAD_PKCS5; -- description in doc
In DBMS_CRYPTO there is another constant DBMS_CRYPTO.ENCRYPT_PBE_MD5DES.
Theoretically this may help:
encryption_type PLS_INTEGER := -- total encryption type
DBMS_CRYPTO.ENCRYPT_PBE_MD5DES
+ DBMS_CRYPTO.CHAIN_CBC -- description in doc
+ DBMS_CRYPTO.PAD_PKCS5; -- description in doc
But using this encryption_type in code I get error. I also try several combination and change code but finally i give up. May be one of the reason is the constant DBMS_CRYPTO.ENCRYPT_PBE_MD5DES doesn't described in doc or I should change smt else in example. .