I have problem with encoding data in Oracle database.
I want to xor string with another string (mask) and then
encode it with base64.
In Python this looks like:
def encode_str(s, mask):
xor_mask = mask
while len(xor_mask) < len(s):
xor_mask += mask
chrs2 = []
for i in range(len(s)):
chrs2.append(chr(ord(s[i]) ^ ord(xor_mask[i])))
s2 = ''.join(chrs2)
return base64.b64encode(s2)
#return binascii.hexlify(s2).lower()
In PL/SQL I got:
create or replace function ht_encode(str in varchar2, mask in varchar2) return varchar2 as
xor_mask varchar2(2000);
result_s varchar2(2000);
i integer;
xx integer;
x char(10);
ch1 char(10);
ch2 char(10);
chrx varchar2(10);
begin
result_s := '';
xor_mask := mask;
while length(xor_mask) < length(str) loop
xor_mask := xor_mask || mask;
end loop;
for i in 1..length(str) loop
ch1 := substr(str, i, 1);
ch2 := substr(xor_mask, i, 1);
xx := BITXOR(ascii(ch1), ascii(ch2));
x := xx;
chrx := rawtohex(x);
--result_s := result_s || ':' || chrx;
--result_s := result_s || chrx;
-- HELP ME HERE!
end loop;
--return lower(utl_encode.base64_encode(result_s));
--return result_s || ' | ' || rawtohex(result_s);
-- HELP ME HERE!
return result_s;
end;
(bitxor comes from http://forums.oracle.com/forums/thread.jspa?threadID=496773 )
I don't know how to create "binary" string and then encode it to hex or preferable
to base64.
SELECT UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(UTL_RAW.bit_xor(UTL_RAW.cast_to_raw('text'), UTL_RAW.cast_to_raw('mask'))))
FROM dual
Related
create or replace FUNCTION "FNC_CALCULATE_MOD11" (P_VALOR IN NUMBER)
return number is
Result number;
begin
DECLARE
-- LOCAL VARIABLES HERE
V_PROCESSO VARCHAR2(30);
V_PESO NUMBER := 2;
V_SOMA NUMBER := 0;
V_RESTO NUMBER := 0;
BEGIN
V_PROCESSO := TO_CHAR(P_VALOR);
WHILE LENGTH(V_PROCESSO) < 6 --Popular com zeros no inicio até 6
LOOP
V_PROCESSO := '0'||V_PROCESSO;
END LOOP;
--accuses error on this line
FOR I IN REVERSE 1 .. LENGTH(V_PROCESSO)
LOOP
V_SOMA := TO_CHAR (V_SOMA) + TO_NUMBER(SUBSTR(V_PROCESSO,i,1))*V_PESO;
IF V_PESO = 9 THEN --repetir peso se for necessario
V_PESO := 2;
ELSE
V_PESO := V_PESO + 1;
END IF;
END LOOP;
V_RESTO := MOD(V_SOMA, 11);
Result := 11 - V_RESTO;
IF ((Result = 0) OR (Result = 1) OR (Result >= 10)) THEN
Result := 1;
END IF;
END;
return(Result);
end FNC_CALCULATE_MOD11;
Try to change V_PROCESSO to a bigger size, for example V_PROCESSO VARCHAR2(300);
I have the following code
declare
l_clob clob;
l_line varchar2(32767);
l_field varchar2(32767);
l_line_start pls_integer := 1;
l_line_end pls_integer := 1;
l_field_start pls_integer := 1;
l_field_end pls_integer := 1;
begin
select response_clob
into l_clob
from xxhr.xxhr_web_service_response
where response_id = 290;
-- Loop through lines.
loop
l_line_end := dbms_lob.instr(l_clob, chr(10), l_line_start, 1);
l_line := dbms_lob.substr(l_clob, l_line_end - l_line_start + 1, l_line_start);
-- If this is a line with fields and not web service garbage.
if substr(l_line, 1, 1) = '"' then
l_field_start := 2;
-- Loop through fields.
loop
l_field_end := instr(l_line, chr(9), l_field_start, 1);
l_field := substr(l_line, l_field_start, l_field_end - l_field_start);
dbms_output.put(l_field || ',');
l_field_start := l_field_end + 1;
exit when l_field_end = 0;
end loop;
dbms_output.put_line('');
end if;
l_line_start := l_line_end + 1;
exit when l_line_end = 0;
end loop;
end;
with which I'm trying to parse this clob test data:
LINE_TEXT
"PERSON_ID_NUMBER 30000 1223445454"
"PERSON_DOB 30000 01-01-1900"
The clob data is tab separated and has a chr(10) at the end. I'm not familiar with regexp_instr, but currently I'm only using instr to search for the tab separators; so it's missing the end of line field and producing:
PERSON_ID_NUMBER,30000,,
PERSON_DOB,30000,,
How can I change the instr into a regexp_instr to also look for the end of line character in addition to the tab and then correctly pick up the last field?
I need the function to be performant, since it is parsing large files.
You can split the line of CLOB column by converting to char, and then apply regexp_substr() as
with t as
(
select level as row_num, regexp_substr(to_char(t.line_text),'^.*$',1,level,'m') as str
from tab t
connect by level <= length (to_char(t.line_text))
- length (replace (to_char(t.line_text), chr (10))) + 1
)
select row_num, regexp_replace(str,'[[:space:]]+',',') as str
from t;
ROW_NUM STR
------- -----------------------------------------
1 PERSON_ID_NUMBER,30000,1223445454
2 PERSON_DOB,30000,01-01-1900
Demo
Edit : even works without to_char() conversion, if your CLOB is huge then you need to split step by step by substr(str,1,4000), substr(str,4001,8000) ...
with t as
(
select level as row_num, regexp_substr(substr(t.line_text,1,4000),'^.*$',1,level,'m') str
from tab t
connect by level <= length (substr(t.line_text,1,4000))
- length (replace(substr(t.line_text,1,4000), chr (10))) + 1
)
select row_num, regexp_replace(substr(str,1,4000),'[[:space:]]+',',') as str
from t
Fixed it with:
declare
l_clob clob;
l_line varchar2(32767);
l_field varchar2(32767);
l_line_start pls_integer := 1;
l_line_end pls_integer := 1;
l_field_start pls_integer := 1;
l_field_end pls_integer := 1;
begin
select response_clob
into l_clob
from xxhr.xxhr_web_service_response
where response_id = 290;
-- Loop through lines.
loop
l_line_end := dbms_lob.instr(l_clob, chr(10), l_line_start, 1);
l_line := dbms_lob.substr(l_clob, l_line_end - l_line_start + 1, l_line_start);
-- If this is a line with fields and not web service garbage.
if substr(l_line, 1, 1) = '"' then
l_field_start := 2;
-- Loop through fields.
loop
l_field_end := instr(l_line, chr(9), l_field_start, 1);
l_field := substr(l_line, l_field_start, l_field_end - l_field_start);
dbms_output.put(l_field || ',');
exit when l_field_end = 0;
l_field_start := l_field_end + 1;
end loop;
l_field := substr(l_line, l_field_start);
dbms_output.put_line(l_field);
end if;
l_line_start := l_line_end + 1;
exit when l_line_end = 0;
end loop;
end;
I have a function in Oracle SQL which scrambles a given input. I need to unscramble it.
I need a program which can basically reverse the output of the program below.
source IN VARCHAR2
sGARBLED VARCHAR2(510);
index NUMBER;
length NUMBER;
onec NUMBER;
BEGIN
length := LENGTH(source);
IF length > 255 THEN
length := 255;
END IF;
index := 1;
sGARBLED := '';
WHILE index <= length LOOP
onec := ASCII(SUBSTR(source, index, 1)) - 30;
IF (onec < 10) THEN
sGARBLED := sGARBLED || '0';
END IF;
sGARBLED := sGARBLED || CAST(onec AS VARCHAR2);
index := index + 1;
END LOOP;
RETURN sGARBLED;
END
It can be possible, if you are only allowing character values in your original string before scrambling it, to be characters of the alphabet.
Code would look something like this:
sgarbled in VARCHAR2
UNsGARBLED VARCHAR2(255);
length NUMBER;
index NUMBER;
onec NUMBER;
BEGIN
length := LENGTH(sgarbled);
index := 1;
UNsGARBLED := '';
WHILE index <= length LOOP
onec := SUBSTR(sgarbled, index, 1);
IF (onec = '0') THEN
onec := SUBSTR(sgarbled, (index + 1), 1) + 30;
ELSE
onec := SUBSTR(sgarbled, (index), 2) + 30;
END IF;
UNsGARBLED := UNsGARBLED || CAST(CHR(onec) AS VARCHAR2);
index := index + 2;
END LOOP;
RETURN UNsGARBLED;
END
As Mr. Llama pointed out if you are allowing the original input string to contain all ASCII characters then it may not be possible.
I used the function below to extract a string from a character string in input,the issue is that when i put a string character with a string quote i can't extract it
exp as input :
AA015streetl'adeuilAB00201AC0041234AD012XXXXXXXXXXXXAE009TTTTTTTTT
FUNCTION get_string (p_name IN VARCHAR2,
p_strg IN VARCHAR2,
p_len OUT NOCOPY PLS_INTEGER,
p_value OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER
IS
v_counter PLS_INTEGER := 1;
v_strg VARCHAR2 (4096) := SUBSTR (p_strg, 5);
BEGIN
p_value := NULL;
p_len := 0.;
WHILE v_counter < LENGTH (v_strg)
LOOP
IF SUBSTR (v_strg, v_counter, 3.) = p_name
THEN
p_len :=
TO_NUMBER (SUBSTR (v_strg, v_counter + 3., 3.));
p_value :=
SUBSTR (v_strg, v_counter + 6., p_len);
RETURN (declaration_cst.ok);
END IF;
v_counter :=
v_counter
+ 6.
+ TO_NUMBER (SUBSTR (v_strg, v_counter + 3., 3.));
END LOOP;
RETURN (declaration_cst.nok);
END;
END get_string;
For the example string you showed, the substring offsets and counter adjustments in your code are off. This can extract any of the 'tags':
CREATE OR REPLACE FUNCTION get_string (p_name IN VARCHAR2, p_strg IN VARCHAR2,
p_len OUT NOCOPY PLS_INTEGER, p_value OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER IS
v_counter PLS_INTEGER := 1;
BEGIN
p_value := NULL;
p_len := 0;
WHILE v_counter < LENGTH (p_strg)
LOOP
IF SUBSTR (p_strg, v_counter, 2) = p_name
THEN
p_len := TO_NUMBER (SUBSTR (p_strg, v_counter + 2, 3));
p_value := SUBSTR (p_strg, v_counter + 5, p_len);
RETURN declaration_cst.ok;
END IF;
v_counter := v_counter + 5
+ TO_NUMBER (SUBSTR (p_strg, v_counter + 2, 3));
END LOOP;
RETURN declaration_cst.nok;
END get_string;
/
Your version was losing the first four characters completely in the v_strg assignment, and was then adjusting as if the tags were three characters, not two.
With a test block like:
set serveroutput on size unlimited
declare
str varchar2(256) := q'[AA015street l'adeuilAB00201AC0041234AD012XXXXXXXXXXXXAE009TTTTTTTTT]';
len pls_integer;
value varchar2(256);
rc pls_integer;
begin
rc := get_string('AB', str, len, value);
dbms_output.put_line('AB -> ' || rc ||':'|| len ||':'|| value);
end;
/
and changing the 'AB' tags this gets:
AA -> 0:15:street l'adeuil
AB -> 0:2:01
AC -> 0:4:1234
AD -> 0:12:XXXXXXXXXXXX
AE -> 0:9:TTTTTTTTT
AF -> 1:0:
SQL Fiddle demo with wrapper function so the extracted tag info can be shown more easily.
It doesn't make any difference if the string value contains a single quote, as long as the tag length is correct - it's just another character to substr and won't be interpreted any differently.
As Bob Jarvis mentioned, there are other ways to achieve this breakdown, but that's even more beyond the scope of what you asked.
When you have a single-quote embedded in a string literal you're trying to pass to a function or procedure you'll need to double it (i.e. type two single-quotes instead of one) so that PL/SQL interprets it correctly. The doubled single-quote will be sent to the function as just one single quote, as you intend, but this is how single-quotes in literals are handled in PL/SQL. Thus, instead of calling your function as
n := get_string ('AB',
'AA015streetl'adeuilAB00201AC0041234AD012XXXXXXXXXXXXAE009TTTTTTTTT',
p_len,
p_value );
you should double the single-quote in the second parameter:
n := get_string ('AB',
'AA015streetl''adeuilAB00201AC0041234AD012XXXXXXXXXXXXAE009TTTTTTTTT',
p_len,
p_value );
If you're reading the input from a file or some other external source you don't have to do this; it's only necessary when a string literal has a single-quote in it.
Share and enjoy.
I need to write a SQL query to print the following aphanumberic sequence in SQL
0001,
0002,
... ,
0009,
000A,
... ,
000Z,
... ,
0010,
0011,
... ,
001A,
... and so on till... ,
ZZZZ
please note: all characters are UPPERCASE.
Thanks in advance
You could create a function like this:
create function to_base_36 (n integer) return varchar2
is
q integer;
r varchar2(100);
begin
q := n;
while q >= 36 loop
r := chr(mod(q,36)+case when mod(q,36) < 10 then 48 else 55 end) || r;
q := floor(q/36);
end loop;
r := chr(mod(q,36)+case when mod(q,36) < 10 then 48 else 55 end) || r;
return lpad(r,4,'0');
end;
and then use it like this:
select rownum, to_base_36(rownum)
from dual
connect by level < 36*36*36*36;
Or, without creating a function:
with digits as
( select n, chr(mod(n,36)+case when mod(n,36) < 10 then 48 else 55 end) d
from (Select rownum-1 as n from dual connect by level < 37)
)
select d1.n*36*36*36 + d2.n*36*36 + d3.n*36 + d4.n, d1.d||d2.d||d3.d||d4.d
from digits d1, digits d2, digits d3, digits d4
You could use this function:
create or replace FUNCTION SEQGEN(vinp in varchar2, iSeq in INTEGER)
RETURN VARCHAR2 is vResult VARCHAR2(32);
iBas INTEGER; iRem INTEGER; iQuo INTEGER; lLen CONSTANT INTEGER := 2;
BEGIN
iBas := length(vInp);
iQuo := iSeq;
WHILE iQuo > 0 LOOP
iRem := iQuo mod iBas;
--dbms_output.put_line('Now we divide ' || lpad(iQuo,lLen,'0') || ' by ' || lpad(iBas,lLen,'0') || ', yielding a quotient of ' || lpad( TRUNC(iQuo / iBas) ,lLen,'0') || ' and a remainder of ' || lpad(iRem,lLen,'0') || ' giving the char: ' || substr(vInp, iRem, 1));
iQuo := TRUNC(iQuo / iBas);
If iRem < 1 Then iRem := iBas; iQuo := iQuo - 1; End If;
vResult := substr(vInp, iRem, 1) || vResult;
END LOOP;
RETURN vResult;
END SEQGEN;
Try the function:
SELECT * FROM (
SELECT seqgen('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',rownum + 47989 --start value
) Output, level evt FROM dual CONNECT BY level < 1679618) --stop value
WHERE mod(evt,50000) = 0 OR output in ('0001','0002','0009','000A','000Z',
'0010','0011','001A','ZZZZ')
Note that if you change the string you must also change the start and stop value.
Read more about number systems here: Number System Conversion - Explanation
-- To get 00000 to ZZZZZ next auto alphanumeric sequence using this function [Please verify before use]
-- This starts from 0-9 then A-Z and then increase next digit from 0-9 then A-Z
-- You need to pass the starting/Last sequence as value to get next sequence
CREATE OR REPLACE FUNCTION return_next_seq (curr_sequence VARCHAR2)
RETURN VARCHAR2 IS
retval VARCHAR2(4000) := NULL;
retMaxval VARCHAR2(4000) := NULL;
eval_digit CHAR(1) := NULL;
original_sequence VARCHAR2(4000) := curr_sequence;
curr1_sequence VARCHAR2(4000) := curr_sequence;
BEGIN
retval := original_sequence;
FOR j IN REVERSE 1..LENGTH(curr1_sequence) LOOP -- Using reverse to know
-- the exact digit position
eval_digit := SUBSTR(curr1_sequence, LENGTH(curr1_sequence));
--IF (ASCII(eval_digit) BETWEEN 49 AND 56) OR
--(ASCII(eval_digit) BETWEEN 97 AND 121) THEN
IF (ASCII(eval_digit) BETWEEN 48 AND 56) OR
(ASCII(eval_digit) BETWEEN 65 AND 89) THEN
eval_digit := CHR(ASCII(eval_digit) +1);
curr1_sequence := SUBSTR(curr1_sequence,1,LENGTH(curr1_sequence)-1);
retval := curr1_sequence || eval_digit || SUBSTR(original_sequence,
LENGTH(curr1_sequence || eval_digit)+1);
EXIT;
ELSE -- move to the next digit leaving the evaluated digit untouched.
IF (ASCII(eval_digit) = 57) THEN
eval_digit := CHR(ASCII(eval_digit) +8);
curr1_sequence := SUBSTR(curr1_sequence,1,LENGTH(curr1_sequence)-1);
retval := curr1_sequence || eval_digit || SUBSTR(original_sequence,
LENGTH(curr1_sequence || eval_digit)+1);
EXIT;
END IF;
IF (ASCII(eval_digit) = 90) THEN
retMaxval := eval_digit;
eval_digit := CHR(ASCII(eval_digit) -42);
curr1_sequence := SUBSTR(curr1_sequence,1,LENGTH(curr1_sequence)-1);
FOR k IN REVERSE 1..LENGTH(curr1_sequence) LOOP
IF (ASCII(SUBSTR(curr1_sequence,LENGTH(curr1_sequence))) BETWEEN 48 AND 56) OR (ASCII(SUBSTR(curr1_sequence,LENGTH(curr1_sequence))) BETWEEN 65 AND 89) THEN
retval := SUBSTR(curr1_sequence,0,LENGTH(curr1_sequence)-1) || CHR(ASCII(SUBSTR(curr1_sequence,LENGTH(curr1_sequence)))+1) || eval_digit || SUBSTR(retval,
LENGTH(curr1_sequence || eval_digit)+1);
ELSE
IF ASCII(SUBSTR(curr1_sequence,LENGTH(curr1_sequence))) = 57 THEN
retval := SUBSTR(curr1_sequence,0,LENGTH(curr1_sequence)-1) || CHR(65) || eval_digit || SUBSTR(retval,
LENGTH(curr1_sequence || eval_digit)+1);
ELSE
IF ASCII(SUBSTR(curr1_sequence,LENGTH(curr1_sequence))) = 90 AND LENGTH(curr1_sequence)>1 THEN
retval := SUBSTR(curr1_sequence,0,LENGTH(curr1_sequence)-1) || CHR(48) || eval_digit || SUBSTR(retval,
LENGTH(curr1_sequence || eval_digit)+1);
curr1_sequence := SUBSTR(curr1_sequence,1,LENGTH(curr1_sequence)-1);
retMaxval := retMaxval||'Z';
ELSE
retMaxval := retMaxval||'Z';
EXIT;
END IF;
END IF;
END IF;
END LOOP;
EXIT;
ELSE
curr1_sequence := SUBSTR(curr1_sequence,1,LENGTH(curr1_sequence)-1);
END IF;
END IF;
END LOOP;
IF retval IS NULL OR (LENGTH(retval) = LENGTH(retMaxval)) THEN
RETURN 'MAX';
END IF;
RETURN retval;
END;
-- To verify, call this function like
SELECT return_next_seq('ZY9Z') FROM dual;
-- Any improvement suggestion is welcome!
--- Thanks!..Sanjiv