How to restrict special characters < and > in input value for a column - plsql

I am not that fluent in using regular expressions. I want that user should not provide value for a column with '<' or '>' in it.
Regards,
Sachin

Use group () and or | operators for example to validate user input you sould do:
set serveroutput on;
declare
l_value varchar2(32767) := '<p>test and data</p>';
begin
if regexp_like(l_value,'(<|>)','i') then
dbms_output.put_line('invalid');
else
dbms_output.put_line('valid');
end if;
end;
/
Good luck.

Use [ and ] to define a set of characters to match, e.g. [abc], [a-z], [a-z0-9_]
select string
, case
when regexp_like(string,'[<>]') then 'Invalid'
else 'Valid'
end as test
from
( select '<p>text</p>' as string from dual union all
select 'text' from dual );
STRING TEST
---------------- -------
<p>text</p> Invalid
text Valid
Or in PL/SQL:
declare
teststring varchar2(100) := '<p>test and data</p>';
regex varchar2(100) := '[<>]';
begin
dbms_output.put('"'||teststring||'"');
dbms_output.put(case when regexp_like(teststring,regex) then ' matches ' else ' does not match ' end );
dbms_output.put(regex);
dbms_output.new_line();
end;
/
"<p>test and data</p>" matches [<>]
PL/SQL procedure successfully completed
As a check constraint:
create table mytable
( col varchar2(20)
constraint mytable_ltgt_chk check ( not regexp_like(col,'[<>]') )
);
Test:
insert into mytable (col) values ('kitten > puppy');
rejected with:
ORA-02290: check constraint (MYRIAD_OWNER_82.MYTABLE_LTGT_CHK) violated
If you wanted to exclude square brackets as well, that would be:
constraint mytable_symbol_chk check ( not regexp_like(col,'[][<>]') );
Or without any regex:
constraint mytable_symbol_chk check ( col = translate(col,'[]<>','.') )
https://regex101.com/r/oQJztM/2/tests

Related

Oracle - store large string in CLOB

I need to save a procedure body into a Clob column with a use of variable. String is longer than 4000 characters, so I can't use VarChar2, but with CLOB variable I receive error "ORA-01422: exact fetch returns more than requested number of rows". Same error appears with Varchar2. My PL/SQL block:
DECLARE
txt_procedure CLOB;
BEGIN
SELECT text INTO txt_procedure
FROM all_source
WHERE name = 'My_procedure'
ORDER BY line;
INSERT INTO TABLE1(ID,DATE,CLOB_COLUMN)
VALUES (my_seq.NEXTVAL,'11.10.2018',txt_procedure);
END;
/
How could I insert procedure body into clob column ?
As you will get multiple rows from your query for every line of your source, the following might help:
DECLARE
txt_procedure CLOB;
BEGIN
FOR source_r IN ( SELECT text
FROM all_source
WHERE name = 'My_procedure'
ORDER BY line
)
LOOP
txt_procedure := txt_procedure || chr(10) || source_r.text;
END LOOP;
INSERT INTO TABLE1(ID,DATE,CLOB_COLUMN)
VALUES (my_seq.NEXTVAL,'11.10.2018',txt_procedure);
END;
/
UPDATE
As an alternative, you might also use the DBMS_METADATA package for this:
DECLARE
txt_procedure CLOB;
BEGIN
txt_procedure := DBMS_METADATA.get_ddl(
object_type => 'PROCEDURE',
name => 'My_procedure',
owner => 'YOUR_SCHEMA'
);
INSERT INTO TABLE1(ID,DATE,CLOB_COLUMN)
VALUES (my_seq.NEXTVAL,'11.10.2018',txt_procedure);
END;
/

How to use an array in PL/SQL?

How do I make arrays in PL / SQL?
I have a string that I want to split on spaces and then loop through them all.
Declare your array like this:
"your array" apex_application_global.vc_arr2;
"your array" := APEX_UTIL.STRING_TO_TABLE("your string",' ');
FOR i IN 1.. "your array".COUNT LOOP
"Your string" := "Your string"|| "your array"(i);
END LOOP;
and there you have it
And this is the ultimate and universal solution without using any packages, just oracle SQL. See a full featured solution (including a pipelining function) over here: http://www.armbruster-it.org/index.php/12-it/pl-sql/20-string-tokenizer-with-oracle-pl-sql
declare
cursor c_tokenizer(ci_string in varchar2, ci_delimiter in varchar2) is
SELECT regexp_substr(str, '[^' || ci_delimiter || ']+', 1, LEVEL) AS splitted_element,
LEVEL AS element_no
FROM (SELECT rownum AS id, ci_string str FROM dual)
CONNECT BY instr(str, ci_delimiter, 1, LEVEL - 1) > 0
AND id = PRIOR id
AND PRIOR dbms_random.value IS NOT null;
l_string varchar2(100) := 'Hello World, I like PL/SQL';
l_delimiter varchar2(1) := ' ';
begin
-- extract each word of the string above (delimited by blank)
for c1 in c_tokenizer(l_string, l_delimiter) loop
dbms_output.put_line(c1.splitted_element);
end loop;
end;
The Result is:
Hello
World,
I
like
PL/SQL

