Oracle plsql rtf varchar2 field to plain text format - oracle

I need to convert a rich formatted text of a VARCHAR2 field to plain text.
For example:
{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fnil Tahoma;}{\f1\fnil\fcharset0 Tahoma;}}
{\colortbl ;\red0\green0\blue255;}
\viewkind4\uc1\pard\cf1\lang1031\b\f0\fs16 NUMBER_A\cf0\b0\f1 *\cf1\b\protect NUMBER_B\cf0\b0\protect0\f0\par
}
should be converted to:
NUMBER_A * NUMBER_B
I have tried to parse the RTF-string char by char but this isn't a very smart solution.
A PL/SQL utilities method for any RTF-text would be the nicest way.
Is there a native solution? Any ideas how to convert the rtf-text?
Thx for sharing your time and ideas.

Apparently this can be done via Oracle Text - see this AskTom question

I made it with simple PL/SQL.
Based on this SQL source, but completly rewritten.
(Backslash + Apostrofe confuses code coloring here)
CREATE OR REPLACE FUNCTION Rtf2Txt
(
pRtf varchar2
)
return nvarchar2 is
/*
Converts RTF text to TXT format by removing headers, commands, and formatting
*/
vPos1 int;
vPos2 int;
vPos3 int;
vPos4 int;
vTmp int;
vText varchar2(4000);
begin
vText := pRtf;
if vText is null then
return vText;
end if;
-- Remove outer { and } pair
vPos1 := instr(vText, '{', +1); -- The first {
vPos2 := instr(vText, '}', -1); -- The last }
if vPos1 > 0 and vPos2 > 0 then
vText := substr(vText, vPos1 +1, vPos2 - vPos1 -1);
end if;
-- Remove inner { and } pairs
while 1 = 1 loop
vPos2 := instr(vText, '}', +1); -- The first }
vPos1 := instr(vText, '{', (length(vText) - vPos2) *-1 -1); -- The last { before the found }
if vPos1 > 0 and vPos2 > 0 and vPos1 < vPos2 then
vText := substr(vText, 1, vPos1 -1) || substr(vText, vPos2 +1, length(vText) - vPos2);
else
exit;
end if;
end loop;
-- Cleaning up
vText := replace(vText, '\pard', '');
vText := replace(vText, chr(13), '');
vText := replace(vText, chr(10), '');
vText := replace(vText, '\par', chr(13));
while length(vText) > 0 and substr(vText, 1, 1) IN (' ', CHR(13), CHR(10)) loop
vText := substr(vText, 2, length(vText) -1);
end loop;
while length(vText) > 0 and substr(vText, length(vText), 1) IN (' ', CHR(13), CHR(10)) loop
vText := substr(vText, 1, length(vText) -1);
end loop;
-- Remove \ commands and replace \'XX charactercoding
vPos2 := 1;
while 1 = 1 loop
vPos1 := instr(vText, '\', vPos2);
if vPos1 = 0 then
exit;
end if;
if substr(vText, vPos1 +1, 1) = '\' then -- Skip \\ escape sequence, when present
vPos2 := vPos1 +2;
continue;
end if;
if substr(vText, vPos1 +1, 1) = '''' then -- Decode \' hex sequence
vTmp := to_number(substr(vText, vPos1 +2, 2), 'xx');
vText := substr(vText, 1, vPos1 -1) ||chr(vTmp)|| substr(vText, vPos1 +4, length(vText) - vPos1 -3);
vPos2 := vPos1 +1;
continue;
end if;
-- Skip \anything sequence
vPos2 := instr(vText, '\', vPos1 +1); -- The next \
vPos3 := instr(vText, ' ', vPos1 +1); -- The next ' '
vPos4 := instr(vText, chr(13), vPos1 +1); -- The next Enter
if vPos4 > 0 and vPos4 < vPos3 then
vPos3 := vPos4;
end if;
if vPos2 = 0 and vPos3 = 0 then
vPos3 := length(vText);
end if;
if vPos2 > 0 and (vPos2 < vPos3 or vPos3 = 0) then
vText := substr(vText, 1, vPos1 -1) || substr(vText, vPos2, length(vText) - vPos2 +1);
vPos2 := vPos1;
end if;
if vPos3 > 0 and (vPos3 < vPos2 or vPos2 = 0) then
vText := substr(vText, 1, vPos1 -1) || substr(vText, vPos3 +1, length(vText) - vPos3);
vPos2 := vPos1;
end if;
end loop;
return vText;
end;
/

I dont'have the repuation to comment CLS's posting, but I would like to
advise that the line
if vPos1 = 0 then
should be changed to
if nvl(vPos1,0)=0 then
otherwise with not well formed RTFs the function will be stuck in the while loop forever
Bye
Andreas

Related

Clearing a single space from the console

I don't know how to word the title correctly but I was wondering if there is any way for me to be able to clear a single space from the console instead of clearing the entire thing just to re-write it again? for example, say I was to draw out a 3 by 3 square with the numbers counting from 1-9 all the way down, is there a way for me to change the final 9 without clearing all the prior numbers. when I clear the entire console it creates a flicker effect which is very annoying. if you want to see an example of this in code here is the thing I plan to use it on:
program randomPath;
uses crt, sysutils;
type StrMultiArray = array of array of String;
var
finishedBoard : StrMultiArray;
size, i, j : integer;
regenerate : boolean;
function generateMap(size : integer) : StrMultiArray;
var
posx, posy, choice, counter : integer;
ongoing : boolean;
board : StrMultiArray;
begin
setLength(board, size, size);
for i := 0 to (size-1) do
begin
for j := 0 to (size-1) do
begin
board[i,j] := '#';
end;
end;
posx := (size div 2);
posy := (size div 2);
board[posx,posy] := ' ';
ongoing := true;
counter := 0;
while ongoing = true do
begin
choice := random(2);
if (choice = 0) then
begin
choice := random(2);
if (choice = 0) then
begin
if (posx > 0) then
begin
posx := posx - 1;
end;
end
else
begin
if (posx < size-1) then
begin
posx := posx + 1;
end;
end;
end
else
begin
choice := random(2);
if (choice = 0) then
begin
if (posy > 0) then
begin
posy := posy - 1;
end;
end
else
begin
if (posy < size-1) then
begin
posy := posy + 1;
end;
end;
end;
counter := counter + 1;
board[posx,posy] := ' ';
if counter = (size * 12) then
begin
ongoing := false;
end;
end;
generateMap := board;
end;
procedure printBoard(board : StrMultiArray; size : integer);
begin
textColor(Cyan);
write('+');
for i := 0 to (size-1) do
begin
write('-');
end;
writeLn('+');
for i := 0 to (size-1) do
begin
textColor(Cyan);
write('|');
textColor(White);
for j := 0 to (size-1) do
begin
if (board[i,j] = '#') then
begin
textBackground(White);
end;
if (board[i,j] = '#') then
begin
textBackground(Red);
end;
//write(board[i,j]);
write(' ');
textBackground(Black);
end;
textColor(Cyan);
writeLn('|');
end;
textColor(Cyan);
write('+');
for i := 0 to (size-1) do
begin
write('-');
end;
writeLn('+');
textColor(White);
end;
procedure movePlayer(board : StrMultiArray; size : integer);
var
ongoing : boolean;
posx, posy, prevx, prevy : integer;
input : char;
begin
ongoing := true;
posx := (size div 2);
posy := (size div 2);
while (ongoing = true) do
begin
board[posx,posy] := '#';
prevx := posx;
prevy := posy;
printBoard(board, size);
input := readKey();
clrScr();
case input of
'w' :
if (posx > 0) then
begin
if (board[posx-1,posy] = ' ') then
posx := posx - 1;
end;
'a' :
if (posy > 0) then
begin
if (board[posx,posy-1] = ' ') then
posy := posy - 1;
end;
's' :
if (posx < (size-1)) then
begin
if (board[posx+1,posy] = ' ') then
posx := posx + 1;
end;
'd' :
if (posy < (size-1)) then
begin
if (board[posx,posy+1] = ' ') then
posy := posy + 1;
end;
'x' :
begin
regenerate := false;
ongoing := false;
end;
else
ongoing := false;
end;
board[prevx,prevy] := ' ';
end;
end;
begin
size := 10;
regenerate := true;
randomize;
while (regenerate = true) do
begin
finishedBoard := generateMap(size);
movePlayer(finishedBoard, size);
end;
end.
I found a useful function in Pascal that allow you to select a position in the console to move the "cursor" to from which point you can write in that location. the function is called GoToXY(). documentation can be found here: https://www.freepascal.org/docs-html/rtl/crt/gotoxy.html.

Is it possible to write function and procedure within single program in PL/SQL without using packages?

I'm trying to calculate LCM using GCF but somehow i'm getting error saying "no function with name LCM exists in the scope". What can i do about this?. I think this error is because i'm writing procedure and function together..
create or replace FUNCTION gcf (
x IN INTEGER,
y IN INTEGER
) RETURN INTEGER IS
res INTEGER;
BEGIN
IF ( y = 0 ) OR MOD(y,x) = 0 THEN
RETURN x;
ELSIF ( x = 0 ) THEN
RETURN y;
ELSIF ( x < y ) THEN
res := gcf(y,x);
ELSE
res := gcf(y,MOD(x,y) );
END IF;
RETURN res;
END;
/
create or replace PROCEDURE lcm (
num1 IN INTEGER,
num2 IN INTEGER,
answer OUT INTEGER
) IS
BEGIN
IF num1 = 0 AND num2 = 0 THEN
answer := 0;
ELSE
answer := abs(num1 * num2) / gcf(num1,num2);
END IF;
END lcm;
/
DECLARE
c integer;
BEGIN
dbms_output.put_line(' LCM(8, 12)-> ' || lcm(8, 12,c) );
dbms_output.put_line(' LCM(38,150)-> ' || lcm(38,150,c) );
dbms_output.put_line(' LCM(16,50)-> ' || lcm(16,60,c) );
dbms_output.put_line(' LCM(16,60)-> ' || lcm(16,60,c) );
dbms_output.put_line(' LCM(48,99)-> ' || lcm(48,99,c) );
END;
/
You can't use a PL/SQL procedure like a function. You must simply run it without being part of any other expression, which will set the value of the out parameter c.
DECLARE
c integer;
BEGIN
lcm(8, 12,c);
dbms_output.put_line(' LCM(8, 12)-> ' || c);
lcm(38,150,c);
dbms_output.put_line(' LCM(38,150)->' || c);
lcm(16,60,c);
dbms_output.put_line(' LCM(16,50)-> ' || c);
lcm(16,60,c);
dbms_output.put_line(' LCM(16,60)-> ' || c);
lcm(48,99,c);
dbms_output.put_line(' LCM(48,99)-> ' || c );
END;
/
Create the procedure and function inside the procedure as follows:
create or replace PROCEDURE lcm (
num1 IN INTEGER,
num2 IN INTEGER,
answer OUT INTEGER
) IS
FUNCTION gcf (
x IN INTEGER,
y IN INTEGER
) RETURN INTEGER IS
res INTEGER;
BEGIN
IF ( y = 0 ) OR MOD(y,x) = 0 THEN
RETURN x;
ELSIF ( x = 0 ) THEN
RETURN y;
ELSIF ( x < y ) THEN
res := gcf(y,x);
ELSE
res := gcf(y,MOD(x,y) );
END IF;
RETURN res;
END;
BEGIN
IF num1 = 0 AND num2 = 0 THEN
answer := 0;
ELSE
answer := abs(num1 * num2) / gcf(num1,num2);
END IF;
END lcm;
/
Then call it:
DECLARE
c INTEGER;
BEGIN
lcm (8, 12, c);
DBMS_OUTPUT.put_line (' LCM(8, 12)-> ' || c);
lcm (38, 150, c);
DBMS_OUTPUT.put_line (' LCM(38,150)-> ' || c);
lcm (16, 60, c);
DBMS_OUTPUT.put_line (' LCM(16,50)-> ' || c);
lcm (16, 60, c);
DBMS_OUTPUT.put_line (' LCM(16,60)-> ' || c);
lcm (48, 99, c);
DBMS_OUTPUT.put_line (' LCM(48,99)-> ' || c);
END;
The Lowest Common Multiple should be a function (since it returns a single value):
CREATE FUNCTION lcm (
num1 IN INTEGER,
num2 IN INTEGER,
) RETURN INTEGER DETERMINISTIC
IS
BEGIN
IF num1 = 0 AND num2 = 0 THEN
RETURN 0;
END IF;
RETURN abs(num1 * num2) / gcf(num1,num2);
END lcm;
/
Then you can use the code:
BEGIN
dbms_output.put_line(' LCM(8, 12)-> ' || lcm(8, 12) );
dbms_output.put_line(' LCM(38,150)-> ' || lcm(38,150) );
dbms_output.put_line(' LCM(16,50)-> ' || lcm(16,60) );
dbms_output.put_line(' LCM(16,60)-> ' || lcm(16,60) );
dbms_output.put_line(' LCM(48,99)-> ' || lcm(48,99) );
END;
/

How write a PL/SQL program that prints out string which looking like xml format

Input String : “a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k3g5g5k3w2”
I tried this code as first step:
declare
word varchar2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num number := length(word)/2;
name_array dbms_sql.varchar2_table;
begin
dbms_output.put_line(word);
FOR i IN 1..num LOOP
name_array(i) := substr(word, -2*i, 2);
END LOOP;
FOR i IN name_array.FIRST .. name_array.LAST LOOP
dbms_output.put_line(name_array(i));
END LOOP;
end;
This code creates only an array of string. Not xml format. I need this output:
Which SQL functions,conditional clauses... do I need to use?
Oracle Setup:
CREATE OR REPLACE TYPE CHARS_TABLE IS TABLE OF CHAR(2);
/
CREATE OR REPLACE TYPE INTEGERS_TABLE IS TABLE OF INTEGER;
/
PL/SQL:
This assumes a well-formed set of character pairs and just indents each pair to the appropriate level:
DECLARE
word VARCHAR2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num PLS_INTEGER := LENGTH( word ) / 2;
name_array CHARS_TABLE := CHARS_TABLE();
depth_array INTEGERS_TABLE := INTEGERS_TABLE();
open_array INTEGERS_TABLE := INTEGERS_TABLE();
BEGIN
name_array.EXTEND( num );
depth_array.EXTEND( num );
open_array.EXTEND( num );
name_array(1) := SUBSTR( word, 1, 2 );
depth_array(1) := 1;
open_array(1) := 1;
FOR i IN 2 .. num LOOP
name_array(i) := SUBSTR( word, 2*i - 1, 2 );
open_array(i) := 1;
FOR j IN 1 .. i-1 LOOP
IF name_array(j) = name_array(i) THEN
open_array(i) := -open_array(i);
END IF;
END LOOP;
depth_array(i) := depth_array(i-1) + open_array(i);
END LOOP;
FOR i IN 1 .. num LOOP
FOR j IN 2 .. depth_array(i) + CASE open_array(i) WHEN 1 THEN 0 ELSE 1 END LOOP
DBMS_OUTPUT.PUT( ' ' );
END LOOP;
DBMS_OUTPUT.PUT_LINE( name_array(i) );
END LOOP;
END;
/
Output:
a4
b4
c2
d9
d9
c2
e6
e6
b4
s2
o1
o1
s2
a4
w2
r8
r8
k2
g5
g5
k2
w2
Update - Simpler Stack-Based Version:
DECLARE
word CONSTANT VARCHAR2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num CONSTANT PLS_INTEGER := LENGTH( word ) / 2;
name_array CHARS_TABLE := CHARS_TABLE();
depth PLS_INTEGER := 0;
name CHAR(2);
PROCEDURE indent( depth PLS_INTEGER, name CHAR )
IS
BEGIN
FOR j IN 2 .. depth LOOP
DBMS_OUTPUT.PUT( ' ' );
END LOOP;
DBMS_OUTPUT.PUT_LINE( name );
END;
BEGIN
name_array.EXTEND( num );
FOR i IN 1 .. num LOOP
name := SUBSTR( word, 2*i - 1, 2 );
IF depth > 0 AND name = name_array(depth) THEN
indent(depth,name);
depth := depth - 1;
ELSE
depth := depth - 1;
name_array(depth) := name;
indent(depth,name);
END IF;
END LOOP;
END;
/
DECLARE
vs_CurrentChar VARCHAR2(1);
vs_NextChar VARCHAR2(1);
vs_TempText VARCHAR2(100);
vs_InputText VARCHAR2(100) := 'abcdffdcba';
vn_LengthOfText NUMBER := 1;
vn_WhileIndex NUMBER := 1;
vs_Spaces VARCHAR(100);
BEGIN
vs_TempText := NULL;
vs_CurrentChar := substr(vs_InputText, vn_WhileIndex, vn_LengthOfText);
dbms_output.put_line(vs_CurrentChar);
WHILE vn_WhileIndex < length(vs_InputText) - 1 LOOP
vs_NextChar := substr(vs_InputText, vn_WhileIndex + 1, vn_LengthOfText);
EXIT WHEN vs_CurrentChar = vs_NextChar;
vs_TempText := vs_TempText || vs_CurrentChar;
vs_CurrentChar := vs_NextChar;
vs_Spaces := NULL;
FOR i IN 1 .. vn_WhileIndex LOOP
vs_Spaces := vs_Spaces || chr(9); --'*';
END LOOP;
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
vn_WhileIndex := vn_WhileIndex + 1;
END LOOP;
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
FOR i IN 1 .. length(vs_TempText) LOOP
vs_Spaces := substr(vs_Spaces, vn_LengthOfText, length(vs_Spaces) - 1);
vs_CurrentChar := substr(vs_TempText, -i, vn_LengthOfText);
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
END LOOP;
END;
/
And output:
a
b
c
d
f
f
d
c
b
a
even, if you put '*'; instead of chr(9); then output will look like as:
a
*b
**c
***d
****f
****f
***d
**c
*b
a

Oracle PL/SQL split csv string into n parts

I need to split a csv string into n parts. I have addresses in various formats, so I converted them to a csv string. My report requires 3 or 4 address line fields, so I want to split my csv string into the number of parts the report requires.
For example:
10,Dingle Apartments,MANZINI,,MANZINI,NGWANE STREET,,,Swaziland
should be split into equal parts for 3 fields:
1:10,Dingle Apartments,MANZINI
2:,MANZINI,NGWANE STREET
3:,,,Swaziland
I wrote the following code, but it does not work well at all:
-- ============================================================================
-- This function splits a csv string into x parts and returns one of the parts.
-- Note: The smallest number of items in a part is hard coded to 2. Needs debug.
--
-- p_string - The csv string.
-- p_parts - The number of parts to split into.
-- p_part - The splitted part to return.
-- p_separator - The separator used in the csv string.
-- p_separator_out - The separator to return.
-- p_trim - Trim trailing separator. Y/N
-- ============================================================================
function get_csv_part
(
p_string in varchar2,
p_parts in number,
p_part in number,
p_separator in varchar2,
p_separator_out in varchar2,
p_trim in varchar2 default 'Y'
) return varchar2 is
l_answer varchar2(32767) := '';
l_count number := 0;
l_count2 number := 0;
l_size number;
l_pos number;
l_pos2 number;
begin
-- hr_utility.trace_on(null, 'FDL');
hr_utility.trace('p_string: ' || p_string);
l_pos := instr(p_string, p_separator);
-- Determine the number of separators.
while l_pos > 0 loop
l_count := l_count + 1;
l_pos := instr(p_string, p_separator, l_pos + 1);
end loop;
-- Get the size of a part.
if l_count <= p_parts then
l_size := 2;
else
l_size := floor(l_count / p_parts);
end if;
--if l_size = 1 then
-- l_size := 2;
--end if;
hr_utility.trace('l_size: ' || to_char(l_size));
l_pos := instr(p_string, p_separator);
if l_pos = 0 then
if p_part = 1 then
l_answer := p_string;
end if;
else
if p_part = 1 then
l_answer := substr(p_string, 1, l_pos - 1) || p_separator_out;
end if;
end if;
-- Split csv into parts.
while l_pos > 0 loop
l_count2 := l_count2 + 1;
l_pos2 := instr(p_string, p_separator, l_pos + 1);
hr_utility.trace('----------------------------------------');
hr_utility.trace('l_count: ' || to_char(l_count));
hr_utility.trace('l_count2: ' || to_char(l_count2));
hr_utility.trace('floor(l_count2 / l_size) + 1: ' || to_char(floor(l_count2 / l_size) + 1));
hr_utility.trace('l_pos: ' || to_char(l_pos));
hr_utility.trace('l_pos2: ' || to_char(l_pos2));
hr_utility.trace('l_answer: ' || l_answer);
-- If we are at a position that should go into the returned part.
if
(
l_size > 1
and floor(l_count2 / l_size) + 1 = p_part
)
or
(
l_size = 1
and l_count2 = p_part
)
or
(
p_part = p_parts
and l_size = 1
and l_count2 >= p_part
)
or
(
p_part = p_parts
and floor(l_count2 / l_size) + 1 >= p_part
) then
if l_pos2 = 0 then
if l_pos + 1 < length(p_string) then
l_answer := l_answer || substr(p_string, l_pos + 1) || p_separator_out;
end if;
elsif ((l_pos + 1) <= (l_pos2 - 1)) then
l_answer := l_answer || substr(p_string, l_pos + 1, ((l_pos2 - 1) - (l_pos + 1) + 1)) || p_separator_out;
else
l_answer := l_answer || p_separator_out;
end if;
end if;
l_pos := l_pos2;
end loop;
if p_part = p_parts then
l_pos := instr(p_string, p_separator, 1, l_count);
-- Dodge.
if instr(p_string, substr(p_string, l_pos + 1)) = 0 then
l_answer := l_answer || substr(p_string, l_pos + 1);
end if;
end if;
if p_trim = 'Y' then
-- Did not work if all separators.
-- l_answer := trim(trailing p_separator_out from l_answer);
if substr(l_answer, length(l_answer)) = p_separator_out then
l_answer := substr(l_answer, 1, length(l_answer) - 1);
end if;
end if;
return l_answer;
end get_csv_part;
Some of the issues are:
It does not work for part size of 1
It puts address parts into multiple parts, for example ,,TEST,SWAZILIND,LOCATION,THING,,,,
It skips address parts
It uses unequal part sizes.
Note that for some reason the requirement is to leave the blank fields with separators so a part could be ,
Can anyone help me fix this or does anyone have a function that can do this?
Untested but something like:
CREATE OR REPLACE function get_csv_part(
p_string in varchar2,
p_parts in number,
p_part in number,
p_separator in varchar2,
p_separator_out in varchar2,
p_trim in varchar2 default 'Y'
)
RETURN VARCHAR2 DETERMINISTIC
IS
p_value VARCHAR2(4000) := p_string;
p_start INTEGER;
p_end INTEGER;
p_items INTEGER;
p_sep_len CONSTANT INTEGER := LENGTH( p_separator );
BEGIN
IF p_value IS NULL THEN
RETURN NULL;
END IF;
p_items = ( LENGTH( p_value ) - LENGTH( REPLACE( p_value, p_separator ) ) ) / p_sep_len + 1;
IF p_part = 1 THEN
p_start := 1;
ELSE
p_start := INSTR( p_value, p_separator, 1, ROUND( ( p_part - 1 ) / p_parts * p_items ) ) + p_sep_len;
END IF;
p_end := INSTR( p_value, p_separator, 1, ROUND( p_part / p_parts * p_items ) ) - 1;
IF p_end = -1 THEN
p_value := SUBSTR( p_value, p_start );
ELSE
p_value := SUBSTR( p_value, p_start, p_end - p_start + 1 );
END IF;
IF p_trim = 'Y' THEN
WHILE SUBSTR( p_value, -p_sep_len ) = p_separator THEN
p_value := SUBSTR( p_value, 1, LENGTH( p_value ) - p_sep_len );
END LOOP;
END IF;
RETURN REPLACE( p_value, p_separator, p_separator_out );
END;
/

PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: begin function pragma procedure

i know the same answer is asked before, but i'm just staring blind on my code.
what's wrong with my function???
other posts say it's missing a ; but i just can't find it.
FUNCTION checkIBAN
( p_IBAN in varchar2 )
RETURN varchar2
is
v_landcode varchar2(2);
v_lengte number(2);
v_omgezettelandcode varchar2;
v_teller number(2) DEFAULT 1;
n number(9);
d varchar2;
BEGIN
v_landcode := SUBSTRING(p_IBAN, 1, 2);
select lengte
into v_lengte
from IBAN
where code = v_landcode;
if p_IBAN.LENGTH != v_lengte
then return 'F';
end if;
v_omgezettelandcode := SUBSTRING(p_IBAN, 5) || SUBSTRING(p_IBAN, 1, 4);
WHILE v_teller < v_omgezettelandcode.LENGTH LOOP
select getal
into SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte)
from abc
where SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte) = letter;
v_teller := v_teller + 1;
END LOOP;
d := v_omgezettelandcode;
n := SUBSTRING(d, 1, 9);
d := SUBSTRING(d, 10);
n := n/97;
WHILE d.LENGTH > 7 LOOP
n := n || SUBSTRING(d, 1, 7);
d := SUBSTRING(d, 8);
n := n/97;
END LOOP;
n := n || d;
if n/97 = 1
then return 'T';
else return 'F';
end if;
END checkIBAN;
SUBSTRING is not a function in Oracle - you're looking for SUBSTR.
A variable such as d cannot be declared as VARCHAR2 - it must be given a length. Note that this is different from a parameter, such as p_IBAN, or a return value declaration - in both cases a length is not required (or even allowed).
#wweicker correctly points out that you cannot SELECT into a SUBSTR, and must instead use a variable.
When these errors are corrected I think your function should look something like:
CREATE OR REPLACE FUNCTION checkIBAN
(p_IBAN in varchar2)
RETURN varchar2
is
v_landcode varchar2(2);
v_lengte number(2);
v_omgezettelandcode varchar2(32767); -- max possible size for a VARCHAR2 var
v_teller number(2) DEFAULT 1;
n number(9);
d varchar2(32767);
s VARCHAR2(32767);
BEGIN
v_landcode := SUBSTR(p_IBAN, 1, 2);
select lengte
into v_lengte
from IBAN
where code = v_landcode;
if p_IBAN.LENGTH != v_lengte then
return 'F';
end if;
v_omgezettelandcode := SUBSTR(p_IBAN, 5) || SUBSTR(p_IBAN, 1, 4);
WHILE v_teller < v_omgezettelandcode.LENGTH LOOP
select getal
into s
from abc
where SUBSTR(v_omgezettelandcode, v_lengte, v_lengte) = letter;
v_omgezettelandcode := SUBSTR(vomgezettelandcode, 1, v_lengte-1) ||
letter ||
SUBSTR(vomgezettelandcode, v_lengte+LENGTH(letter));
v_teller := v_teller + 1;
END LOOP;
d := v_omgezettelandcode;
n := SUBSTR(d, 1, 9);
d := SUBSTR(d, 10);
n := n/97;
WHILE d.LENGTH > 7 LOOP
n := n || SUBSTR(d, 1, 7);
d := SUBSTR(d, 8);
n := n/97;
END LOOP;
n := n || d;
if n/97 = 1
then return 'T';
else return 'F';
end if;
END checkIBAN;
Best of luck.
Share and enjoy.
You need to use CREATE OR REPLACE FUNCTION instead of just FUNCTION
ex.
CREATE OR REPLACE FUNCTION checkIBAN
( p_IBAN in varchar2 )
RETURN varchar2
is
v_landcode varchar2(2);
v_lengte number(2);
v_omgezettelandcode varchar2;
v_teller number(2) DEFAULT 1;
n number(9);
d varchar2;
BEGIN
v_landcode := SUBSTRING(p_IBAN, 1, 2);
select lengte
into v_lengte
from IBAN
where code = v_landcode;
if p_IBAN.LENGTH != v_lengte
then return 'F';
end if;
v_omgezettelandcode := SUBSTRING(p_IBAN, 5) || SUBSTRING(p_IBAN, 1, 4);
WHILE v_teller < v_omgezettelandcode.LENGTH LOOP
select getal
into SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte)
from abc
where SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte) = letter;
v_teller := v_teller + 1;
END LOOP;
d := v_omgezettelandcode;
n := SUBSTRING(d, 1, 9);
d := SUBSTRING(d, 10);
n := n/97;
WHILE d.LENGTH > 7 LOOP
n := n || SUBSTRING(d, 1, 7);
d := SUBSTRING(d, 8);
n := n/97;
END LOOP;
n := n || d;
if n/97 = 1
then return 'T';
else return 'F';
end if;
END checkIBAN;
There is another error as well. Where you have:
select getal
into SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte)
from abc
where SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte) = letter;
You use INTO you must specify a variable. You can't specify the built in function 'SUBSTRING' to "select into"
ex.
select getal
into SOME_LOCAL_VARIABLE_NAME
from abc
where SUBSTRING(v_omgezettelandcode, v_lengte, v_lengte) = letter;
thank you all, eventually i turned it over al little bit, now it works.
CREATE OR REPLACE FUNCTION checkIBAN
(p_IBAN in varchar2)
RETURN varchar2
is
v_landcode varchar2(2);
v_lengte number(2);
v_omgezettelandcode varchar2(32767); -- max possible size for a VARCHAR2 var
v_teller number(2) DEFAULT 1;
n number(9);
d varchar2(32767);
s VARCHAR2(32767);
v_omgezet varchar2(32767);
v_number varchar2(32767);
BEGIN
v_landcode := SUBSTR(p_IBAN, 1, 2);
select lengte
into v_lengte
from IBAN
where code = v_landcode;
if LENGTH(p_IBAN) != v_lengte then
return 'F';
end if;
v_omgezettelandcode := SUBSTR(p_IBAN, 5) || SUBSTR(p_IBAN, 1, 4);
while v_teller < LENGTH(v_omgezettelandcode) LOOP
if SUBSTR(v_omgezettelandcode, v_teller, v_teller) in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
then v_omgezet := v_omgezet || SUBSTR(v_omgezettelandcode, v_teller, v_teller);
else
select getal
into v_number
from abc
where letter = SUBSTR(v_omgezettelandcode, v_teller, v_teller);
v_omgezet := v_omgezet || v_number;
end if;
end loop;
d := v_omgezet;
n := SUBSTR(d, 1, 9);
d := SUBSTR(d, 10);
n := n/97;
WHILE LENGTH(d) > 7 LOOP
n := n || SUBSTR(d, 1, 7);
d := SUBSTR(d, 8);
n := n/97;
END LOOP;
n := n || d;
if n/97 = 1
then return 'T';
else return 'F';
end if;
END checkIBAN;
Another potential solution for those using DbVisualizer, (and maybe other tools?). This is what solved this problem for me.
Add these two lines to your code, like so:
--/
(all your code)
/

Resources