How to use a comma-separated list of strings as pl/sql stored function parameter inside a "NOT IN" clause of a select statement

I have a list of comma-separated strings (from a user input) and I'd like to use this list as a parameter in a pl/sql stored function in a nested sql block using a "not in where clause".
I can't find an elegant way to make it work...
That's what I'm thinking of:
CREATE TABLE example ( somevalue VARCHAR(36) NOT NULL);
--
INSERT INTO example VALUES ('value1');
INSERT INTO example VALUES ('value2');
INSERT INTO example VALUES ('value3');
--
SELECT * FROM example;
--
CREATE OR REPLACE
FUNCTION resultmaker(
ignoreList IN VARCHAR2)
RETURN VARCHAR2
IS
result VARCHAR2(4000);
BEGIN
result := 'Here is my calculated result, using ignorelist=' || ignoreList || ':' || CHR(10);
FOR rec IN
(SELECT DISTINCT somevalue
FROM example
WHERE somevalue NOT IN resultmaker.ignoreList -- here's my issue, the NOT IN clause using the parameter value
)
LOOP
result := result || 'not in ignorelist: ' || rec.somevalue || CHR(10);
END LOOP;
result := result || '.' || CHR(10);
--
RETURN result;
END resultmaker;
/
--
-- simulate function call with user input 'value2, value3'
SELECT resultmaker('value2, value3') FROM dual; -- doesn't work
--
DROP TABLE example;
DROP FUNCTION resultmaker;
Just pass the parameter like '"value2","value3"' and have your statement replace the double quote with single quotes like REPLACE(#Param1,'"','''').
Call to function: SELECT * FROM Function1('"value2","value3"')
Inside function: NOT IN REPLACE(#Param1,'"','''')
In every case you should parse that input. As there is no built-in string tokenizer in PL/SQL (at least I couldn't find it) You may want to look into these options,
http://blog.tanelpoder.com/2007/06/20/my-version-of-sql-string-to-table-tokenizer/
Does PL/SQL have an equivalent StringTokenizer to Java's?
After you parsed the string, you may create a new string like:
not_in_statement varchar2(1000);
CURSOR c1 IS select token from tokenized_strings_table;
BEGIN
not_in_statement := '('
FOR rec IN c1 LOOP
not_in_statement := not_in_statement || '''||rec.token||'''||','
END LOOP
not_in_statement := not_in_statement||')'
END
SELECT DISTINCT somevalue
FROM example
WHERE somevalue NOT IN not_in_statement
You may need to make it dynamic SQL, I did not have time to try.
Here's my solution using dynamic sql for my original question above:
CREATE TABLE example ( somevalue VARCHAR(36) NOT NULL);
--
INSERT INTO example VALUES ('value1');
INSERT INTO example VALUES ('value2');
INSERT INTO example VALUES ('value3');
--
SELECT * FROM example;
--
CREATE OR REPLACE
FUNCTION resultmaker(
ignoreList IN VARCHAR2)
RETURN VARCHAR2
IS
result VARCHAR2(4000);
example_cursor sys_refcursor;
rec example.somevalue%type;
BEGIN
result := 'Here is my calculated result, using ignorelist=' || ignoreList || ':' || CHR(10);
OPEN example_cursor FOR ( 'SELECT DISTINCT somevalue FROM example WHERE somevalue NOT IN (' || ignoreList || ')' );
FETCH example_cursor INTO rec;
WHILE example_cursor%found
LOOP
result := result || 'not in ignorelist: ' || rec || CHR(10);
FETCH example_cursor INTO rec;
END LOOP;
CLOSE example_cursor;
result := result || '.' || CHR(10);
--
RETURN result;
END resultmaker;
/
--
-- simulate function call with user input 'value2', 'value3'
SELECT resultmaker('''value2'', ''value3''') FROM dual;
--
DROP TABLE example;
DROP FUNCTION resultmaker;
The classic and probably correct solution would be to use PL/SQL table passing it as prameter...
There are some good solutions at asktom.oracle.com regarding taking a string of values and dynamically creating an IN clause for them:
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:210612357425

Sanitizing user inputs that are part of dynamic PL/SQL command

I have a table filled by users and its VARCHAR2 records contain a part of executable PL/SQL code "IN(user_input)". I wonder how I can sanitize these user inputs or maybe revrite it to be more efficient. All my ideas failed so far. For example:
A Bind variable is not accepted in this case
DBMS_ASSERT.enquote_literal always raise an exception etc.
Thank you very much for any help.
/* A "MY_PARAMETER" column is part of SQL: ...WHERE MY_DATA IN(MY_PARAMETER)... */
CREATE TABLE my_parameter_table (
"ID" INTEGER NOT NULL ENABLE,
"MY_PARAMETER" VARCHAR(255) NOT NULL ENABLE
)
INSERT INTO my_parameter_table ("ID","MY_PARAMETER") VALUES (1,'6,7,8');
INSERT INTO my_parameter_table ("ID","MY_PARAMETER") VALUES (2,'''b'',''g'',''k''');
INSERT INTO my_parameter_table ("ID","MY_PARAMETER") VALUES (3,'SELECT dummy FROM dual'); -- return "X"
/* Tested table with data */
CREATE TABLE my_data_table (
"ID" INTEGER NOT NULL ENABLE,
"MY_DATA" VARCHAR(255) NOT NULL ENABLE,
"MY_RESULT" VARCHAR(255) NOT NULL ENABLE
);
INSERT INTO my_data_table ("ID","MY_DATA","MY_RESULT") VALUES (1,'a','NOT');
INSERT INTO my_data_table ("ID","MY_DATA","MY_RESULT") VALUES (2,'b','THIS'); --WILL PASS
INSERT INTO my_data_table ("ID","MY_DATA","MY_RESULT") VALUES (3,'c','NOT');
INSERT INTO my_data_table ("ID","MY_DATA","MY_RESULT") VALUES (4,'X','IS'); --WILL PASS
INSERT INTO my_data_table ("ID","MY_DATA","MY_RESULT") VALUES (5,'Y','NOT');
INSERT INTO my_data_table ("ID","MY_DATA","MY_RESULT") VALUES (6,'Z','CORRECT'); --WILL PASS
/* Result table where results are inserted */
CREATE TABLE my_result_table (
"MESSAGE" VARCHAR(255) NOT NULL ENABLE
);
/* ------------------------------------------- */
DECLARE
where_condition VARCHAR2(1000) := '';
v_query VARCHAR2(1000) := '';
insert_or VARCHAR2(5) := '';
CURSOR test_parameter_cur IS
(
SELECT * FROM my_parameter_table
);
test_parameter_rec test_parameter_cur%ROWTYPE;
BEGIN
/* Read all parameters and build an WHERE condition */
OPEN test_parameter_cur;
LOOP
FETCH test_parameter_cur INTO test_parameter_rec;
EXIT WHEN test_parameter_cur%NOTFOUND;
/* Condition check can be any type. Varchar, number, date or some subselect */
IF test_parameter_rec.ID = 1 THEN where_condition := where_condition || insert_or || 'd.ID IN('|| test_parameter_rec.MY_PARAMETER ||')';
ELSE where_condition := where_condition || insert_or || 'd.MY_DATA IN('|| test_parameter_rec.MY_PARAMETER ||')';
END IF;
insert_or := ' OR '; -- after first run the "OR" operator is inserted in front of each where condition
END LOOP;
CLOSE test_parameter_cur;
v_query := 'INSERT INTO my_result_table(MESSAGE)
(SELECT d.MY_RESULT FROM my_data_table d
WHERE '|| where_condition ||')';
EXECUTE IMMEDIATE v_query;
COMMIT;
END;
/* Now the my_result_table contains 3 records: THIS, IS, CORRECT */
SELECT * FROM my_result_table;
DROP TABLE my_parameter_table;
DROP TABLE my_data_table;
Have a look here : https://blogs.oracle.com/aramamoo/entry/how_to_split_comma_separated_string_and_pass_to_in_clause_of_select_statement
Works like a charme for ',' - separated list. I recommend to TRIM() the result to remove trailing and leading blanks from the results.
You can use the select-statement as a subselect in an in-clause.

Dynamic column name to record type variable

DECLARE
TYPE t IS RECORD (
col_name VARCHAR2 (100)
);
t_row t;
cname VARCHAR (100) := 'col_name';
BEGIN
t_row.col_name := 'col';
DBMS_OUTPUT.put_line ('out');
IF t_row.cname IS NULL THEN
DBMS_OUTPUT.put_line ('in');
END IF;
END;
Error at line 1
ORA-06550: line 12, column 12:
PLS-00302: component 'CNAME' must be declared
ORA-06550: line 12, column 3:
PL/SQL: Statement ignored
How can I assign dynamic column name to type variable of record?
You can do that with dynamic sql:
To make the example simpler I'll make your type t a schema object (but basically you don't have to - you can put it in the dynamic part as well)
create or replace type t is object(col_name varchar2(100));
/
Then you can look at this script:
declare
t_row t;
cname varchar2(100) := 'col_name';
begin
t_row := new t('col');
execute immediate 'declare t_in t := :0; begin if t_in.' || cname ||
' is null then dbms_output.put_line(''in''); end if; end;'
using t_row;
end;
Though, I must say, that this is a strange requirement ...
The error is because record t doesn't have a field cname, but col_name:
type t is record (
col_name varchar2(100)
);
One have to know record fields during compile time.
Could tell us what is the real problem you're going to solve ?

Resources